| // 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/ng_constraint_space.h" |
| |
| #include <algorithm> |
| #include <memory> |
| |
| #include "third_party/blink/renderer/core/layout/layout_block.h" |
| #include "third_party/blink/renderer/core/layout/layout_flexible_box.h" |
| #include "third_party/blink/renderer/core/layout/layout_view.h" |
| #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h" |
| #include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h" |
| |
| namespace blink { |
| |
| namespace { |
| |
| struct SameSizeAsNGConstraintSpace { |
| NGLogicalSize logical_sizes[3]; |
| NGPhysicalSize physical_sizes[1]; |
| NGMarginStrut margin_strut; |
| NGBfcOffset bfc_offset; |
| NGExclusionSpace exclusion_space; |
| base::Optional<LayoutUnit> optional_layout_unit; |
| LayoutUnit layout_units[3]; |
| unsigned flags[1]; |
| }; |
| |
| static_assert(sizeof(NGConstraintSpace) == sizeof(SameSizeAsNGConstraintSpace), |
| "NGConstraintSpace should stay small."); |
| |
| } // namespace |
| |
| NGConstraintSpace::NGConstraintSpace(WritingMode out_writing_mode, |
| bool is_new_fc, |
| NGConstraintSpaceBuilder& builder) |
| : available_size_(builder.available_size_), |
| percentage_resolution_size_(builder.percentage_resolution_size_), |
| replaced_percentage_resolution_size_( |
| builder.replaced_percentage_resolution_size_), |
| initial_containing_block_size_(builder.initial_containing_block_size_), |
| margin_strut_(is_new_fc ? NGMarginStrut() : builder.margin_strut_), |
| bfc_offset_(is_new_fc ? NGBfcOffset() : builder.bfc_offset_), |
| floats_bfc_block_offset_(is_new_fc ? base::nullopt |
| : builder.floats_bfc_block_offset_), |
| fragmentainer_block_size_(builder.fragmentainer_block_size_), |
| fragmentainer_space_at_bfc_start_( |
| builder.fragmentainer_space_at_bfc_start_), |
| clearance_offset_(is_new_fc ? LayoutUnit::Min() |
| : builder.clearance_offset_), |
| block_direction_fragmentation_type_(builder.fragmentation_type_), |
| table_cell_child_layout_phase_(builder.table_cell_child_layout_phase_), |
| adjoining_floats_(builder.adjoining_floats_), |
| writing_mode_(static_cast<unsigned>(out_writing_mode)), |
| direction_(static_cast<unsigned>(builder.text_direction_)), |
| flags_(builder.flags_), |
| baseline_requests_(builder.baseline_requests_.Serialize()) { |
| bool is_in_parallel_flow = |
| IsParallelWritingMode(builder.parent_writing_mode_, out_writing_mode); |
| |
| DCHECK(!is_new_fc || !adjoining_floats_); |
| |
| auto SetResolvedFlag = [this](unsigned mask, bool value) { |
| flags_ = (flags_ & ~static_cast<unsigned>(mask)) | |
| (-(int32_t)value & static_cast<unsigned>(mask)); |
| }; |
| if (!is_in_parallel_flow) { |
| available_size_.Flip(); |
| percentage_resolution_size_.Flip(); |
| replaced_percentage_resolution_size_.Flip(); |
| // Swap the fixed size block/inline flags |
| bool fixed_size_block = flags_ & kFixedSizeBlock; |
| bool fixed_size_inline = flags_ & kFixedSizeInline; |
| SetResolvedFlag(kFixedSizeInline, fixed_size_block); |
| SetResolvedFlag(kFixedSizeBlock, fixed_size_inline); |
| SetResolvedFlag(kFixedSizeBlockIsDefinite, true); |
| SetResolvedFlag(kOrthogonalWritingModeRoot, true); |
| } |
| DCHECK_EQ(flags_ & kOrthogonalWritingModeRoot, !is_in_parallel_flow); |
| |
| // For ConstraintSpace instances created from layout objects, |
| // parent_writing_mode_ isn't actually the parent's, it's the same as the out |
| // writing mode. So we miss setting kOrthogonalWritingModeRoot on such |
| // constraint spaces unless it is forced. |
| if (builder.force_orthogonal_writing_mode_root_) { |
| DCHECK(is_in_parallel_flow) |
| << "Forced and inferred ortho writing mode shouldn't happen " |
| "simultaneously. Inferred means the constraints are in parent " |
| "writing mode, forced means they are in child writing mode. " |
| "parent_writing_mode_ = " |
| << static_cast<int>(builder.parent_writing_mode_) |
| << ", requested writing mode = " << static_cast<int>(out_writing_mode); |
| SetResolvedFlag(kOrthogonalWritingModeRoot, true); |
| SetResolvedFlag(kFixedSizeBlockIsDefinite, true); |
| } |
| |
| // If inline size is indefinite, use size of initial containing block. |
| // https://www.w3.org/TR/css-writing-modes-3/#orthogonal-auto |
| if (available_size_.inline_size == NGSizeIndefinite) { |
| DCHECK(!is_in_parallel_flow); |
| if (out_writing_mode == WritingMode::kHorizontalTb) { |
| available_size_.inline_size = initial_containing_block_size_.width; |
| } else { |
| available_size_.inline_size = initial_containing_block_size_.height; |
| } |
| } |
| if (percentage_resolution_size_.inline_size == NGSizeIndefinite) { |
| DCHECK(!is_in_parallel_flow); |
| if (out_writing_mode == WritingMode::kHorizontalTb) { |
| percentage_resolution_size_.inline_size = |
| initial_containing_block_size_.width; |
| } else { |
| percentage_resolution_size_.inline_size = |
| initial_containing_block_size_.height; |
| } |
| } |
| if (replaced_percentage_resolution_size_.inline_size == NGSizeIndefinite) { |
| DCHECK(!is_in_parallel_flow); |
| if (out_writing_mode == WritingMode::kHorizontalTb) { |
| replaced_percentage_resolution_size_.inline_size = |
| initial_containing_block_size_.width; |
| } else { |
| replaced_percentage_resolution_size_.inline_size = |
| initial_containing_block_size_.height; |
| } |
| } |
| |
| if (!is_new_fc && builder.exclusion_space_) |
| exclusion_space_ = *builder.exclusion_space_; |
| } |
| |
| NGConstraintSpace NGConstraintSpace::CreateFromLayoutObject( |
| const LayoutBox& box) { |
| auto writing_mode = box.StyleRef().GetWritingMode(); |
| bool parallel_containing_block = IsParallelWritingMode( |
| box.ContainingBlock()->StyleRef().GetWritingMode(), writing_mode); |
| bool fixed_inline = false, fixed_block = false; |
| bool fixed_block_is_definite = true; |
| |
| LayoutUnit available_logical_width; |
| if (parallel_containing_block && |
| box.HasOverrideContainingBlockContentLogicalWidth()) { |
| // Grid layout sets OverrideContainingBlockContentLogicalWidth|Height |
| available_logical_width = box.OverrideContainingBlockContentLogicalWidth(); |
| } else if (!parallel_containing_block && |
| box.HasOverrideContainingBlockContentLogicalHeight()) { |
| available_logical_width = box.OverrideContainingBlockContentLogicalHeight(); |
| } else { |
| if (parallel_containing_block) |
| available_logical_width = box.ContainingBlockLogicalWidthForContent(); |
| else |
| available_logical_width = box.PerpendicularContainingBlockLogicalHeight(); |
| } |
| available_logical_width = std::max(LayoutUnit(), available_logical_width); |
| |
| LayoutUnit available_logical_height; |
| if (parallel_containing_block && |
| box.HasOverrideContainingBlockContentLogicalHeight()) { |
| // Grid layout sets OverrideContainingBlockContentLogicalWidth|Height |
| available_logical_height = |
| box.OverrideContainingBlockContentLogicalHeight(); |
| } else if (!parallel_containing_block && |
| box.HasOverrideContainingBlockContentLogicalWidth()) { |
| available_logical_height = box.OverrideContainingBlockContentLogicalWidth(); |
| } else { |
| if (!box.Parent()) { |
| available_logical_height = box.View()->ViewLogicalHeightForPercentages(); |
| } else if (box.ContainingBlock()) { |
| if (parallel_containing_block) { |
| available_logical_height = |
| box.ContainingBlockLogicalHeightForPercentageResolution(); |
| } else { |
| available_logical_height = box.ContainingBlockLogicalWidthForContent(); |
| } |
| } |
| } |
| NGLogicalSize percentage_size = {available_logical_width, |
| available_logical_height}; |
| NGLogicalSize available_size = percentage_size; |
| if (box.HasOverrideLogicalWidth()) { |
| available_size.inline_size = box.OverrideLogicalWidth(); |
| fixed_inline = true; |
| } |
| if (box.HasOverrideLogicalHeight()) { |
| available_size.block_size = box.OverrideLogicalHeight(); |
| fixed_block = true; |
| } |
| if (box.IsFlexItem() && fixed_block) { |
| // The flexbox-specific behavior is in addition to regular definite-ness, so |
| // if the flex item would normally have a definite height it should keep it. |
| fixed_block_is_definite = |
| ToLayoutFlexibleBox(box.Parent()) |
| ->UseOverrideLogicalHeightForPerentageResolution(box) || |
| (box.IsLayoutBlock() && ToLayoutBlock(box).HasDefiniteLogicalHeight()); |
| } |
| |
| bool is_new_fc = true; |
| // TODO(ikilpatrick): This DCHECK needs to be enabled once we've switched |
| // LayoutTableCell, etc over to LayoutNG. |
| // |
| // We currently need to "force" LayoutNG roots to be formatting contexts so |
| // that floats have layout performed on them. |
| // |
| // DCHECK(is_new_fc, |
| // box.IsLayoutBlock() && ToLayoutBlock(box).CreatesNewFormattingContext()); |
| |
| IntSize icb_size = box.View()->GetLayoutSize(kExcludeScrollbars); |
| NGPhysicalSize initial_containing_block_size{LayoutUnit(icb_size.Width()), |
| LayoutUnit(icb_size.Height())}; |
| |
| // ICB cannot be indefinite by the spec. |
| DCHECK_GE(initial_containing_block_size.width, LayoutUnit()); |
| DCHECK_GE(initial_containing_block_size.height, LayoutUnit()); |
| |
| NGConstraintSpaceBuilder builder(writing_mode, initial_containing_block_size); |
| |
| if (!box.IsWritingModeRoot() || box.IsGridItem()) { |
| // Add all types because we don't know which baselines will be requested. |
| FontBaseline baseline_type = box.StyleRef().GetFontBaseline(); |
| bool synthesize_inline_block_baseline = |
| box.IsLayoutBlock() && |
| ToLayoutBlock(box).UseLogicalBottomMarginEdgeForInlineBlockBaseline(); |
| if (!synthesize_inline_block_baseline) { |
| builder.AddBaselineRequest( |
| {NGBaselineAlgorithmType::kAtomicInline, baseline_type}); |
| } |
| builder.AddBaselineRequest( |
| {NGBaselineAlgorithmType::kFirstLine, baseline_type}); |
| } |
| |
| return builder.SetAvailableSize(available_size) |
| .SetPercentageResolutionSize(percentage_size) |
| .SetIsFixedSizeInline(fixed_inline) |
| .SetIsFixedSizeBlock(fixed_block) |
| .SetFixedSizeBlockIsDefinite(fixed_block_is_definite) |
| .SetIsShrinkToFit( |
| box.SizesLogicalWidthToFitContent(box.StyleRef().LogicalWidth())) |
| .SetIsNewFormattingContext(is_new_fc) |
| .SetTextDirection(box.StyleRef().Direction()) |
| .SetIsOrthogonalWritingModeRoot(!parallel_containing_block) |
| .ToConstraintSpace(writing_mode); |
| } |
| |
| bool NGConstraintSpace::operator==(const NGConstraintSpace& other) const { |
| return available_size_ == other.available_size_ && |
| percentage_resolution_size_ == other.percentage_resolution_size_ && |
| replaced_percentage_resolution_size_ == |
| other.replaced_percentage_resolution_size_ && |
| initial_containing_block_size_ == |
| other.initial_containing_block_size_ && |
| fragmentainer_block_size_ == other.fragmentainer_block_size_ && |
| fragmentainer_space_at_bfc_start_ == |
| other.fragmentainer_space_at_bfc_start_ && |
| block_direction_fragmentation_type_ == |
| other.block_direction_fragmentation_type_ && |
| table_cell_child_layout_phase_ == |
| other.table_cell_child_layout_phase_ && |
| flags_ == other.flags_ && |
| adjoining_floats_ == other.adjoining_floats_ && |
| writing_mode_ == other.writing_mode_ && |
| direction_ == other.direction_ && |
| margin_strut_ == other.margin_strut_ && |
| bfc_offset_ == other.bfc_offset_ && |
| floats_bfc_block_offset_ == other.floats_bfc_block_offset_ && |
| exclusion_space_ == other.exclusion_space_ && |
| clearance_offset_ == other.clearance_offset_ && |
| baseline_requests_ == other.baseline_requests_; |
| } |
| |
| String NGConstraintSpace::ToString() const { |
| return String::Format("Offset: %s,%s Size: %sx%s Clearance: %s", |
| bfc_offset_.line_offset.ToString().Ascii().data(), |
| bfc_offset_.block_offset.ToString().Ascii().data(), |
| AvailableSize().inline_size.ToString().Ascii().data(), |
| AvailableSize().block_size.ToString().Ascii().data(), |
| HasClearanceOffset() |
| ? ClearanceOffset().ToString().Ascii().data() |
| : "none"); |
| } |
| |
| } // namespace blink |