OLD | NEW |
---|---|
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "core/layout/ng/inline/ng_inline_layout_algorithm.h" | 5 #include "core/layout/ng/inline/ng_inline_layout_algorithm.h" |
6 | 6 |
7 #include "core/layout/ng/inline/ng_bidi_paragraph.h" | 7 #include "core/layout/ng/inline/ng_bidi_paragraph.h" |
8 #include "core/layout/ng/inline/ng_inline_break_token.h" | 8 #include "core/layout/ng/inline/ng_inline_break_token.h" |
9 #include "core/layout/ng/inline/ng_inline_node.h" | 9 #include "core/layout/ng/inline/ng_inline_node.h" |
10 #include "core/layout/ng/inline/ng_line_box_fragment.h" | 10 #include "core/layout/ng/inline/ng_line_box_fragment.h" |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
54 | 54 |
55 NGInlineLayoutAlgorithm::NGInlineLayoutAlgorithm( | 55 NGInlineLayoutAlgorithm::NGInlineLayoutAlgorithm( |
56 NGInlineNode* inline_node, | 56 NGInlineNode* inline_node, |
57 NGConstraintSpace* space, | 57 NGConstraintSpace* space, |
58 NGInlineBreakToken* break_token) | 58 NGInlineBreakToken* break_token) |
59 : NGLayoutAlgorithm(inline_node, space, break_token), | 59 : NGLayoutAlgorithm(inline_node, space, break_token), |
60 is_horizontal_writing_mode_( | 60 is_horizontal_writing_mode_( |
61 blink::IsHorizontalWritingMode(space->WritingMode())), | 61 blink::IsHorizontalWritingMode(space->WritingMode())), |
62 disallow_first_line_rules_(false), | 62 disallow_first_line_rules_(false), |
63 space_builder_(space) | 63 space_builder_(space) |
64 #if DCHECK_IS_ON() | |
65 , | |
66 is_bidi_reordered_(false) | |
67 #endif | |
68 { | 64 { |
69 container_builder_.MutableUnpositionedFloats() = space->UnpositionedFloats(); | 65 container_builder_.MutableUnpositionedFloats() = space->UnpositionedFloats(); |
70 | 66 |
71 // TODO(crbug.com/716930): We may be an empty LayoutInline due to splitting. | 67 // TODO(crbug.com/716930): We may be an empty LayoutInline due to splitting. |
72 // Only resolve our BFC offset if we know that we are non-empty as we may | 68 // Only resolve our BFC offset if we know that we are non-empty as we may |
73 // need to pass through our margin strut. | 69 // need to pass through our margin strut. |
74 if (!inline_node->Items().IsEmpty()) { | 70 if (!inline_node->Items().IsEmpty()) { |
75 NGLogicalOffset bfc_offset = ConstraintSpace().BfcOffset(); | 71 NGLogicalOffset bfc_offset = ConstraintSpace().BfcOffset(); |
76 bfc_offset.block_offset += ConstraintSpace().MarginStrut().Sum(); | 72 bfc_offset.block_offset += ConstraintSpace().MarginStrut().Sum(); |
77 MaybeUpdateFragmentBfcOffset(ConstraintSpace(), bfc_offset, | 73 MaybeUpdateFragmentBfcOffset(ConstraintSpace(), bfc_offset, |
78 &container_builder_); | 74 &container_builder_); |
79 PositionPendingFloats(bfc_offset.block_offset, &container_builder_, | 75 PositionPendingFloats(bfc_offset.block_offset, &container_builder_, |
80 MutableConstraintSpace()); | 76 MutableConstraintSpace()); |
81 } | 77 } |
82 | 78 |
83 if (!is_horizontal_writing_mode_) | 79 if (!is_horizontal_writing_mode_) |
84 baseline_type_ = FontBaseline::kIdeographicBaseline; | 80 baseline_type_ = FontBaseline::kIdeographicBaseline; |
85 if (break_token) | 81 |
86 Initialize(break_token->ItemIndex(), break_token->TextOffset()); | 82 if (break_token) { |
87 else | 83 // If a break_token is given, we're re-starting layout for 2nd or later |
88 Initialize(0, 0); | 84 // lines, and that the first line we create should not use the first line |
85 // rules. | |
86 DCHECK(!break_token->IsFinished()); | |
87 DCHECK(break_token->TextOffset() || break_token->ItemIndex()); | |
88 disallow_first_line_rules_ = true; | |
89 } else { | |
90 auto& engine = Node()->GetLayoutObject()->GetDocument().GetStyleEngine(); | |
91 disallow_first_line_rules_ = !engine.UsesFirstLineRules(); | |
92 } | |
93 | |
94 FindNextLayoutOpportunity(); | |
89 } | 95 } |
90 | 96 |
91 bool NGInlineLayoutAlgorithm::IsFirstLine() const { | 97 bool NGInlineLayoutAlgorithm::IsFirstLine() const { |
92 return !disallow_first_line_rules_ && container_builder_.Children().IsEmpty(); | 98 return !disallow_first_line_rules_ && container_builder_.Children().IsEmpty(); |
93 } | 99 } |
94 | 100 |
95 const ComputedStyle& NGInlineLayoutAlgorithm::FirstLineStyle() const { | 101 const ComputedStyle& NGInlineLayoutAlgorithm::FirstLineStyle() const { |
96 return Node()->GetLayoutObject()->FirstLineStyleRef(); | 102 return Node()->GetLayoutObject()->FirstLineStyleRef(); |
97 } | 103 } |
98 | 104 |
99 const ComputedStyle& NGInlineLayoutAlgorithm::LineStyle() const { | 105 const ComputedStyle& NGInlineLayoutAlgorithm::LineStyle() const { |
100 return IsFirstLine() ? FirstLineStyle() : Style(); | 106 return IsFirstLine() ? FirstLineStyle() : Style(); |
101 } | 107 } |
102 | 108 |
103 bool NGInlineLayoutAlgorithm::CanFitOnLine() const { | 109 LayoutUnit NGInlineLayoutAlgorithm::AvailableWidth() const { |
104 LayoutUnit available_size = current_opportunity_.InlineSize(); | 110 return current_opportunity_.InlineSize(); |
105 if (available_size == NGSizeIndefinite) | |
106 return true; | |
107 return end_position_ <= available_size; | |
108 } | 111 } |
109 | 112 |
110 bool NGInlineLayoutAlgorithm::HasItems() const { | 113 bool NGInlineLayoutAlgorithm::CreateLine( |
111 return start_offset_ != end_offset_; | 114 NGInlineItemResults* item_results, |
112 } | 115 RefPtr<NGInlineBreakToken> break_token) { |
113 | |
114 bool NGInlineLayoutAlgorithm::HasBreakOpportunity() const { | |
115 return start_offset_ != last_break_opportunity_offset_; | |
116 } | |
117 | |
118 bool NGInlineLayoutAlgorithm::HasItemsAfterLastBreakOpportunity() const { | |
119 return last_break_opportunity_offset_ != end_offset_; | |
120 } | |
121 | |
122 void NGInlineLayoutAlgorithm::Initialize(unsigned index, unsigned offset) { | |
123 if (index || offset) | |
124 Node()->AssertOffset(index, offset); | |
125 | |
126 start_index_ = last_index_ = last_break_opportunity_index_ = index; | |
127 start_offset_ = end_offset_ = last_break_opportunity_offset_ = offset; | |
128 end_position_ = last_break_opportunity_position_ = LayoutUnit(); | |
129 | |
130 auto& engine = Node()->GetLayoutObject()->GetDocument().GetStyleEngine(); | |
131 disallow_first_line_rules_ = index || offset || !engine.UsesFirstLineRules(); | |
132 | |
133 FindNextLayoutOpportunity(); | |
134 } | |
135 | |
136 void NGInlineLayoutAlgorithm::SetEnd(unsigned new_end_offset) { | |
137 DCHECK_GT(new_end_offset, end_offset_); | |
138 const Vector<NGInlineItem>& items = Node()->Items(); | |
139 DCHECK_LE(new_end_offset, items.back().EndOffset()); | |
140 | |
141 // SetEnd() while |new_end_offset| is beyond the current last item. | |
142 unsigned index = last_index_; | |
143 const NGInlineItem* item = &items[index]; | |
144 if (new_end_offset > item->EndOffset()) { | |
145 if (end_offset_ < item->EndOffset()) { | |
146 SetEnd(index, item->EndOffset(), | |
147 InlineSize(*item, end_offset_, item->EndOffset())); | |
148 } | |
149 item = &items[++index]; | |
150 | |
151 while (new_end_offset > item->EndOffset()) { | |
152 SetEnd(index, item->EndOffset(), InlineSize(*item)); | |
153 item = &items[++index]; | |
154 } | |
155 } | |
156 | |
157 SetEnd(index, new_end_offset, InlineSize(*item, end_offset_, new_end_offset)); | |
158 | |
159 // Include closing elements. | |
160 while (new_end_offset == item->EndOffset() && index < items.size() - 1) { | |
161 item = &items[++index]; | |
162 if (item->Type() != NGInlineItem::kCloseTag) | |
163 break; | |
164 SetEnd(index, new_end_offset, InlineSize(*item)); | |
165 } | |
166 } | |
167 | |
168 void NGInlineLayoutAlgorithm::SetEnd(unsigned index, | |
169 unsigned new_end_offset, | |
170 LayoutUnit inline_size_since_current_end) { | |
171 const Vector<NGInlineItem>& items = Node()->Items(); | |
172 DCHECK_LE(new_end_offset, items.back().EndOffset()); | |
173 | |
174 // |new_end_offset| should be in the current item or next. | |
175 // TODO(kojii): Reconsider this restriction if needed. | |
176 DCHECK((index == last_index_ && new_end_offset > end_offset_) || | |
177 (index == last_index_ + 1 && new_end_offset >= end_offset_ && | |
178 end_offset_ == items[last_index_].EndOffset())); | |
179 const NGInlineItem& item = items[index]; | |
180 item.AssertEndOffset(new_end_offset); | |
181 | |
182 if (item.Type() == NGInlineItem::kFloating) { | |
183 LayoutAndPositionFloat( | |
184 LayoutUnit(end_position_) + inline_size_since_current_end, | |
185 item.GetLayoutObject()); | |
186 } | |
187 | |
188 last_index_ = index; | |
189 end_offset_ = new_end_offset; | |
190 end_position_ += inline_size_since_current_end; | |
191 } | |
192 | |
193 void NGInlineLayoutAlgorithm::SetBreakOpportunity() { | |
194 last_break_opportunity_index_ = last_index_; | |
195 last_break_opportunity_offset_ = end_offset_; | |
196 last_break_opportunity_position_ = end_position_; | |
197 } | |
198 | |
199 void NGInlineLayoutAlgorithm::SetStartOfHangables(unsigned offset) { | |
200 // TODO(kojii): Implement. | |
201 } | |
202 | |
203 LayoutUnit NGInlineLayoutAlgorithm::InlineSize(const NGInlineItem& item) { | |
204 if (item.Type() == NGInlineItem::kAtomicInline) | |
205 return InlineSizeFromLayout(item); | |
206 return item.InlineSize(); | |
207 } | |
208 | |
209 LayoutUnit NGInlineLayoutAlgorithm::InlineSize(const NGInlineItem& item, | |
210 unsigned start_offset, | |
211 unsigned end_offset) { | |
212 if (item.StartOffset() == start_offset && item.EndOffset() == end_offset) | |
213 return InlineSize(item); | |
214 return item.InlineSize(start_offset, end_offset); | |
215 } | |
216 | |
217 LayoutUnit NGInlineLayoutAlgorithm::InlineSizeFromLayout( | |
218 const NGInlineItem& item) { | |
219 return NGBoxFragment(ConstraintSpace().WritingMode(), | |
220 ToNGPhysicalBoxFragment( | |
221 LayoutItem(item)->PhysicalFragment().Get())) | |
222 .InlineSize(); | |
223 } | |
224 | |
225 const NGLayoutResult* NGInlineLayoutAlgorithm::LayoutItem( | |
226 const NGInlineItem& item) { | |
227 // Returns the cached NGLayoutResult if available. | |
228 const Vector<NGInlineItem>& items = Node()->Items(); | |
229 if (layout_results_.IsEmpty()) | |
230 layout_results_.resize(items.size()); | |
231 unsigned index = std::distance(items.begin(), &item); | |
232 RefPtr<NGLayoutResult>* layout_result = &layout_results_[index]; | |
233 if (*layout_result) | |
234 return layout_result->Get(); | |
235 | |
236 DCHECK_EQ(item.Type(), NGInlineItem::kAtomicInline); | |
237 NGBlockNode* node = new NGBlockNode(item.GetLayoutObject()); | |
238 // TODO(kojii): Keep node in NGInlineItem. | |
239 const ComputedStyle& style = node->Style(); | |
240 NGConstraintSpaceBuilder constraint_space_builder(&ConstraintSpace()); | |
241 RefPtr<NGConstraintSpace> constraint_space = | |
242 constraint_space_builder.SetIsNewFormattingContext(true) | |
243 .SetIsShrinkToFit(true) | |
244 .SetTextDirection(style.Direction()) | |
245 .ToConstraintSpace(FromPlatformWritingMode(style.GetWritingMode())); | |
246 *layout_result = node->Layout(constraint_space.Get()); | |
247 return layout_result->Get(); | |
248 } | |
249 | |
250 bool NGInlineLayoutAlgorithm::CreateLine() { | |
251 if (HasItemsAfterLastBreakOpportunity()) | |
252 SetBreakOpportunity(); | |
253 return CreateLineUpToLastBreakOpportunity(); | |
254 } | |
255 | |
256 bool NGInlineLayoutAlgorithm::CreateLineUpToLastBreakOpportunity() { | |
257 const Vector<NGInlineItem>& items = Node()->Items(); | 116 const Vector<NGInlineItem>& items = Node()->Items(); |
258 | 117 |
259 // Create a list of LineItemChunk from |start| and |last_break_opportunity|. | 118 // TODO(crbug.com/716930): We may be an empty LayoutInline due to splitting. |
260 // TODO(kojii): Consider refactoring LineItemChunk once NGLineBuilder's public | 119 // Only resolve our BFC offset if we know that we are non-empty as we may |
261 // API is more finalized. It does not fit well with the current API. | 120 // need to pass through our margin strut. |
262 Vector<LineItemChunk, 32> line_item_chunks; | 121 if (!items.IsEmpty()) { |
263 unsigned start_offset = start_offset_; | 122 NGLogicalOffset bfc_offset = ConstraintSpace().BfcOffset(); |
264 for (unsigned i = start_index_; i <= last_break_opportunity_index_; i++) { | 123 bfc_offset.block_offset += ConstraintSpace().MarginStrut().Sum(); |
265 const NGInlineItem& item = items[i]; | 124 MaybeUpdateFragmentBfcOffset(ConstraintSpace(), bfc_offset, |
266 unsigned end_offset = | 125 &container_builder_); |
267 std::min(item.EndOffset(), last_break_opportunity_offset_); | 126 PositionPendingFloats(bfc_offset.block_offset, &container_builder_, |
268 line_item_chunks.push_back( | 127 MutableConstraintSpace()); |
269 LineItemChunk{i, start_offset, end_offset, | |
270 InlineSize(item, start_offset, end_offset)}); | |
271 start_offset = end_offset; | |
272 } | 128 } |
273 | 129 |
274 if (Node()->IsBidiEnabled()) | 130 if (Node()->IsBidiEnabled()) |
275 BidiReorder(&line_item_chunks); | 131 BidiReorder(item_results); |
276 | 132 |
277 if (!PlaceItems(line_item_chunks)) | 133 if (!PlaceItems(item_results, break_token)) |
278 return false; | 134 return false; |
279 | 135 |
280 // Prepare for the next line. | 136 // Prepare for the next line. |
281 // Move |start| to |last_break_opportunity|, keeping items after | |
282 // |last_break_opportunity|. | |
283 start_index_ = last_break_opportunity_index_; | |
284 start_offset_ = last_break_opportunity_offset_; | |
285 // If the offset is at the end of the item, move to the next item. | |
286 if (start_offset_ == items[start_index_].EndOffset() && | |
287 start_index_ < items.size() - 1) { | |
288 start_index_++; | |
289 } | |
290 DCHECK_GE(end_position_, last_break_opportunity_position_); | |
291 end_position_ -= last_break_opportunity_position_; | |
292 last_break_opportunity_position_ = LayoutUnit(); | |
293 #if DCHECK_IS_ON() | |
294 is_bidi_reordered_ = false; | |
295 #endif | |
296 | |
297 NGLogicalOffset origin_point = | 137 NGLogicalOffset origin_point = |
298 GetOriginPointForFloats(ConstraintSpace(), content_size_); | 138 GetOriginPointForFloats(ConstraintSpace(), content_size_); |
299 PositionPendingFloats(origin_point.block_offset, &container_builder_, | 139 PositionPendingFloats(origin_point.block_offset, &container_builder_, |
300 MutableConstraintSpace()); | 140 MutableConstraintSpace()); |
301 FindNextLayoutOpportunity(); | 141 FindNextLayoutOpportunity(); |
302 return true; | 142 return true; |
303 } | 143 } |
304 | 144 |
305 void NGInlineLayoutAlgorithm::BidiReorder( | 145 void NGInlineLayoutAlgorithm::BidiReorder(NGInlineItemResults* line_items) { |
306 Vector<LineItemChunk, 32>* line_item_chunks) { | |
307 #if DCHECK_IS_ON() | |
308 DCHECK(!is_bidi_reordered_); | |
309 is_bidi_reordered_ = true; | |
310 #endif | |
311 | |
312 // TODO(kojii): UAX#9 L1 is not supported yet. Supporting L1 may change | 146 // TODO(kojii): UAX#9 L1 is not supported yet. Supporting L1 may change |
313 // embedding levels of parts of runs, which requires to split items. | 147 // embedding levels of parts of runs, which requires to split items. |
314 // http://unicode.org/reports/tr9/#L1 | 148 // http://unicode.org/reports/tr9/#L1 |
315 // BidiResolver does not support L1 crbug.com/316409. | 149 // BidiResolver does not support L1 crbug.com/316409. |
316 | 150 |
317 // Create a list of chunk indices in the visual order. | 151 // Create a list of chunk indices in the visual order. |
318 // ICU |ubidi_getVisualMap()| works for a run of characters. Since we can | 152 // ICU |ubidi_getVisualMap()| works for a run of characters. Since we can |
319 // handle the direction of each run, we use |ubidi_reorderVisual()| to reorder | 153 // handle the direction of each run, we use |ubidi_reorderVisual()| to reorder |
320 // runs instead of characters. | 154 // runs instead of characters. |
321 Vector<UBiDiLevel, 32> levels; | 155 Vector<UBiDiLevel, 32> levels; |
322 levels.ReserveInitialCapacity(line_item_chunks->size()); | 156 levels.ReserveInitialCapacity(line_items->size()); |
323 const Vector<NGInlineItem>& items = Node()->Items(); | 157 const Vector<NGInlineItem>& items = Node()->Items(); |
324 for (const auto& chunk : *line_item_chunks) | 158 for (const auto& item_result : *line_items) |
325 levels.push_back(items[chunk.index].BidiLevel()); | 159 levels.push_back(items[item_result.item_index].BidiLevel()); |
326 Vector<int32_t, 32> indices_in_visual_order(line_item_chunks->size()); | 160 Vector<int32_t, 32> indices_in_visual_order(line_items->size()); |
327 NGBidiParagraph::IndicesInVisualOrder(levels, &indices_in_visual_order); | 161 NGBidiParagraph::IndicesInVisualOrder(levels, &indices_in_visual_order); |
328 | 162 |
329 // Reorder |line_item_chunks| in visual order. | 163 // Reorder to the visual order. |
330 Vector<LineItemChunk, 32> line_item_chunks_in_visual_order( | 164 NGInlineItemResults line_items_in_visual_order(line_items->size()); |
331 line_item_chunks->size()); | |
332 for (unsigned visual_index = 0; visual_index < indices_in_visual_order.size(); | 165 for (unsigned visual_index = 0; visual_index < indices_in_visual_order.size(); |
333 visual_index++) { | 166 visual_index++) { |
334 unsigned logical_index = indices_in_visual_order[visual_index]; | 167 unsigned logical_index = indices_in_visual_order[visual_index]; |
335 line_item_chunks_in_visual_order[visual_index] = | 168 line_items_in_visual_order[visual_index] = |
336 (*line_item_chunks)[logical_index]; | 169 std::move((*line_items)[logical_index]); |
337 } | 170 } |
338 | 171 |
339 // Keep Open before Close in the visual order. | 172 // Keep Open before Close in the visual order. |
340 HashMap<LayoutObject*, unsigned> first_index; | 173 HashMap<LayoutObject*, unsigned> first_index; |
341 for (unsigned i = 0; i < line_item_chunks_in_visual_order.size(); i++) { | 174 for (unsigned i = 0; i < line_items_in_visual_order.size(); i++) { |
342 LineItemChunk& chunk = line_item_chunks_in_visual_order[i]; | 175 NGInlineItemResult& item_result = line_items_in_visual_order[i]; |
343 const NGInlineItem& item = items[chunk.index]; | 176 const NGInlineItem& item = items[item_result.item_index]; |
344 if (item.Type() != NGInlineItem::kOpenTag && | 177 if (item.Type() != NGInlineItem::kOpenTag && |
345 item.Type() != NGInlineItem::kCloseTag) { | 178 item.Type() != NGInlineItem::kCloseTag) { |
346 continue; | 179 continue; |
347 } | 180 } |
348 auto result = first_index.insert(item.GetLayoutObject(), i); | 181 auto result = first_index.insert(item.GetLayoutObject(), i); |
349 if (!result.is_new_entry && item.Type() == NGInlineItem::kOpenTag) { | 182 if (!result.is_new_entry && item.Type() == NGInlineItem::kOpenTag) { |
350 std::swap(line_item_chunks_in_visual_order[i], | 183 std::swap(line_items_in_visual_order[i], |
351 line_item_chunks_in_visual_order[result.stored_value->value]); | 184 line_items_in_visual_order[result.stored_value->value]); |
352 } | 185 } |
353 } | 186 } |
354 | 187 |
355 line_item_chunks->swap(line_item_chunks_in_visual_order); | 188 line_items->swap(line_items_in_visual_order); |
356 } | 189 } |
357 | 190 |
358 // TODO(glebl): Add the support of clearance for inline floats. | 191 // TODO(glebl): Add the support of clearance for inline floats. |
359 void NGInlineLayoutAlgorithm::LayoutAndPositionFloat( | 192 void NGInlineLayoutAlgorithm::LayoutAndPositionFloat( |
360 LayoutUnit end_position, | 193 LayoutUnit end_position, |
361 LayoutObject* layout_object) { | 194 LayoutObject* layout_object) { |
362 NGBlockNode* node = new NGBlockNode(layout_object); | 195 NGBlockNode* node = new NGBlockNode(layout_object); |
363 | 196 |
364 RefPtr<NGConstraintSpace> float_space = | 197 RefPtr<NGConstraintSpace> float_space = |
365 CreateConstraintSpaceForFloat(Node()->Style(), *node, &space_builder_); | 198 CreateConstraintSpaceForFloat(Node()->Style(), *node, &space_builder_); |
(...skipping 28 matching lines...) Expand all Loading... | |
394 float_does_not_fit) { | 227 float_does_not_fit) { |
395 container_builder_.AddUnpositionedFloat(floating_object); | 228 container_builder_.AddUnpositionedFloat(floating_object); |
396 } else { | 229 } else { |
397 container_builder_.MutablePositionedFloats().push_back( | 230 container_builder_.MutablePositionedFloats().push_back( |
398 PositionFloat(floating_object.Get(), MutableConstraintSpace())); | 231 PositionFloat(floating_object.Get(), MutableConstraintSpace())); |
399 FindNextLayoutOpportunity(); | 232 FindNextLayoutOpportunity(); |
400 } | 233 } |
401 } | 234 } |
402 | 235 |
403 bool NGInlineLayoutAlgorithm::PlaceItems( | 236 bool NGInlineLayoutAlgorithm::PlaceItems( |
404 const Vector<LineItemChunk, 32>& line_item_chunks) { | 237 NGInlineItemResults* line_items, |
238 RefPtr<NGInlineBreakToken> break_token) { | |
405 const Vector<NGInlineItem>& items = Node()->Items(); | 239 const Vector<NGInlineItem>& items = Node()->Items(); |
406 | 240 |
407 const ComputedStyle& line_style = LineStyle(); | 241 const ComputedStyle& line_style = LineStyle(); |
408 NGLineHeightMetrics line_metrics(line_style, baseline_type_); | 242 NGLineHeightMetrics line_metrics(line_style, baseline_type_); |
409 NGLineHeightMetrics line_metrics_with_leading = line_metrics; | 243 NGLineHeightMetrics line_metrics_with_leading = line_metrics; |
410 line_metrics_with_leading.AddLeading(line_style.ComputedLineHeightAsFixed()); | 244 line_metrics_with_leading.AddLeading(line_style.ComputedLineHeightAsFixed()); |
411 NGLineBoxFragmentBuilder line_box(Node()); | 245 NGLineBoxFragmentBuilder line_box(Node()); |
412 | 246 |
413 // Compute heights of all inline items by placing the dominant baseline at 0. | 247 // Compute heights of all inline items by placing the dominant baseline at 0. |
414 // The baseline is adjusted after the height of the line box is computed. | 248 // The baseline is adjusted after the height of the line box is computed. |
415 NGTextFragmentBuilder text_builder(Node()); | 249 NGTextFragmentBuilder text_builder(Node()); |
416 NGInlineBoxState* box = | 250 NGInlineBoxState* box = |
417 box_states_.OnBeginPlaceItems(&LineStyle(), baseline_type_); | 251 box_states_.OnBeginPlaceItems(&LineStyle(), baseline_type_); |
418 LayoutUnit inline_size; | 252 LayoutUnit inline_size; |
419 for (const auto& line_item_chunk : line_item_chunks) { | 253 for (auto& item_result : *line_items) { |
420 const NGInlineItem& item = items[line_item_chunk.index]; | 254 const NGInlineItem& item = items[item_result.item_index]; |
421 LayoutUnit line_top; | 255 LayoutUnit line_top; |
422 if (item.Type() == NGInlineItem::kText) { | 256 if (item.Type() == NGInlineItem::kText) { |
423 DCHECK(item.GetLayoutObject()->IsText()); | 257 DCHECK(item.GetLayoutObject()->IsText()); |
424 DCHECK(!box->text_metrics.IsEmpty()); | 258 DCHECK(!box->text_metrics.IsEmpty()); |
425 line_top = box->text_top; | 259 line_top = box->text_top; |
426 text_builder.SetSize( | 260 text_builder.SetSize( |
427 {line_item_chunk.inline_size, box->text_metrics.LineHeight()}); | 261 {item_result.inline_size, box->text_metrics.LineHeight()}); |
428 // Take all used fonts into account if 'line-height: normal'. | 262 // Take all used fonts into account if 'line-height: normal'. |
429 if (box->include_used_fonts) { | 263 if (box->include_used_fonts) { |
430 box->AccumulateUsedFonts(item, line_item_chunk.start_offset, | 264 box->AccumulateUsedFonts(item, item_result.start_offset, |
431 line_item_chunk.end_offset, baseline_type_); | 265 item_result.end_offset, baseline_type_); |
432 } | 266 } |
433 } else if (item.Type() == NGInlineItem::kOpenTag) { | 267 } else if (item.Type() == NGInlineItem::kOpenTag) { |
434 box = box_states_.OnOpenTag(item, &line_box, &text_builder); | 268 box = box_states_.OnOpenTag(item, &line_box, &text_builder); |
435 // Compute text metrics for all inline boxes since even empty inlines | 269 // Compute text metrics for all inline boxes since even empty inlines |
436 // influence the line height. | 270 // influence the line height. |
437 // https://drafts.csswg.org/css2/visudet.html#line-height | 271 // https://drafts.csswg.org/css2/visudet.html#line-height |
438 // TODO(kojii): Review if atomic inline level should have open/close. | 272 // TODO(kojii): Review if atomic inline level should have open/close. |
439 if (!item.GetLayoutObject()->IsAtomicInlineLevel()) | 273 if (!item.GetLayoutObject()->IsAtomicInlineLevel()) |
440 box->ComputeTextMetrics(*item.Style(), baseline_type_); | 274 box->ComputeTextMetrics(*item.Style(), baseline_type_); |
441 continue; | 275 continue; |
442 } else if (item.Type() == NGInlineItem::kCloseTag) { | 276 } else if (item.Type() == NGInlineItem::kCloseTag) { |
443 box = box_states_.OnCloseTag(item, &line_box, box); | 277 box = box_states_.OnCloseTag(item, &line_box, box); |
444 continue; | 278 continue; |
445 } else if (item.Type() == NGInlineItem::kAtomicInline) { | 279 } else if (item.Type() == NGInlineItem::kAtomicInline) { |
446 line_top = PlaceAtomicInline(item, &line_box, box, &text_builder); | 280 line_top = |
281 PlaceAtomicInline(item, &item_result, &line_box, box, &text_builder); | |
447 } else if (item.Type() == NGInlineItem::kOutOfFlowPositioned) { | 282 } else if (item.Type() == NGInlineItem::kOutOfFlowPositioned) { |
448 // TODO(layout-dev): Report the correct static position for the out of | 283 // TODO(layout-dev): Report the correct static position for the out of |
449 // flow descendant. We can't do this here yet as it doesn't know the | 284 // flow descendant. We can't do this here yet as it doesn't know the |
450 // size of the line box. | 285 // size of the line box. |
451 container_builder_.AddOutOfFlowDescendant( | 286 container_builder_.AddOutOfFlowDescendant( |
452 // Absolute positioning blockifies the box's display type. | 287 // Absolute positioning blockifies the box's display type. |
453 // https://drafts.csswg.org/css-display/#transformations | 288 // https://drafts.csswg.org/css-display/#transformations |
454 new NGBlockNode(item.GetLayoutObject()), | 289 new NGBlockNode(item.GetLayoutObject()), |
455 NGStaticPosition::Create(ConstraintSpace().WritingMode(), | 290 NGStaticPosition::Create(ConstraintSpace().WritingMode(), |
456 ConstraintSpace().Direction(), | 291 ConstraintSpace().Direction(), |
457 NGPhysicalOffset())); | 292 NGPhysicalOffset())); |
458 continue; | 293 continue; |
459 } else { | 294 } else { |
460 continue; | 295 continue; |
461 } | 296 } |
462 | 297 |
463 RefPtr<NGPhysicalTextFragment> text_fragment = text_builder.ToTextFragment( | 298 RefPtr<NGPhysicalTextFragment> text_fragment = text_builder.ToTextFragment( |
464 line_item_chunk.index, line_item_chunk.start_offset, | 299 item_result.item_index, item_result.start_offset, |
465 line_item_chunk.end_offset); | 300 item_result.end_offset); |
466 | 301 |
467 NGLogicalOffset logical_offset( | 302 NGLogicalOffset logical_offset( |
468 inline_size + current_opportunity_.InlineStartOffset() - | 303 inline_size + current_opportunity_.InlineStartOffset() - |
469 ConstraintSpace().BfcOffset().inline_offset, | 304 ConstraintSpace().BfcOffset().inline_offset, |
470 line_top); | 305 line_top); |
471 line_box.AddChild(std::move(text_fragment), logical_offset); | 306 line_box.AddChild(std::move(text_fragment), logical_offset); |
472 inline_size += line_item_chunk.inline_size; | 307 inline_size += item_result.inline_size; |
473 } | 308 } |
474 | 309 |
475 if (line_box.Children().IsEmpty()) { | 310 if (line_box.Children().IsEmpty()) { |
476 return true; // The line was empty. | 311 return true; // The line was empty. |
477 } | 312 } |
478 | 313 |
479 box_states_.OnEndPlaceItems(&line_box); | 314 box_states_.OnEndPlaceItems(&line_box); |
480 | 315 |
481 // The baselines are always placed at pixel boundaries. Not doing so results | 316 // The baselines are always placed at pixel boundaries. Not doing so results |
482 // in incorrect layout of text decorations, most notably underlines. | 317 // in incorrect layout of text decorations, most notably underlines. |
483 LayoutUnit baseline = content_size_ + line_box.Metrics().ascent; | 318 LayoutUnit baseline = content_size_ + line_box.Metrics().ascent; |
484 baseline = LayoutUnit(baseline.Round()); | 319 baseline = LayoutUnit(baseline.Round()); |
485 | 320 |
486 // Check if the line fits into the constraint space in block direction. | 321 // Check if the line fits into the constraint space in block direction. |
487 LayoutUnit line_bottom = baseline + line_box.Metrics().descent; | 322 LayoutUnit line_bottom = baseline + line_box.Metrics().descent; |
488 | 323 |
489 if (!container_builder_.Children().IsEmpty() && | 324 if (!container_builder_.Children().IsEmpty() && |
490 ConstraintSpace().AvailableSize().block_size != NGSizeIndefinite && | 325 ConstraintSpace().AvailableSize().block_size != NGSizeIndefinite && |
491 line_bottom > ConstraintSpace().AvailableSize().block_size) { | 326 line_bottom > ConstraintSpace().AvailableSize().block_size) { |
492 return false; | 327 return false; |
493 } | 328 } |
494 | 329 |
495 // If there are more content to consume, create an unfinished break token. | 330 line_box.SetBreakToken(std::move(break_token)); |
496 if (last_break_opportunity_index_ != items.size() - 1 || | |
497 last_break_opportunity_offset_ != Node()->Text().length()) { | |
498 line_box.SetBreakToken(NGInlineBreakToken::Create( | |
499 Node(), last_break_opportunity_index_, last_break_opportunity_offset_)); | |
500 } | |
501 | 331 |
502 // TODO(kojii): Implement flipped line (vertical-lr). In this case, line_top | 332 // TODO(kojii): Implement flipped line (vertical-lr). In this case, line_top |
503 // and block_start do not match. | 333 // and block_start do not match. |
504 | 334 |
505 // Up until this point, children are placed so that the dominant baseline is | 335 // Up until this point, children are placed so that the dominant baseline is |
506 // at 0. Move them to the final baseline position, and set the logical top of | 336 // at 0. Move them to the final baseline position, and set the logical top of |
507 // the line box to the line top. | 337 // the line box to the line top. |
508 line_box.MoveChildrenInBlockDirection(baseline); | 338 line_box.MoveChildrenInBlockDirection(baseline); |
509 line_box.SetInlineSize(inline_size); | 339 line_box.SetInlineSize(inline_size); |
510 container_builder_.AddChild( | 340 container_builder_.AddChild( |
511 line_box.ToLineBoxFragment(), | 341 line_box.ToLineBoxFragment(), |
512 {LayoutUnit(), baseline + box_states_.LineBoxState().text_top}); | 342 {LayoutUnit(), baseline + box_states_.LineBoxState().text_top}); |
513 | 343 |
514 max_inline_size_ = std::max(max_inline_size_, inline_size); | 344 max_inline_size_ = std::max(max_inline_size_, inline_size); |
515 content_size_ = line_bottom; | 345 content_size_ = line_bottom; |
516 return true; | 346 return true; |
517 } | 347 } |
518 | 348 |
349 // TODO(kojii): Currently, this function does not change item_result, but | |
350 // when NG paint is enabled, this will std::move() the LayoutResult. | |
519 LayoutUnit NGInlineLayoutAlgorithm::PlaceAtomicInline( | 351 LayoutUnit NGInlineLayoutAlgorithm::PlaceAtomicInline( |
520 const NGInlineItem& item, | 352 const NGInlineItem& item, |
353 NGInlineItemResult* item_result, | |
521 NGLineBoxFragmentBuilder* line_box, | 354 NGLineBoxFragmentBuilder* line_box, |
522 NGInlineBoxState* state, | 355 NGInlineBoxState* state, |
523 NGTextFragmentBuilder* text_builder) { | 356 NGTextFragmentBuilder* text_builder) { |
357 DCHECK(item_result->layout_result); | |
524 NGBoxFragment fragment( | 358 NGBoxFragment fragment( |
525 ConstraintSpace().WritingMode(), | 359 ConstraintSpace().WritingMode(), |
526 ToNGPhysicalBoxFragment(LayoutItem(item)->PhysicalFragment().Get())); | 360 ToNGPhysicalBoxFragment( |
361 item_result->layout_result->PhysicalFragment().Get())); | |
527 // TODO(kojii): Margin and border in block progression not implemented yet. | 362 // TODO(kojii): Margin and border in block progression not implemented yet. |
528 LayoutUnit block_size = fragment.BlockSize(); | 363 LayoutUnit block_size = fragment.BlockSize(); |
529 | 364 |
530 // TODO(kojii): Try to eliminate the wrapping text fragment and use the | 365 // TODO(kojii): Try to eliminate the wrapping text fragment and use the |
531 // |fragment| directly. Currently |CopyFragmentDataToLayoutBlockFlow| | 366 // |fragment| directly. Currently |CopyFragmentDataToLayoutBlockFlow| |
532 // requires a text fragment. | 367 // requires a text fragment. |
533 text_builder->SetSize({fragment.InlineSize(), block_size}); | 368 text_builder->SetSize({fragment.InlineSize(), block_size}); |
534 | 369 |
535 // TODO(kojii): Add baseline position to NGPhysicalFragment. | 370 // TODO(kojii): Add baseline position to NGPhysicalFragment. |
536 LayoutBox* layout_box = ToLayoutBox(item.GetLayoutObject()); | 371 LayoutBox* layout_box = ToLayoutBox(item.GetLayoutObject()); |
(...skipping 17 matching lines...) Expand all Loading... | |
554 if (container_builder_.BfcOffset()) | 389 if (container_builder_.BfcOffset()) |
555 iter_offset = ContainerBfcOffset(); | 390 iter_offset = ContainerBfcOffset(); |
556 iter_offset.block_offset += content_size_; | 391 iter_offset.block_offset += content_size_; |
557 auto* iter = MutableConstraintSpace()->LayoutOpportunityIterator(iter_offset); | 392 auto* iter = MutableConstraintSpace()->LayoutOpportunityIterator(iter_offset); |
558 NGLayoutOpportunity opportunity = iter->Next(); | 393 NGLayoutOpportunity opportunity = iter->Next(); |
559 if (!opportunity.IsEmpty()) | 394 if (!opportunity.IsEmpty()) |
560 current_opportunity_ = opportunity; | 395 current_opportunity_ = opportunity; |
561 } | 396 } |
562 | 397 |
563 RefPtr<NGLayoutResult> NGInlineLayoutAlgorithm::Layout() { | 398 RefPtr<NGLayoutResult> NGInlineLayoutAlgorithm::Layout() { |
564 // TODO(koji): The relationship of NGInlineLayoutAlgorithm and NGLineBreaker | 399 NGLineBreaker line_breaker(Node(), constraint_space_, BreakToken()); |
565 // should be inverted. | 400 NGInlineItemResults item_results; |
566 if (!Node()->Text().IsEmpty()) { | 401 while (true) { |
567 // TODO(eae): Does this need the LayoutText::LocaleForLineBreakIterator | 402 line_breaker.NextLine(&item_results, this); |
ikilpatrick
2017/05/11 15:55:55
So it'd be nice if we didn't have the shaper calli
| |
568 // logic to switch the locale based on breaking mode? | 403 if (item_results.IsEmpty()) |
569 NGLineBreaker line_breaker(Node()->Style().Locale()); | 404 break; |
570 line_breaker.BreakLines(this, Node()->Text(), start_offset_); | 405 CreateLine(&item_results, line_breaker.CreateBreakToken()); |
406 item_results.clear(); | |
571 } | 407 } |
572 | 408 |
573 // TODO(kojii): Check if the line box width should be content or available. | 409 // TODO(kojii): Check if the line box width should be content or available. |
574 NGLogicalSize size(max_inline_size_, content_size_); | 410 NGLogicalSize size(max_inline_size_, content_size_); |
575 container_builder_.SetSize(size).SetOverflowSize(size); | 411 container_builder_.SetSize(size).SetOverflowSize(size); |
576 | 412 |
577 // TODO(crbug.com/716930): We may be an empty LayoutInline due to splitting. | 413 // TODO(crbug.com/716930): We may be an empty LayoutInline due to splitting. |
578 // Margin struts shouldn't need to be passed through like this once we've | 414 // Margin struts shouldn't need to be passed through like this once we've |
579 // removed LayoutInline splitting. | 415 // removed LayoutInline splitting. |
580 if (!container_builder_.BfcOffset()) { | 416 if (!container_builder_.BfcOffset()) { |
581 container_builder_.SetEndMarginStrut(ConstraintSpace().MarginStrut()); | 417 container_builder_.SetEndMarginStrut(ConstraintSpace().MarginStrut()); |
582 } | 418 } |
583 | 419 |
584 return container_builder_.ToBoxFragment(); | 420 return container_builder_.ToBoxFragment(); |
585 } | 421 } |
586 | 422 |
587 MinMaxContentSize NGInlineLayoutAlgorithm::ComputeMinMaxContentSizeByLayout() { | |
588 DCHECK_EQ(ConstraintSpace().AvailableSize().inline_size, LayoutUnit()); | |
589 DCHECK_EQ(ConstraintSpace().AvailableSize().block_size, NGSizeIndefinite); | |
590 if (!Node()->Text().IsEmpty()) { | |
591 NGLineBreaker line_breaker(Node()->Style().Locale()); | |
592 line_breaker.BreakLines(this, Node()->Text(), start_offset_); | |
593 } | |
594 MinMaxContentSize sizes; | |
595 sizes.min_content = MaxInlineSize(); | |
596 | |
597 // max-content is the width without any line wrapping. | |
598 // TODO(kojii): Implement hard breaks (<br> etc.) to break. | |
599 for (const auto& item : Node()->Items()) | |
600 sizes.max_content += InlineSize(item); | |
601 | |
602 return sizes; | |
603 } | |
604 | |
605 } // namespace blink | 423 } // namespace blink |
OLD | NEW |