1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_FRAGMENT_ITEMS_BUILDER_H_
6 #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_FRAGMENT_ITEMS_BUILDER_H_
7 
8 #include "third_party/blink/renderer/core/core_export.h"
9 #include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h"
10 #include "third_party/blink/renderer/core/layout/ng/inline/ng_logical_line_item.h"
11 #include "third_party/blink/renderer/platform/text/writing_direction_mode.h"
12 
13 namespace blink {
14 
15 class NGFragmentItem;
16 class NGFragmentItems;
17 class NGInlineNode;
18 
19 // This class builds |NGFragmentItems|.
20 //
21 // Once |NGFragmentItems| is built, it is immutable.
22 class CORE_EXPORT NGFragmentItemsBuilder {
23   STACK_ALLOCATED();
24 
25  public:
26   explicit NGFragmentItemsBuilder(WritingDirectionMode writing_direction);
27   NGFragmentItemsBuilder(const NGInlineNode& node,
28                          WritingDirectionMode writing_direction);
29   ~NGFragmentItemsBuilder();
30 
GetWritingDirection()31   WritingDirectionMode GetWritingDirection() const {
32     return writing_direction_;
33   }
GetWritingMode()34   WritingMode GetWritingMode() const {
35     return writing_direction_.GetWritingMode();
36   }
Direction()37   TextDirection Direction() const { return writing_direction_.Direction(); }
38 
Size()39   wtf_size_t Size() const { return items_.size(); }
40 
41   // Returns true if we have any floating descendants which need to be
42   // traversed during the float paint phase.
HasFloatingDescendantsForPaint()43   bool HasFloatingDescendantsForPaint() const {
44     return has_floating_descendants_for_paint_;
45   }
46 
TextContent(bool first_line)47   const String& TextContent(bool first_line) const {
48     return UNLIKELY(first_line && first_line_text_content_)
49                ? first_line_text_content_
50                : text_content_;
51   }
52 
53   // Adding a line is a three-pass operation, because |NGInlineLayoutAlgorithm|
54   // creates and positions children within a line box, but its parent algorithm
55   // positions the line box.
56   //
57   // 1. |AcquireLogicalLineItems| to get an instance of |NGLogicalLineItems|.
58   // 2. Add items to |NGLogicalLineItems| and create |NGPhysicalFragment|,
59   //    then associate them by |AssociateLogicalLineItems|.
60   // 3. |AddLine| adds the |NGPhysicalLineBoxFragment|.
61   //
62   // |NGBlockLayoutAlgorithm| runs these phases in the order for each line. In
63   // this case, one instance of |NGLogicalLineItems| is reused for all lines to
64   // reduce memory allocations.
65   //
66   // Custom layout produces all line boxes first by running only 1 and 2 (in
67   // |NGInlineLayoutAlgorithm|). Then after worklet determined the position and
68   // the order of line boxes, it runs 3 for each line. In this case,
69   // |NGFragmentItemsBuilder| allocates new instance for each line, and keeps
70   // them alive until |AddLine|.
71   NGLogicalLineItems* AcquireLogicalLineItems();
72   void AssociateLogicalLineItems(NGLogicalLineItems* line_items,
73                                  const NGPhysicalFragment& line_fragment);
74   void AddLine(const NGPhysicalLineBoxFragment& line,
75                const LogicalOffset& offset);
76 
77   // Add to |NGLogicalLineItems| instance pool. |AcquireLogicalLineItems|
78   // uses pooled instances first if available to avoid memory allocations.
79   void AddLogicalLineItemsPool(NGLogicalLineItems* line_items);
80 
81   // Add a list marker to the current line.
82   void AddListMarker(const NGPhysicalBoxFragment& marker_fragment,
83                      const LogicalOffset& offset);
84 
85   // See |AddPreviousItems| below.
86   struct AddPreviousItemsResult {
87     STACK_ALLOCATED();
88 
89    public:
90     const NGInlineBreakToken* inline_break_token = nullptr;
91     LayoutUnit used_block_size;
92     wtf_size_t line_count = 0;
93     bool succeeded = false;
94   };
95 
96   // Add previously laid out |NGFragmentItems|.
97   //
98   // When |stop_at_dirty| is true, this function checks reusability of previous
99   // items and stops copying before the first dirty line.
100   AddPreviousItemsResult AddPreviousItems(
101       const NGPhysicalBoxFragment& container,
102       const NGFragmentItems& items,
103       NGBoxFragmentBuilder* container_builder = nullptr,
104       const NGFragmentItem* end_item = nullptr,
105       wtf_size_t max_lines = 0);
106 
107   struct ItemWithOffset {
108     DISALLOW_NEW();
109 
110    public:
111     template <class... Args>
ItemWithOffsetItemWithOffset112     explicit ItemWithOffset(const LogicalOffset& offset, Args&&... args)
113         : item(std::forward<Args>(args)...), offset(offset) {}
114 
115     const NGFragmentItem& operator*() const { return item; }
116     const NGFragmentItem* operator->() const { return &item; }
117 
118     NGFragmentItem item;
119     LogicalOffset offset;
120   };
121 
122   // Give an inline size, the allocation of this vector is hot. "128" is
123   // heuristic. Usually 10-40, some wikipedia pages have >64 items.
124   using ItemWithOffsetList = Vector<ItemWithOffset, 128>;
125 
126   // Find |LogicalOffset| of the first |NGFragmentItem| for |LayoutObject|.
127   base::Optional<LogicalOffset> LogicalOffsetFor(const LayoutObject&) const;
128 
129   // Moves all the |NGFragmentItem|s by |offset| in the block-direction.
130   void MoveChildrenInBlockDirection(LayoutUnit offset);
131 
132   // Converts the |NGFragmentItem| vector to the physical coordinate space and
133   // returns the result. This should only be used for determining the inline
134   // containing block geometry for OOF-positioned nodes.
135   //
136   // Once this method has been called, new items cannot be added.
137   const ItemWithOffsetList& Items(const PhysicalSize& outer_size);
138 
139   // Build a |NGFragmentItems|. The builder cannot build twice because data set
140   // to this builder may be cleared.
141   void ToFragmentItems(const PhysicalSize& outer_size, void* data);
142 
143  private:
144   void ReleaseCurrentLogicalLineItems();
145   void MoveCurrentLogicalLineItemsToMap();
146 
147   void AddItems(NGLogicalLineItem* child_begin, NGLogicalLineItem* child_end);
148 
149   void ConvertToPhysical(const PhysicalSize& outer_size);
150 
151   ItemWithOffsetList items_;
152   String text_content_;
153   String first_line_text_content_;
154 
155   // Keeps children of a line until the offset is determined. See |AddLine|.
156   NGLogicalLineItems* current_line_items_ = nullptr;
157   const NGPhysicalFragment* current_line_fragment_ = nullptr;
158 
159   HashMap<const NGPhysicalFragment*, NGLogicalLineItems*> line_items_map_;
160   NGLogicalLineItems* line_items_pool_ = nullptr;
161 
162   NGInlineNode node_;
163 
164   WritingDirectionMode writing_direction_;
165 
166   bool has_floating_descendants_for_paint_ = false;
167   bool is_converted_to_physical_ = false;
168   bool is_line_items_pool_acquired_ = false;
169 
170   friend class NGFragmentItems;
171 };
172 
173 }  // namespace blink
174 
175 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_FRAGMENT_ITEMS_BUILDER_H_
176