Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(156)

Side by Side Diff: third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc

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

Powered by Google App Engine
This is Rietveld 408576698