| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_LOGICAL_LINE_ITEM_H_ |
| #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_LOGICAL_LINE_ITEM_H_ |
| |
| #include "third_party/blink/renderer/core/layout/geometry/logical_rect.h" |
| #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h" |
| #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h" |
| #include "third_party/blink/renderer/core/layout/ng/inline/ng_text_offset.h" |
| #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" |
| #include "third_party/blink/renderer/platform/fonts/shaping/shape_result_view.h" |
| #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" |
| |
| namespace blink { |
| |
| class LayoutObject; |
| |
| // This class represents an item in a line, after line break, but still mutable |
| // and in the logical coordinate system. |
| struct NGLogicalLineItem { |
| DISALLOW_NEW(); |
| |
| // Empty constructor needed for |resize()|. |
| NGLogicalLineItem() = default; |
| // Create a placeholder. A placeholder does not have a fragment nor a bidi |
| // level. |
| NGLogicalLineItem(LayoutUnit block_offset, LayoutUnit block_size) |
| : rect(LayoutUnit(), block_offset, LayoutUnit(), block_size) {} |
| // Crete a bidi control. A bidi control does not have a fragment, but has |
| // bidi level and affects bidi reordering. |
| explicit NGLogicalLineItem(UBiDiLevel bidi_level) : bidi_level(bidi_level) {} |
| // Create an in-flow |NGLayoutResult|. |
| NGLogicalLineItem(scoped_refptr<const NGLayoutResult> layout_result, |
| const LogicalRect& rect, |
| unsigned children_count, |
| UBiDiLevel bidi_level) |
| : layout_result(std::move(layout_result)), |
| rect(rect), |
| children_count(children_count), |
| bidi_level(bidi_level) {} |
| NGLogicalLineItem(scoped_refptr<const NGLayoutResult> layout_result, |
| LogicalOffset offset, |
| LayoutUnit inline_size, |
| unsigned children_count, |
| UBiDiLevel bidi_level) |
| : layout_result(std::move(layout_result)), |
| rect(offset, LogicalSize()), |
| inline_size(inline_size), |
| children_count(children_count), |
| bidi_level(bidi_level) {} |
| // Create an in-flow text fragment. |
| NGLogicalLineItem(const NGInlineItem& inline_item, |
| NGInlineItemResult& item_result, |
| const NGTextOffset& text_offset, |
| LayoutUnit block_offset, |
| LayoutUnit inline_size, |
| LayoutUnit text_height, |
| UBiDiLevel bidi_level) |
| : inline_item(&inline_item), |
| shape_result(std::move(item_result.shape_result)), |
| text_offset(text_offset), |
| rect(LayoutUnit(), block_offset, LayoutUnit(), text_height), |
| inline_size(inline_size), |
| bidi_level(bidi_level), |
| has_only_trailing_spaces(item_result.has_only_trailing_spaces) {} |
| NGLogicalLineItem(const NGInlineItem& inline_item, |
| scoped_refptr<const ShapeResultView> shape_result, |
| const NGTextOffset& text_offset, |
| LayoutUnit block_offset, |
| LayoutUnit inline_size, |
| LayoutUnit text_height, |
| UBiDiLevel bidi_level) |
| : inline_item(&inline_item), |
| shape_result(std::move(shape_result)), |
| text_offset(text_offset), |
| rect(LayoutUnit(), block_offset, LayoutUnit(), text_height), |
| inline_size(inline_size), |
| bidi_level(bidi_level) {} |
| // Create a generated text. |
| NGLogicalLineItem(const NGInlineItem& inline_item, |
| scoped_refptr<const ShapeResultView> shape_result, |
| const String& text_content, |
| LayoutUnit block_offset, |
| LayoutUnit inline_size, |
| LayoutUnit text_height, |
| UBiDiLevel bidi_level) |
| : inline_item(&inline_item), |
| shape_result(std::move(shape_result)), |
| text_offset( |
| {this->shape_result->StartIndex(), this->shape_result->EndIndex()}), |
| text_content(text_content), |
| rect(LayoutUnit(), block_offset, LayoutUnit(), text_height), |
| inline_size(inline_size), |
| bidi_level(bidi_level) {} |
| // Create an ellipsis item. Ellipsis is a generated text, but does not have |
| // corresponding |NGInlineItem| as it is generated by layout. |
| NGLogicalLineItem(const LayoutObject& layout_object, |
| NGStyleVariant style_variant, |
| scoped_refptr<const ShapeResultView> shape_result, |
| const String& text_content, |
| const LogicalRect& rect, |
| UBiDiLevel bidi_level) |
| : shape_result(std::move(shape_result)), |
| text_offset( |
| {this->shape_result->StartIndex(), this->shape_result->EndIndex()}), |
| text_content(text_content), |
| layout_object(&layout_object), |
| style_variant(style_variant), |
| rect(rect), |
| inline_size(rect.size.inline_size), |
| bidi_level(bidi_level) {} |
| NGLogicalLineItem(const NGLogicalLineItem& source_item, |
| scoped_refptr<const ShapeResultView> shape_result, |
| const NGTextOffset& text_offset) |
| : inline_item(source_item.inline_item), |
| shape_result(std::move(shape_result)), |
| text_offset(text_offset), |
| text_content(source_item.text_content), |
| rect(source_item.rect), |
| inline_size(this->shape_result->SnappedWidth()), |
| bidi_level(source_item.bidi_level) {} |
| // Create an out-of-flow positioned object. |
| NGLogicalLineItem(LayoutObject* out_of_flow_positioned_box, |
| UBiDiLevel bidi_level, |
| TextDirection container_direction) |
| : out_of_flow_positioned_box(out_of_flow_positioned_box), |
| bidi_level(bidi_level), |
| container_direction(container_direction) {} |
| // Create an unpositioned float. |
| NGLogicalLineItem(LayoutObject* unpositioned_float, UBiDiLevel bidi_level) |
| : unpositioned_float(unpositioned_float), bidi_level(bidi_level) {} |
| // Create a positioned float. |
| NGLogicalLineItem(scoped_refptr<const NGLayoutResult> layout_result, |
| NGBfcOffset bfc_offset, |
| UBiDiLevel bidi_level) |
| : layout_result(std::move(layout_result)), |
| bfc_offset(bfc_offset), |
| bidi_level(bidi_level) {} |
| |
| bool IsInlineBox() const { |
| return layout_result && layout_result->PhysicalFragment().IsInlineBox(); |
| } |
| bool HasInFlowFragment() const { |
| return inline_item || |
| (layout_result && !layout_result->PhysicalFragment().IsFloating()); |
| } |
| bool HasInFlowOrFloatingFragment() const { |
| return inline_item || layout_result || layout_object; |
| } |
| bool HasOutOfFlowFragment() const { return out_of_flow_positioned_box; } |
| bool HasFragment() const { |
| return HasInFlowOrFloatingFragment() || HasOutOfFlowFragment(); |
| } |
| bool IsControl() const { |
| return inline_item && inline_item->Type() == NGInlineItem::kControl; |
| } |
| bool CanCreateFragmentItem() const { return HasInFlowOrFloatingFragment(); } |
| bool HasBidiLevel() const { return bidi_level != 0xff; } |
| bool IsPlaceholder() const { return !HasFragment() && !HasBidiLevel(); } |
| bool IsOpaqueToBidiReordering() const { |
| if (IsPlaceholder()) |
| return true; |
| // Skip all inline boxes. Fragments for inline boxes maybe created earlier |
| // if they have no children. |
| if (layout_result) { |
| DCHECK(layout_result->PhysicalFragment().GetLayoutObject()); |
| if (layout_result->PhysicalFragment().GetLayoutObject()->IsLayoutInline()) |
| return true; |
| } |
| return false; |
| } |
| |
| const LogicalOffset& Offset() const { return rect.offset; } |
| LayoutUnit InlineOffset() const { return rect.offset.inline_offset; } |
| LayoutUnit BlockOffset() const { return rect.offset.block_offset; } |
| LayoutUnit BlockEndOffset() const { return rect.BlockEndOffset(); } |
| const LogicalSize& Size() const { return rect.size; } |
| LogicalSize MarginSize() const { return {inline_size, Size().block_size}; } |
| |
| const NGPhysicalContainerFragment* PhysicalFragment() const { |
| if (layout_result) |
| return &layout_result->PhysicalFragment(); |
| return nullptr; |
| } |
| const LayoutObject* GetLayoutObject() const; |
| LayoutObject* GetMutableLayoutObject() const; |
| const Node* GetNode() const; |
| const ComputedStyle* Style() const; |
| |
| unsigned StartOffset() const { return text_offset.start; } |
| unsigned EndOffset() const { return text_offset.end; } |
| |
| TextDirection ResolvedDirection() const { |
| // Inline boxes are not leaves that they don't have directions. |
| DCHECK(HasBidiLevel() || IsInlineBox()); |
| return HasBidiLevel() ? DirectionFromLevel(bidi_level) |
| : TextDirection::kLtr; |
| } |
| |
| scoped_refptr<const NGLayoutResult> layout_result; |
| |
| // Data to create a text fragment from. |
| // |inline_item| is null only for ellipsis items. |
| const NGInlineItem* inline_item = nullptr; |
| scoped_refptr<const ShapeResultView> shape_result; |
| NGTextOffset text_offset; |
| |
| // Data to create a generated text fragment. |
| String text_content; |
| |
| // Ellipsis does not have |NGInlineItem|, but built from |LayoutObject| and |
| // |NGStyleVariant|. |
| const LayoutObject* layout_object = nullptr; |
| // Used only when |layout_object_| is not null. |
| NGStyleVariant style_variant = NGStyleVariant::kStandard; |
| |
| LayoutObject* out_of_flow_positioned_box = nullptr; |
| LayoutObject* unpositioned_float = nullptr; |
| // The offset of the border box, initially in this child coordinate system. |
| // |ComputeInlinePositions()| converts it to the offset within the line box. |
| LogicalRect rect; |
| // The offset of a positioned float wrt. the root BFC. This should only be |
| // set for positioned floats. |
| NGBfcOffset bfc_offset; |
| // The inline size of the margin box. |
| LayoutUnit inline_size; |
| LayoutUnit margin_line_left; |
| // The index of |box_data_list_|, used in |PrepareForReorder()| and |
| // |UpdateAfterReorder()| to track children of boxes across BiDi reorder. |
| unsigned box_data_index = 0; |
| // For an inline box, shows the number of descendant |Child|ren, including |
| // empty ones. Includes itself, so 1 means no descendants. 0 if not an |
| // inline box. Available only after |CreateBoxFragments()|. |
| unsigned children_count = 0; |
| UBiDiLevel bidi_level = 0xff; |
| // The current text direction for OOF positioned items. |
| TextDirection container_direction = TextDirection::kLtr; |
| // When an item contains only trailing spaces, the original bidi level needs |
| // to be ignored, and just use paragraph direction (UAX#9 L1) |
| bool has_only_trailing_spaces = false; |
| |
| bool is_hidden_for_paint = false; |
| }; |
| |
| CORE_EXPORT std::ostream& operator<<(std::ostream& stream, |
| const NGLogicalLineItem& item); |
| |
| // A vector of Child. |
| // Unlike the fragment builder, chlidren are mutable. |
| // Callers can add to the fragment builder in a batch once finalized. |
| class NGLogicalLineItems { |
| public: |
| NGLogicalLineItems() = default; |
| void operator=(NGLogicalLineItems&& other) { |
| children_ = std::move(other.children_); |
| } |
| |
| NGLogicalLineItem& operator[](wtf_size_t i) { return children_[i]; } |
| const NGLogicalLineItem& operator[](wtf_size_t i) const { |
| return children_[i]; |
| } |
| |
| wtf_size_t size() const { return children_.size(); } |
| bool IsEmpty() const { return children_.IsEmpty(); } |
| void ReserveInitialCapacity(unsigned capacity) { |
| children_.ReserveInitialCapacity(capacity); |
| } |
| void Shrink(wtf_size_t size) { children_.Shrink(size); } |
| |
| using iterator = Vector<NGLogicalLineItem, 16>::iterator; |
| iterator begin() { return children_.begin(); } |
| iterator end() { return children_.end(); } |
| using const_iterator = Vector<NGLogicalLineItem, 16>::const_iterator; |
| const_iterator begin() const { return children_.begin(); } |
| const_iterator end() const { return children_.end(); } |
| using reverse_iterator = Vector<NGLogicalLineItem, 16>::reverse_iterator; |
| reverse_iterator rbegin() { return children_.rbegin(); } |
| reverse_iterator rend() { return children_.rend(); } |
| using const_reverse_iterator = |
| Vector<NGLogicalLineItem, 16>::const_reverse_iterator; |
| const_reverse_iterator rbegin() const { return children_.rbegin(); } |
| const_reverse_iterator rend() const { return children_.rend(); } |
| |
| NGLogicalLineItem* FirstInFlowChild(); |
| NGLogicalLineItem* LastInFlowChild(); |
| |
| // Add a child. Accepts all constructor arguments for |NGLogicalLineItem|. |
| template <class... Args> |
| void AddChild(Args&&... args) { |
| children_.emplace_back(std::forward<Args>(args)...); |
| } |
| void InsertChild(unsigned index, NGLogicalLineItem&& item) { |
| WillInsertChild(index); |
| children_.insert(index, item); |
| } |
| void InsertChild(unsigned index, |
| scoped_refptr<const NGLayoutResult> layout_result, |
| const LogicalRect& rect, |
| unsigned children_count) { |
| WillInsertChild(index); |
| children_.insert( |
| index, NGLogicalLineItem(std::move(layout_result), rect, children_count, |
| /* bidi_level */ 0)); |
| } |
| |
| void MoveInInlineDirection(LayoutUnit); |
| void MoveInInlineDirection(LayoutUnit, unsigned start, unsigned end); |
| void MoveInBlockDirection(LayoutUnit); |
| void MoveInBlockDirection(LayoutUnit, unsigned start, unsigned end); |
| |
| private: |
| void WillInsertChild(unsigned index); |
| |
| Vector<NGLogicalLineItem, 16> children_; |
| }; |
| |
| } // namespace blink |
| |
| WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS(blink::NGLogicalLineItem) |
| |
| #endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_LOGICAL_LINE_ITEM_H_ |