| // Copyright 2016 The Chromium Authors |
| // 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_BOX_FRAGMENT_BUILDER_H_ |
| #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_BOX_FRAGMENT_BUILDER_H_ |
| |
| #include "base/check_op.h" |
| #include "base/dcheck_is_on.h" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| #include "third_party/blink/renderer/core/core_export.h" |
| #include "third_party/blink/renderer/core/layout/geometry/box_sides.h" |
| #include "third_party/blink/renderer/core/layout/geometry/physical_rect.h" |
| #include "third_party/blink/renderer/core/layout/ng/flex/ng_flex_data.h" |
| #include "third_party/blink/renderer/core/layout/ng/frame_set_layout_data.h" |
| #include "third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h" |
| #include "third_party/blink/renderer/core/layout/ng/geometry/ng_fragment_geometry.h" |
| #include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.h" |
| #include "third_party/blink/renderer/core/layout/ng/mathml/ng_mathml_paint_info.h" |
| #include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h" |
| #include "third_party/blink/renderer/core/layout/ng/ng_break_token.h" |
| #include "third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h" |
| #include "third_party/blink/renderer/core/layout/ng/ng_layout_overflow_calculator.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/table/ng_table_borders.h" |
| #include "third_party/blink/renderer/core/layout/ng/table/ng_table_fragment_data.h" |
| #include "third_party/blink/renderer/core/style/computed_style_constants.h" |
| #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" |
| #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h" |
| |
| namespace blink { |
| |
| class NGPhysicalFragment; |
| |
| class CORE_EXPORT NGBoxFragmentBuilder final |
| : public NGContainerFragmentBuilder { |
| STACK_ALLOCATED(); |
| |
| public: |
| NGBoxFragmentBuilder(NGLayoutInputNode node, |
| scoped_refptr<const ComputedStyle> style, |
| const NGConstraintSpace& space, |
| WritingDirectionMode writing_direction) |
| : NGContainerFragmentBuilder(node, |
| std::move(style), |
| space, |
| writing_direction), |
| box_type_(NGPhysicalFragment::NGBoxType::kNormalBox), |
| is_inline_formatting_context_(node.IsInline()) {} |
| |
| // Build a fragment for LayoutObject without NGLayoutInputNode. LayoutInline |
| // has NGInlineItem but does not have corresponding NGLayoutInputNode. |
| NGBoxFragmentBuilder(LayoutObject* layout_object, |
| scoped_refptr<const ComputedStyle> style, |
| const NGConstraintSpace& space, |
| WritingDirectionMode writing_direction) |
| : NGContainerFragmentBuilder(/* node */ nullptr, |
| std::move(style), |
| space, |
| writing_direction), |
| box_type_(NGPhysicalFragment::NGBoxType::kNormalBox), |
| is_inline_formatting_context_(true) { |
| layout_object_ = layout_object; |
| } |
| |
| void 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; |
| |
| border_padding_ = |
| initial_fragment_geometry.border + initial_fragment_geometry.padding; |
| border_scrollbar_padding_ = |
| border_padding_ + initial_fragment_geometry.scrollbar; |
| original_border_scrollbar_padding_block_start_ = |
| border_scrollbar_padding_.block_start; |
| if (node_) { |
| child_available_size_ = CalculateChildAvailableSize( |
| space_, To<NGBlockNode>(node_), size_, border_scrollbar_padding_); |
| } |
| } |
| |
| void AdjustBorderScrollbarPaddingForFragmentation( |
| const NGBlockBreakToken* break_token) { |
| if (LIKELY(!break_token)) |
| return; |
| if (break_token->IsBreakBefore()) |
| return; |
| border_scrollbar_padding_.block_start = LayoutUnit(); |
| } |
| |
| const NGFragmentGeometry& InitialFragmentGeometry() const { |
| DCHECK(initial_fragment_geometry_); |
| return *initial_fragment_geometry_; |
| } |
| |
| // Use the block-size setters/getters further down instead of the inherited |
| // ones. |
| LayoutUnit BlockSize() const = delete; |
| void SetBlockSize(LayoutUnit block_size) = delete; |
| |
| // Set the total border-box block-size of all the fragments to be generated |
| // from this node (as if we stitched them together). Layout algorithms are |
| // expected to pass this value, and at the end of layout (if block |
| // fragmentation is needed), the fragmentation machinery will be invoked to |
| // adjust the block-size to the correct size, ensuring that we break at the |
| // best location. |
| void SetFragmentsTotalBlockSize(LayoutUnit block_size) { |
| #if DCHECK_IS_ON() |
| // Note that we just store the block-size in a shared field. We have a flag |
| // for debugging, to assert that we know what we're doing when attempting to |
| // access the data. |
| block_size_is_for_all_fragments_ = true; |
| #endif |
| size_.block_size = block_size; |
| } |
| LayoutUnit FragmentsTotalBlockSize() const { |
| #if DCHECK_IS_ON() |
| if (has_block_fragmentation_) |
| DCHECK(block_size_is_for_all_fragments_); |
| DCHECK(size_.block_size != kIndefiniteSize); |
| #endif |
| return size_.block_size; |
| } |
| |
| // Set the final block-size of this fragment. |
| void SetFragmentBlockSize(LayoutUnit block_size) { |
| #if DCHECK_IS_ON() |
| // Note that we just store the block-size in a shared field. We have a flag |
| // for debugging, to assert that we know what we're doing when attempting to |
| // access the data. |
| block_size_is_for_all_fragments_ = false; |
| #endif |
| size_.block_size = block_size; |
| } |
| |
| LayoutUnit FragmentBlockSize() const { |
| #if DCHECK_IS_ON() |
| DCHECK(!block_size_is_for_all_fragments_ || !has_block_fragmentation_ || |
| IsInitialColumnBalancingPass()); |
| DCHECK(size_.block_size != kIndefiniteSize); |
| #endif |
| return size_.block_size; |
| } |
| |
| void SetIntrinsicBlockSize(LayoutUnit intrinsic_block_size) { |
| intrinsic_block_size_ = intrinsic_block_size; |
| } |
| LayoutUnit IntrinsicBlockSize() const { return intrinsic_block_size_; } |
| const NGBoxStrut& Borders() const { |
| DCHECK(initial_fragment_geometry_); |
| DCHECK_NE(BoxType(), NGPhysicalFragment::kInlineBox); |
| return initial_fragment_geometry_->border; |
| } |
| const NGBoxStrut& Scrollbar() const { |
| DCHECK(initial_fragment_geometry_); |
| return initial_fragment_geometry_->scrollbar; |
| } |
| 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& BorderPadding() const { |
| DCHECK(initial_fragment_geometry_); |
| return border_padding_; |
| } |
| const NGBoxStrut& BorderScrollbarPadding() const { |
| DCHECK(initial_fragment_geometry_); |
| return border_scrollbar_padding_; |
| } |
| LayoutUnit OriginalBorderScrollbarPaddingBlockStart() const { |
| return original_border_scrollbar_padding_block_start_; |
| } |
| // The child available-size is subtly different from the content-box size of |
| // an element. For an anonymous-block the child available-size is equal to |
| // its non-anonymous parent (similar to percentages). |
| const LogicalSize& ChildAvailableSize() const { |
| DCHECK(initial_fragment_geometry_); |
| return child_available_size_; |
| } |
| const NGBlockNode& Node() { |
| DCHECK(node_); |
| return To<NGBlockNode>(node_); |
| } |
| |
| // Add a break token for a child that doesn't yet have any fragments, because |
| // its first fragment is to be produced in the next fragmentainer. This will |
| // add a break token for the child, but no fragment. Break appeal should |
| // always be provided for regular in-flow children. For other types of |
| // children it may be omitted, if the break shouldn't affect the appeal of |
| // breaking inside this container. |
| void AddBreakBeforeChild(NGLayoutInputNode child, |
| absl::optional<NGBreakAppeal> appeal, |
| bool is_forced_break); |
| |
| // Add a layout result and propagate info from it. This involves appending the |
| // fragment and its relative offset to the builder, but also keeping track of |
| // out-of-flow positioned descendants, propagating fragmentainer breaks, and |
| // more. In some cases, such as grid, the relative offset may need to be |
| // computed ahead of time. If so, a |relative_offset| will be passed |
| // in. Otherwise, the relative offset will be calculated as normal. |
| // |inline_container| is passed when adding an OOF that is contained by a |
| // non-atomic inline. If |flex_column_break_after| is supplied, we are running |
| // layout for a column flex container, in which case, we need to update the |
| // break-after value for the column itself. |
| void AddResult( |
| const NGLayoutResult&, |
| const LogicalOffset, |
| absl::optional<LogicalOffset> relative_offset = absl::nullopt, |
| const NGInlineContainer<LogicalOffset>* inline_container = nullptr, |
| EBreakBetween* flex_column_break_after = nullptr); |
| |
| // Add a child fragment and propagate info from it. Called by AddResult(). |
| // Other callers should call AddResult() instead of this when possible, since |
| // there is information in the layout result that might need to be propagated. |
| void AddChild( |
| const NGPhysicalFragment&, |
| const LogicalOffset&, |
| const NGMarginStrut* margin_strut = nullptr, |
| bool is_self_collapsing = false, |
| absl::optional<LogicalOffset> relative_offset = absl::nullopt, |
| const NGInlineContainer<LogicalOffset>* inline_container = nullptr, |
| absl::optional<LayoutUnit> adjustment_for_oof_propagation = LayoutUnit()); |
| |
| // Manually add a break token to the builder. Note that we're assuming that |
| // this break token is for content in the same flow as this parent. |
| void AddBreakToken(const NGBreakToken*, bool is_in_parallel_flow = false); |
| |
| void AddOutOfFlowLegacyCandidate(NGBlockNode, |
| const NGLogicalStaticPosition&, |
| const LayoutInline* inline_container); |
| |
| // Remove the fragment previously generated for an out-of-flow positioned flex |
| // item inside an out-of-flow legacy flex container. This is a work-around for |
| // OOFs being laid out out-of-document-order, which is an issue with the |
| // legacy engine (although it's not known to cause any other actual problems |
| // than this). We'll call this method to correct a document-out-of-order |
| // issue. |
| void RemoveOldLegacyOOFFlexItem(const LayoutObject&); |
| |
| // Before layout we'll determine whether we can tell for sure that the node |
| // (or what's left of it to lay out, in case we've already broken) will fit in |
| // the current fragmentainer. If this is the case, we'll know that any |
| // block-end padding and border will come at the end of this fragment, and, if |
| // it's the first fragment for the node, this will make us ensure some child |
| // content before allowing breaks. See MustStayInCurrentFragmentainer(). |
| void SetIsKnownToFitInFragmentainer(bool b) { |
| is_known_to_fit_in_fragmentainer_ = b; |
| } |
| bool IsKnownToFitInFragmentainer() const { |
| return is_known_to_fit_in_fragmentainer_; |
| } |
| |
| // True if we need to keep some child content in the current fragmentainer |
| // before breaking (even that overflows the fragmentainer). We'll do this by |
| // refusing last-resort breaks when there's no container separation, and we'll |
| // instead overflow the fragmentainer. See MustStayInCurrentFragmentainer(). |
| void SetRequiresContentBeforeBreaking(bool b) { |
| requires_content_before_breaking_ = b; |
| } |
| bool RequiresContentBeforeBreaking() const { |
| return requires_content_before_breaking_; |
| } |
| |
| void SetIsBlockSizeForFragmentationClamped() { |
| is_block_size_for_fragmentation_clamped_ = true; |
| } |
| |
| // If a node fits in one fragmentainer due to restricted block-size, it must |
| // stay there, even if the first piece of child content should require more |
| // space than that (which would normally push the entire node into the next |
| // fragmentainer, since there typically is no valid breakpoint before the |
| // first child - only *between* siblings). Furthermore, any first piece of |
| // child content also needs to stay in the current fragmentainer, even if this |
| // causes fragmentainer overflow. This is not mandated by any spec, but it is |
| // compatible with Gecko, and is required in order to print Google Docs. |
| // |
| // See https://github.com/w3c/csswg-drafts/issues/6056#issuecomment-951767882 |
| bool MustStayInCurrentFragmentainer() const { |
| return is_known_to_fit_in_fragmentainer_ && is_first_for_node_; |
| } |
| |
| // Specify whether this will be the first fragment generated for the node. |
| void SetIsFirstForNode(bool is_first) { is_first_for_node_ = is_first; } |
| |
| void SetIsMonolithic(bool b) { is_monolithic_ = b; } |
| |
| // Set how much of the block-size we've used so far for this box. This will be |
| // the sum of the block-size of all previous fragments PLUS the one we're |
| // building now. |
| void SetConsumedBlockSize(LayoutUnit size) { |
| EnsureBreakTokenData()->consumed_block_size = size; |
| } |
| |
| // Set how much to adjust |consumed_block_size_| for legacy write-back. See |
| // NGBlockBreakToken::ConsumedBlockSizeForLegacy() for more details. |
| void SetConsumedBlockSizeLegacyAdjustment(LayoutUnit adjustment) { |
| EnsureBreakTokenData()->consumed_block_size_legacy_adjustment = adjustment; |
| } |
| |
| // Set how much of the column block-size we've used so far. This will be used |
| // to determine the block-size of any new columns added by descendant |
| // out-of-flow positioned elements. |
| void SetBlockOffsetForAdditionalColumns(LayoutUnit size) { |
| block_offset_for_additional_columns_ = size; |
| } |
| LayoutUnit BlockOffsetForAdditionalColumns() const { |
| return block_offset_for_additional_columns_; |
| } |
| |
| void SetSequenceNumber(unsigned sequence_number) { |
| EnsureBreakTokenData()->sequence_number = sequence_number; |
| } |
| |
| // During regular layout a break token is created at the end of layout, if |
| // required. When re-using a previous fragment and its children, though, we |
| // may want to just re-use the break token as well. |
| void PresetNextBreakToken(const NGBreakToken* break_token) { |
| // We should either do block fragmentation as part of normal layout, or |
| // pre-set a break token. |
| DCHECK(!did_break_self_); |
| DCHECK(child_break_tokens_.empty()); |
| |
| break_token_ = break_token; |
| } |
| |
| // Return true if we broke inside this node on our own initiative (typically |
| // not because of a child break, but rather due to the size of this node). |
| bool DidBreakSelf() const { return did_break_self_; } |
| void SetDidBreakSelf() { did_break_self_ = true; } |
| |
| // Store the previous break token, if one exists. |
| void SetPreviousBreakToken(const NGBlockBreakToken* break_token) { |
| previous_break_token_ = break_token; |
| } |
| const NGBlockBreakToken* PreviousBreakToken() const { |
| return previous_break_token_; |
| } |
| |
| // Return true if a break has been inserted, doesn't matter if it's in the |
| // same flow or not. As long as there are only breaks in parallel flows, we |
| // may continue layout, but when we're done, we'll need to create a break |
| // token for this fragment nevertheless, so that we re-enter, descend and |
| // resume at the broken children in the next fragmentainer. |
| bool HasInsertedChildBreak() const { |
| if (child_break_tokens_.empty()) |
| return false; |
| for (const NGBreakToken* child_token : child_break_tokens_) { |
| const auto* block_child_token = DynamicTo<NGBlockBreakToken>(child_token); |
| if (!block_child_token || !block_child_token->IsRepeated()) |
| return true; |
| } |
| return false; |
| } |
| |
| // Return true if we need to break inside this node, the way things are |
| // currently looking. This should only be called at the end of layout, right |
| // before creating a fragment. |
| bool ShouldBreakInside() const { |
| if (HasInsertedChildBreak()) |
| return true; |
| // If there's an outgoing inline break-token at this point, and we're about |
| // to finish layout, it means that inline layout needs to continue in the |
| // next fragmentianer. |
| if (last_inline_break_token_) |
| return true; |
| // Grid layout doesn't insert break before tokens, and instead set this bit |
| // to indicate there is content after the current break. |
| return has_subsequent_children_; |
| } |
| |
| // Return true if we need to break before or inside any in-flow child that |
| // doesn't establish a parallel flow. When this happens, we want to finish our |
| // fragment, create a break token, and resume in the next fragmentainer. |
| bool HasInflowChildBreakInside() const { |
| return has_inflow_child_break_inside_; |
| } |
| |
| // Report space shortage, i.e. how much more space would have been sufficient |
| // to prevent some piece of content from breaking. This information may be |
| // used by the column balancer to stretch columns. |
| void PropagateSpaceShortage(absl::optional<LayoutUnit> space_shortage); |
| |
| absl::optional<LayoutUnit> MinimalSpaceShortage() const { |
| if (minimal_space_shortage_ == kIndefiniteSize) |
| return absl::nullopt; |
| return minimal_space_shortage_; |
| } |
| |
| void PropagateTallestUnbreakableBlockSize(LayoutUnit unbreakable_block_size) { |
| // We should only calculate the block-size of the tallest piece of |
| // unbreakable content during the initial column balancing pass, when we |
| // haven't set a tentative fragmentainer block-size yet. |
| DCHECK(IsInitialColumnBalancingPass()); |
| |
| tallest_unbreakable_block_size_ = |
| std::max(tallest_unbreakable_block_size_, unbreakable_block_size); |
| } |
| |
| void SetIsInitialColumnBalancingPass() { |
| // Note that we have no dedicated flag for being in the initial column |
| // balancing pass here. We'll just bump tallest_unbreakable_block_size_ to |
| // 0, so that NGLayoutResult knows that we need to store unbreakable |
| // block-size. |
| DCHECK_EQ(tallest_unbreakable_block_size_, LayoutUnit::Min()); |
| tallest_unbreakable_block_size_ = LayoutUnit(); |
| } |
| bool IsInitialColumnBalancingPass() const { |
| return tallest_unbreakable_block_size_ >= LayoutUnit(); |
| } |
| |
| void SetInitialBreakBefore(EBreakBetween break_before) { |
| initial_break_before_ = break_before; |
| } |
| |
| void SetInitialBreakBeforeIfNeeded(EBreakBetween break_before) { |
| if (!initial_break_before_) |
| initial_break_before_ = break_before; |
| } |
| |
| void SetPreviousBreakAfter(EBreakBetween break_after) { |
| previous_break_after_ = break_after; |
| } |
| |
| void SetStartPageNameIfNeeded(AtomicString name) { |
| if (start_page_name_ == g_null_atom) |
| start_page_name_ = name; |
| } |
| void SetPreviousPageName(AtomicString name) { previous_page_name_ = name; } |
| AtomicString PreviousPageName() const { return previous_page_name_; } |
| |
| void SetPageName(const AtomicString name) { |
| if (!name) |
| return; |
| if (page_name_) { |
| DCHECK_EQ(page_name_, name); |
| return; |
| } |
| page_name_ = name; |
| } |
| AtomicString PageName() const { return page_name_; } |
| |
| // Join/"collapse" the previous (stored) break-after value with the next |
| // break-before value, to determine how to deal with breaking between two |
| // in-flow siblings. |
| EBreakBetween JoinedBreakBetweenValue(EBreakBetween break_before) const; |
| |
| // Return the number of line boxes laid out. |
| int LineCount() const { return line_count_; } |
| void SetLineCount(int line_count) { line_count_ = line_count; } |
| |
| // Set when we have iterated over all the children. This means that all |
| // children have been fully laid out, or have break tokens. No more children |
| // left to discover. |
| void SetHasSeenAllChildren() { has_seen_all_children_ = true; } |
| bool HasSeenAllChildren() { return has_seen_all_children_; } |
| |
| void SetHasSubsequentChildren() { has_subsequent_children_ = true; } |
| |
| void SetIsAtBlockEnd() { is_at_block_end_ = true; } |
| bool IsAtBlockEnd() const { return is_at_block_end_; } |
| |
| void SetDisableOOFDescendantsPropagation() { |
| disable_oof_descendants_propagation_ = true; |
| } |
| |
| void SetDisableSimplifiedLayout() { disable_simplified_layout = true; } |
| |
| void SetIsTruncatedByFragmentationLine() { |
| is_truncated_by_fragmentation_line = true; |
| } |
| |
| // See |NGPhysicalBoxFragment::InflowBounds|. |
| void SetInflowBounds(const LogicalRect& inflow_bounds) { |
| DCHECK_NE(box_type_, NGPhysicalBoxFragment::NGBoxType::kInlineBox); |
| DCHECK(Node().IsScrollContainer()); |
| #if DCHECK_IS_ON() |
| is_inflow_bounds_explicitly_set_ = true; |
| #endif |
| inflow_bounds_ = inflow_bounds; |
| } |
| |
| void SetEarlyBreak(const NGEarlyBreak* breakpoint) { |
| early_break_ = breakpoint; |
| } |
| bool HasEarlyBreak() const { return early_break_; } |
| const NGEarlyBreak& EarlyBreak() const { |
| DCHECK(early_break_); |
| return *early_break_; |
| } |
| |
| // Downgrade the break appeal if the specified break appeal is lower than any |
| // found so far. |
| void ClampBreakAppeal(NGBreakAppeal appeal) { |
| break_appeal_ = std::min(break_appeal_, appeal); |
| } |
| |
| // Creates the fragment. Can only be called once. |
| const NGLayoutResult* ToBoxFragment() { |
| DCHECK_NE(BoxType(), NGPhysicalFragment::kInlineBox); |
| return ToBoxFragment(GetWritingMode()); |
| } |
| const NGLayoutResult* ToInlineBoxFragment() { |
| // The logical coordinate for inline box uses line-relative writing-mode, |
| // not |
| // flow-relative. |
| DCHECK_EQ(BoxType(), NGPhysicalFragment::kInlineBox); |
| return ToBoxFragment(ToLineWritingMode(GetWritingMode())); |
| } |
| |
| NGPhysicalFragment::NGBoxType BoxType() const; |
| void SetBoxType(NGPhysicalFragment::NGBoxType box_type) { |
| box_type_ = box_type; |
| } |
| bool IsFragmentainerBoxType() const { |
| NGPhysicalFragment::NGBoxType box_type = BoxType(); |
| return box_type == NGPhysicalFragment::kColumnBox || |
| box_type == NGPhysicalFragment::kPageBox; |
| } |
| void SetIsFieldsetContainer() { is_fieldset_container_ = true; } |
| void SetIsTableNGPart() { is_table_ng_part_ = true; } |
| void SetIsLegacyLayoutRoot() { is_legacy_layout_root_ = true; } |
| |
| void SetIsInlineFormattingContext(bool is_inline_formatting_context) { |
| is_inline_formatting_context_ = is_inline_formatting_context; |
| } |
| |
| void SetIsMathMLFraction() { is_math_fraction_ = true; } |
| void SetIsMathMLOperator() { is_math_operator_ = true; } |
| void SetMathMLPaintInfo( |
| UChar operator_character, |
| scoped_refptr<const ShapeResultView> operator_shape_result_view, |
| LayoutUnit operator_inline_size, |
| LayoutUnit operator_ascent, |
| LayoutUnit operator_descent) { |
| if (!mathml_paint_info_) |
| mathml_paint_info_ = std::make_unique<NGMathMLPaintInfo>(); |
| |
| mathml_paint_info_->operator_character = operator_character; |
| mathml_paint_info_->operator_shape_result_view = |
| std::move(operator_shape_result_view); |
| |
| mathml_paint_info_->operator_inline_size = operator_inline_size; |
| mathml_paint_info_->operator_ascent = operator_ascent; |
| mathml_paint_info_->operator_descent = operator_descent; |
| } |
| void SetMathMLPaintInfo( |
| scoped_refptr<const ShapeResultView> operator_shape_result_view, |
| LayoutUnit operator_inline_size, |
| LayoutUnit operator_ascent, |
| LayoutUnit operator_descent, |
| LayoutUnit radical_operator_inline_offset, |
| const NGBoxStrut& radical_base_margins) { |
| if (!mathml_paint_info_) |
| mathml_paint_info_ = std::make_unique<NGMathMLPaintInfo>(); |
| |
| mathml_paint_info_->operator_character = kSquareRootCharacter; |
| mathml_paint_info_->operator_shape_result_view = |
| std::move(operator_shape_result_view); |
| |
| mathml_paint_info_->operator_inline_size = operator_inline_size; |
| mathml_paint_info_->operator_ascent = operator_ascent; |
| mathml_paint_info_->operator_descent = operator_descent; |
| mathml_paint_info_->radical_base_margins = radical_base_margins; |
| mathml_paint_info_->radical_operator_inline_offset = |
| radical_operator_inline_offset; |
| } |
| |
| void SetSidesToInclude(LogicalBoxSides sides_to_include) { |
| sides_to_include_ = sides_to_include; |
| } |
| |
| // Either this function or SetBoxType must be called before ToBoxFragment(). |
| void SetIsNewFormattingContext(bool is_new_fc) { is_new_fc_ = is_new_fc; } |
| |
| void SetCustomLayoutData( |
| scoped_refptr<SerializedScriptValue> custom_layout_data) { |
| custom_layout_data_ = std::move(custom_layout_data); |
| } |
| |
| // Sets the first baseline for this fragment. |
| void SetFirstBaseline(LayoutUnit baseline) { first_baseline_ = baseline; } |
| absl::optional<LayoutUnit> FirstBaseline() const { return first_baseline_; } |
| |
| // Sets the last baseline for this fragment. |
| void SetLastBaseline(LayoutUnit baseline) { last_baseline_ = baseline; } |
| absl::optional<LayoutUnit> LastBaseline() const { return last_baseline_; } |
| |
| // Sets both the first and last baseline to the same value. |
| void SetBaselines(LayoutUnit baseline) { |
| first_baseline_ = baseline; |
| last_baseline_ = baseline; |
| } |
| |
| // Lets the parent layout algorithm know if it should use the first or last |
| // baseline for the special inline-block baseline algorithm. |
| void SetUseLastBaselineForInlineBaseline() { |
| use_last_baseline_for_inline_baseline_ = true; |
| } |
| |
| void SetTableGridRect(const LogicalRect& table_grid_rect) { |
| table_grid_rect_ = table_grid_rect; |
| } |
| |
| void SetTableColumnGeometries( |
| const NGTableFragmentData::ColumnGeometries& table_column_geometries) { |
| table_column_geometries_ = table_column_geometries; |
| } |
| |
| void SetTableCollapsedBorders(const NGTableBorders& table_collapsed_borders) { |
| table_collapsed_borders_ = &table_collapsed_borders; |
| } |
| |
| void SetTableCollapsedBordersGeometry( |
| std::unique_ptr<NGTableFragmentData::CollapsedBordersGeometry> |
| table_collapsed_borders_geometry) { |
| table_collapsed_borders_geometry_ = |
| std::move(table_collapsed_borders_geometry); |
| } |
| |
| void SetTableColumnCount(wtf_size_t table_column_count) { |
| table_column_count_ = table_column_count; |
| } |
| |
| void SetTableCellColumnIndex(wtf_size_t table_cell_column_index) { |
| table_cell_column_index_ = table_cell_column_index; |
| } |
| |
| void SetTableSectionCollapsedBordersGeometry( |
| wtf_size_t start_row_index, |
| Vector<LayoutUnit>&& row_offsets) { |
| table_section_start_row_index_ = start_row_index; |
| table_section_row_offsets_ = std::move(row_offsets); |
| } |
| |
| void TransferGridLayoutData( |
| std::unique_ptr<NGGridLayoutData> grid_layout_data) { |
| grid_layout_data_ = std::move(grid_layout_data); |
| } |
| void TransferFlexLayoutData( |
| std::unique_ptr<DevtoolsFlexInfo> flex_layout_data) { |
| flex_layout_data_ = std::move(flex_layout_data); |
| } |
| void TransferFrameSetLayoutData(std::unique_ptr<FrameSetLayoutData> data) { |
| frame_set_layout_data_ = std::move(data); |
| } |
| |
| const NGGridLayoutData& GridLayoutData() const { |
| DCHECK(grid_layout_data_); |
| return *grid_layout_data_.get(); |
| } |
| |
| bool HasBreakTokenData() const { return break_token_data_; } |
| |
| NGBlockBreakTokenData* EnsureBreakTokenData() { |
| if (!HasBreakTokenData()) |
| break_token_data_ = MakeGarbageCollected<NGBlockBreakTokenData>(); |
| return break_token_data_; |
| } |
| |
| NGBlockBreakTokenData* GetBreakTokenData() { return break_token_data_; } |
| |
| void SetBreakTokenData(NGBlockBreakTokenData* break_token_data) { |
| break_token_data_ = break_token_data; |
| } |
| |
| // The |NGFragmentItemsBuilder| for the inline formatting context of this box. |
| bool HasItems() const final { return items_builder_; } |
| NGFragmentItemsBuilder* ItemsBuilder() { return items_builder_; } |
| void SetItemsBuilder(NGFragmentItemsBuilder* builder) { |
| items_builder_ = builder; |
| } |
| |
| // Returns offset for given child. DCHECK if child not found. |
| // Warning: Do not call unless necessary. |
| LogicalOffset GetChildOffset(const LayoutObject* child) const; |
| |
| #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 |
| |
| // Moves all the children by |offset| in the block-direction. (Ensure that |
| // any baselines, OOFs, etc, are also moved by the appropriate amount). |
| void MoveChildrenInBlockDirection(LayoutUnit offset); |
| |
| void SetMathItalicCorrection(LayoutUnit italic_correction) { |
| math_italic_correction_ = italic_correction; |
| } |
| |
| void AdjustFragmentainerDescendant( |
| NGLogicalOOFNodeForFragmentation& descendant, |
| bool only_fixedpos_containing_block = false); |
| void AdjustFixedposContainingBlockForFragmentainerDescendants(); |
| void AdjustFixedposContainingBlockForInnerMulticols(); |
| |
| // OOF positioned elements inside a fragmentation context are laid out once |
| // they reach the fragmentation context root, so we need to adjust the offset |
| // of its containing block to be relative to the fragmentation context |
| // root. This allows us to determine the proper offset for the OOF inside the |
| // same context. The block offset returned is the block contribution from |
| // previous fragmentainers, if the current builder is a fragmentainer. |
| // Otherwise, |fragmentainer_consumed_block_size| will be used. In some cases, |
| // for example, we won't be able to calculate the adjustment from the builder. |
| // This would happen when an OOF positioned element is nested inside another |
| // OOF positioned element. The nested OOF will never have propagated up |
| // through a fragmentainer builder. In such cases, the necessary adjustment |
| // will be passed in via |fragmentainer_consumed_block_size|. |
| LayoutUnit BlockOffsetAdjustmentForFragmentainer( |
| LayoutUnit fragmentainer_consumed_block_size = LayoutUnit()) const; |
| |
| void SetHasForcedBreak() { |
| has_forced_break_ = true; |
| minimal_space_shortage_ = kIndefiniteSize; |
| } |
| |
| bool HasForcedBreak() const { return has_forced_break_; } |
| |
| const NGBreakToken* LastChildBreakToken() const { |
| DCHECK(!child_break_tokens_.empty()); |
| return child_break_tokens_.back().Get(); |
| } |
| |
| void InsertLegacyPositionedObject(const NGBlockNode& positioned) const { |
| positioned.InsertIntoLegacyPositionedObjectsOf( |
| To<LayoutBlock>(layout_object_)); |
| } |
| |
| // Propagate the break-before/break-after of the child (if applicable). |
| // Update the break-after value for |flex_column_break_after|, if supplied. |
| void PropagateChildBreakValues( |
| const NGLayoutResult& child_layout_result, |
| EBreakBetween* flex_column_break_after = nullptr); |
| |
| private: |
| // Propagate fragmentation details. This includes checking whether we have |
| // fragmented in this flow, break appeal, column spanner detection, and column |
| // balancing hints. |
| void PropagateBreakInfo(const NGLayoutResult&, LogicalOffset); |
| |
| const NGLayoutResult* ToBoxFragment(WritingMode); |
| |
| const NGFragmentGeometry* initial_fragment_geometry_ = nullptr; |
| NGBoxStrut border_padding_; |
| NGBoxStrut border_scrollbar_padding_; |
| // We clamp the block-start of |border_scrollbar_padding_| after an item |
| // fragments. Store the original block-start, as well, for cases where it is |
| // needed. |
| LayoutUnit original_border_scrollbar_padding_block_start_; |
| LogicalSize child_available_size_; |
| LayoutUnit intrinsic_block_size_; |
| absl::optional<LogicalRect> inflow_bounds_; |
| |
| NGFragmentItemsBuilder* items_builder_ = nullptr; |
| |
| NGPhysicalFragment::NGBoxType box_type_; |
| bool is_fieldset_container_ = false; |
| bool is_table_ng_part_ = false; |
| bool is_initial_block_size_indefinite_ = false; |
| bool is_inline_formatting_context_; |
| bool is_known_to_fit_in_fragmentainer_ = false; |
| bool requires_content_before_breaking_ = false; |
| bool is_block_size_for_fragmentation_clamped_ = false; |
| bool is_monolithic_ = true; |
| bool is_first_for_node_ = true; |
| bool did_break_self_ = false; |
| bool has_inflow_child_break_inside_ = false; |
| bool has_forced_break_ = false; |
| bool is_new_fc_ = false; |
| bool has_seen_all_children_ = false; |
| bool has_subsequent_children_ = false; |
| bool is_math_fraction_ = false; |
| bool is_math_operator_ = false; |
| bool is_at_block_end_ = false; |
| bool disable_oof_descendants_propagation_ = false; |
| bool disable_simplified_layout = false; |
| bool is_truncated_by_fragmentation_line = false; |
| bool use_last_baseline_for_inline_baseline_ = false; |
| LayoutUnit block_offset_for_additional_columns_; |
| |
| LayoutUnit minimal_space_shortage_ = kIndefiniteSize; |
| LayoutUnit tallest_unbreakable_block_size_ = LayoutUnit::Min(); |
| LayoutUnit block_size_for_fragmentation_; |
| |
| // The break-before value on the initial child we cannot honor. There's no |
| // valid class A break point before a first child, only *between* siblings. |
| absl::optional<EBreakBetween> initial_break_before_; |
| |
| // The break-after value of the previous in-flow sibling. |
| EBreakBetween previous_break_after_ = EBreakBetween::kAuto; |
| |
| AtomicString start_page_name_ = g_null_atom; |
| AtomicString previous_page_name_; |
| |
| AtomicString page_name_; |
| |
| // The appeal of breaking inside this container. |
| NGBreakAppeal break_appeal_ = kBreakAppealPerfect; |
| |
| absl::optional<LayoutUnit> first_baseline_; |
| absl::optional<LayoutUnit> last_baseline_; |
| LayoutUnit math_italic_correction_; |
| |
| // Table specific types. |
| absl::optional<LogicalRect> table_grid_rect_; |
| NGTableFragmentData::ColumnGeometries table_column_geometries_; |
| scoped_refptr<const NGTableBorders> table_collapsed_borders_; |
| std::unique_ptr<NGTableFragmentData::CollapsedBordersGeometry> |
| table_collapsed_borders_geometry_; |
| absl::optional<wtf_size_t> table_column_count_; |
| |
| // Table cell specific types. |
| absl::optional<wtf_size_t> table_cell_column_index_; |
| wtf_size_t table_section_start_row_index_; |
| Vector<LayoutUnit> table_section_row_offsets_; |
| |
| NGBlockBreakTokenData* break_token_data_ = nullptr; |
| |
| // Grid specific types. |
| std::unique_ptr<NGGridLayoutData> grid_layout_data_; |
| |
| std::unique_ptr<DevtoolsFlexInfo> flex_layout_data_; |
| std::unique_ptr<FrameSetLayoutData> frame_set_layout_data_; |
| |
| LogicalBoxSides sides_to_include_; |
| |
| scoped_refptr<SerializedScriptValue> custom_layout_data_; |
| |
| std::unique_ptr<NGMathMLPaintInfo> mathml_paint_info_; |
| |
| const NGBlockBreakToken* previous_break_token_ = nullptr; |
| |
| #if DCHECK_IS_ON() |
| // Describes what size_.block_size represents; either the size of a single |
| // fragment (false), or the size of all fragments for a node (true). |
| bool block_size_is_for_all_fragments_ = false; |
| |
| // If any fragment has been added with an offset including the relative |
| // position, we also need the inflow-bounds set explicitly. |
| bool needs_inflow_bounds_explicitly_set_ = false; |
| bool needs_may_have_descendant_above_block_start_explicitly_set_ = false; |
| bool is_inflow_bounds_explicitly_set_ = false; |
| #endif |
| |
| friend class NGBlockBreakToken; |
| friend class NGPhysicalBoxFragment; |
| friend class NGLayoutResult; |
| }; |
| |
| } // namespace blink |
| |
| #endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_BOX_FRAGMENT_BUILDER_H_ |