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

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: WIP, eae review 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 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698