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

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 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 && (break_token->TextOffset() || break_token->ItemIndex())) {
eae 2017/05/10 21:31:38 Why does this disable first line rules?
kojii 2017/05/11 16:17:36 Comments added and code clarified better.
87 else 83 disallow_first_line_rules_ = true;
88 Initialize(0, 0); 84 } else {
85 auto& engine = Node()->GetLayoutObject()->GetDocument().GetStyleEngine();
86 disallow_first_line_rules_ = !engine.UsesFirstLineRules();
87 }
88
89 FindNextLayoutOpportunity();
89 } 90 }
90 91
91 bool NGInlineLayoutAlgorithm::IsFirstLine() const { 92 bool NGInlineLayoutAlgorithm::IsFirstLine() const {
92 return !disallow_first_line_rules_ && container_builder_.Children().IsEmpty(); 93 return !disallow_first_line_rules_ && container_builder_.Children().IsEmpty();
93 } 94 }
94 95
95 const ComputedStyle& NGInlineLayoutAlgorithm::FirstLineStyle() const { 96 const ComputedStyle& NGInlineLayoutAlgorithm::FirstLineStyle() const {
96 return Node()->GetLayoutObject()->FirstLineStyleRef(); 97 return Node()->GetLayoutObject()->FirstLineStyleRef();
97 } 98 }
98 99
99 const ComputedStyle& NGInlineLayoutAlgorithm::LineStyle() const { 100 const ComputedStyle& NGInlineLayoutAlgorithm::LineStyle() const {
100 return IsFirstLine() ? FirstLineStyle() : Style(); 101 return IsFirstLine() ? FirstLineStyle() : Style();
101 } 102 }
102 103
103 bool NGInlineLayoutAlgorithm::CanFitOnLine() const { 104 LayoutUnit NGInlineLayoutAlgorithm::AvailableWidth() const {
104 LayoutUnit available_size = current_opportunity_.InlineSize(); 105 return current_opportunity_.InlineSize();
105 if (available_size == NGSizeIndefinite)
106 return true;
107 return end_position_ <= available_size;
108 } 106 }
109 107
110 bool NGInlineLayoutAlgorithm::HasItems() const { 108 bool NGInlineLayoutAlgorithm::CreateLine(
111 return start_offset_ != end_offset_; 109 NGInlineItemResults* item_results,
112 } 110 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(); 111 const Vector<NGInlineItem>& items = Node()->Items();
258 112
259 // Create a list of LineItemChunk from |start| and |last_break_opportunity|. 113 // TODO(crbug.com/716930): We may be an empty LayoutInline due to splitting.
260 // TODO(kojii): Consider refactoring LineItemChunk once NGLineBuilder's public 114 // 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. 115 // need to pass through our margin strut.
262 Vector<LineItemChunk, 32> line_item_chunks; 116 if (!items.IsEmpty()) {
263 unsigned start_offset = start_offset_; 117 NGLogicalOffset bfc_offset = ConstraintSpace().BfcOffset();
264 for (unsigned i = start_index_; i <= last_break_opportunity_index_; i++) { 118 bfc_offset.block_offset += ConstraintSpace().MarginStrut().Sum();
265 const NGInlineItem& item = items[i]; 119 MaybeUpdateFragmentBfcOffset(ConstraintSpace(), bfc_offset,
266 unsigned end_offset = 120 &container_builder_);
267 std::min(item.EndOffset(), last_break_opportunity_offset_); 121 PositionPendingFloats(bfc_offset.block_offset, &container_builder_,
268 line_item_chunks.push_back( 122 MutableConstraintSpace());
269 LineItemChunk{i, start_offset, end_offset,
270 InlineSize(item, start_offset, end_offset)});
271 start_offset = end_offset;
272 } 123 }
273 124
274 if (Node()->IsBidiEnabled()) 125 if (Node()->IsBidiEnabled())
275 BidiReorder(&line_item_chunks); 126 BidiReorder(item_results);
276 127
277 if (!PlaceItems(line_item_chunks)) 128 if (!PlaceItems(item_results, break_token))
278 return false; 129 return false;
279 130
280 // Prepare for the next line. 131 // 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 = 132 NGLogicalOffset origin_point =
298 GetOriginPointForFloats(ConstraintSpace(), content_size_); 133 GetOriginPointForFloats(ConstraintSpace(), content_size_);
299 PositionPendingFloats(origin_point.block_offset, &container_builder_, 134 PositionPendingFloats(origin_point.block_offset, &container_builder_,
300 MutableConstraintSpace()); 135 MutableConstraintSpace());
301 FindNextLayoutOpportunity(); 136 FindNextLayoutOpportunity();
302 return true; 137 return true;
303 } 138 }
304 139
305 void NGInlineLayoutAlgorithm::BidiReorder( 140 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 141 // 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. 142 // embedding levels of parts of runs, which requires to split items.
314 // http://unicode.org/reports/tr9/#L1 143 // http://unicode.org/reports/tr9/#L1
315 // BidiResolver does not support L1 crbug.com/316409. 144 // BidiResolver does not support L1 crbug.com/316409.
316 145
317 // Create a list of chunk indices in the visual order. 146 // Create a list of chunk indices in the visual order.
318 // ICU |ubidi_getVisualMap()| works for a run of characters. Since we can 147 // 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 148 // handle the direction of each run, we use |ubidi_reorderVisual()| to reorder
320 // runs instead of characters. 149 // runs instead of characters.
321 Vector<UBiDiLevel, 32> levels; 150 Vector<UBiDiLevel, 32> levels;
322 levels.ReserveInitialCapacity(line_item_chunks->size()); 151 levels.ReserveInitialCapacity(line_items->size());
323 const Vector<NGInlineItem>& items = Node()->Items(); 152 const Vector<NGInlineItem>& items = Node()->Items();
324 for (const auto& chunk : *line_item_chunks) 153 for (const auto& item_result : *line_items)
325 levels.push_back(items[chunk.index].BidiLevel()); 154 levels.push_back(items[item_result.item_index].BidiLevel());
326 Vector<int32_t, 32> indices_in_visual_order(line_item_chunks->size()); 155 Vector<int32_t, 32> indices_in_visual_order(line_items->size());
327 NGBidiParagraph::IndicesInVisualOrder(levels, &indices_in_visual_order); 156 NGBidiParagraph::IndicesInVisualOrder(levels, &indices_in_visual_order);
328 157
329 // Reorder |line_item_chunks| in visual order. 158 // Reorder to the visual order.
330 Vector<LineItemChunk, 32> line_item_chunks_in_visual_order( 159 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(); 160 for (unsigned visual_index = 0; visual_index < indices_in_visual_order.size();
333 visual_index++) { 161 visual_index++) {
334 unsigned logical_index = indices_in_visual_order[visual_index]; 162 unsigned logical_index = indices_in_visual_order[visual_index];
335 line_item_chunks_in_visual_order[visual_index] = 163 line_items_in_visual_order[visual_index] =
336 (*line_item_chunks)[logical_index]; 164 std::move((*line_items)[logical_index]);
337 } 165 }
338 166
339 // Keep Open before Close in the visual order. 167 // Keep Open before Close in the visual order.
340 HashMap<LayoutObject*, unsigned> first_index; 168 HashMap<LayoutObject*, unsigned> first_index;
341 for (unsigned i = 0; i < line_item_chunks_in_visual_order.size(); i++) { 169 for (unsigned i = 0; i < line_items_in_visual_order.size(); i++) {
342 LineItemChunk& chunk = line_item_chunks_in_visual_order[i]; 170 NGInlineItemResult& item_result = line_items_in_visual_order[i];
343 const NGInlineItem& item = items[chunk.index]; 171 const NGInlineItem& item = items[item_result.item_index];
344 if (item.Type() != NGInlineItem::kOpenTag && 172 if (item.Type() != NGInlineItem::kOpenTag &&
345 item.Type() != NGInlineItem::kCloseTag) { 173 item.Type() != NGInlineItem::kCloseTag) {
346 continue; 174 continue;
347 } 175 }
348 auto result = first_index.insert(item.GetLayoutObject(), i); 176 auto result = first_index.insert(item.GetLayoutObject(), i);
349 if (!result.is_new_entry && item.Type() == NGInlineItem::kOpenTag) { 177 if (!result.is_new_entry && item.Type() == NGInlineItem::kOpenTag) {
350 std::swap(line_item_chunks_in_visual_order[i], 178 std::swap(line_items_in_visual_order[i],
351 line_item_chunks_in_visual_order[result.stored_value->value]); 179 line_items_in_visual_order[result.stored_value->value]);
352 } 180 }
353 } 181 }
354 182
355 line_item_chunks->swap(line_item_chunks_in_visual_order); 183 line_items->swap(line_items_in_visual_order);
356 } 184 }
357 185
358 // TODO(glebl): Add the support of clearance for inline floats. 186 // TODO(glebl): Add the support of clearance for inline floats.
359 void NGInlineLayoutAlgorithm::LayoutAndPositionFloat( 187 void NGInlineLayoutAlgorithm::LayoutAndPositionFloat(
360 LayoutUnit end_position, 188 LayoutUnit end_position,
361 LayoutObject* layout_object) { 189 LayoutObject* layout_object) {
362 NGBlockNode* node = new NGBlockNode(layout_object); 190 NGBlockNode* node = new NGBlockNode(layout_object);
363 191
364 RefPtr<NGConstraintSpace> float_space = 192 RefPtr<NGConstraintSpace> float_space =
365 CreateConstraintSpaceForFloat(Node()->Style(), *node, &space_builder_); 193 CreateConstraintSpaceForFloat(Node()->Style(), *node, &space_builder_);
(...skipping 28 matching lines...) Expand all
394 float_does_not_fit) { 222 float_does_not_fit) {
395 container_builder_.AddUnpositionedFloat(floating_object); 223 container_builder_.AddUnpositionedFloat(floating_object);
396 } else { 224 } else {
397 container_builder_.MutablePositionedFloats().push_back( 225 container_builder_.MutablePositionedFloats().push_back(
398 PositionFloat(floating_object.Get(), MutableConstraintSpace())); 226 PositionFloat(floating_object.Get(), MutableConstraintSpace()));
399 FindNextLayoutOpportunity(); 227 FindNextLayoutOpportunity();
400 } 228 }
401 } 229 }
402 230
403 bool NGInlineLayoutAlgorithm::PlaceItems( 231 bool NGInlineLayoutAlgorithm::PlaceItems(
404 const Vector<LineItemChunk, 32>& line_item_chunks) { 232 NGInlineItemResults* line_items,
233 RefPtr<NGInlineBreakToken> break_token) {
405 const Vector<NGInlineItem>& items = Node()->Items(); 234 const Vector<NGInlineItem>& items = Node()->Items();
406 235
407 const ComputedStyle& line_style = LineStyle(); 236 const ComputedStyle& line_style = LineStyle();
408 NGLineHeightMetrics line_metrics(line_style, baseline_type_); 237 NGLineHeightMetrics line_metrics(line_style, baseline_type_);
409 NGLineHeightMetrics line_metrics_with_leading = line_metrics; 238 NGLineHeightMetrics line_metrics_with_leading = line_metrics;
410 line_metrics_with_leading.AddLeading(line_style.ComputedLineHeightAsFixed()); 239 line_metrics_with_leading.AddLeading(line_style.ComputedLineHeightAsFixed());
411 NGLineBoxFragmentBuilder line_box(Node()); 240 NGLineBoxFragmentBuilder line_box(Node());
412 241
413 // Compute heights of all inline items by placing the dominant baseline at 0. 242 // 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. 243 // The baseline is adjusted after the height of the line box is computed.
415 NGTextFragmentBuilder text_builder(Node()); 244 NGTextFragmentBuilder text_builder(Node());
416 NGInlineBoxState* box = 245 NGInlineBoxState* box =
417 box_states_.OnBeginPlaceItems(&LineStyle(), baseline_type_); 246 box_states_.OnBeginPlaceItems(&LineStyle(), baseline_type_);
418 LayoutUnit inline_size; 247 LayoutUnit inline_size;
419 for (const auto& line_item_chunk : line_item_chunks) { 248 for (auto& item_result : *line_items) {
420 const NGInlineItem& item = items[line_item_chunk.index]; 249 const NGInlineItem& item = items[item_result.item_index];
421 LayoutUnit line_top; 250 LayoutUnit line_top;
422 if (item.Type() == NGInlineItem::kText) { 251 if (item.Type() == NGInlineItem::kText) {
423 DCHECK(item.GetLayoutObject()->IsText()); 252 DCHECK(item.GetLayoutObject()->IsText());
424 DCHECK(!box->text_metrics.IsEmpty()); 253 DCHECK(!box->text_metrics.IsEmpty());
425 line_top = box->text_top; 254 line_top = box->text_top;
426 text_builder.SetSize( 255 text_builder.SetSize(
427 {line_item_chunk.inline_size, box->text_metrics.LineHeight()}); 256 {item_result.inline_size, box->text_metrics.LineHeight()});
428 // Take all used fonts into account if 'line-height: normal'. 257 // Take all used fonts into account if 'line-height: normal'.
429 if (box->include_used_fonts) { 258 if (box->include_used_fonts) {
430 box->AccumulateUsedFonts(item, line_item_chunk.start_offset, 259 box->AccumulateUsedFonts(item, item_result.start_offset,
431 line_item_chunk.end_offset, baseline_type_); 260 item_result.end_offset, baseline_type_);
432 } 261 }
433 } else if (item.Type() == NGInlineItem::kOpenTag) { 262 } else if (item.Type() == NGInlineItem::kOpenTag) {
434 box = box_states_.OnOpenTag(item, &line_box, &text_builder); 263 box = box_states_.OnOpenTag(item, &line_box, &text_builder);
435 // Compute text metrics for all inline boxes since even empty inlines 264 // Compute text metrics for all inline boxes since even empty inlines
436 // influence the line height. 265 // influence the line height.
437 // https://drafts.csswg.org/css2/visudet.html#line-height 266 // https://drafts.csswg.org/css2/visudet.html#line-height
438 // TODO(kojii): Review if atomic inline level should have open/close. 267 // TODO(kojii): Review if atomic inline level should have open/close.
439 if (!item.GetLayoutObject()->IsAtomicInlineLevel()) 268 if (!item.GetLayoutObject()->IsAtomicInlineLevel())
440 box->ComputeTextMetrics(*item.Style(), baseline_type_); 269 box->ComputeTextMetrics(*item.Style(), baseline_type_);
441 continue; 270 continue;
442 } else if (item.Type() == NGInlineItem::kCloseTag) { 271 } else if (item.Type() == NGInlineItem::kCloseTag) {
443 box = box_states_.OnCloseTag(item, &line_box, box); 272 box = box_states_.OnCloseTag(item, &line_box, box);
444 continue; 273 continue;
445 } else if (item.Type() == NGInlineItem::kAtomicInline) { 274 } else if (item.Type() == NGInlineItem::kAtomicInline) {
446 line_top = PlaceAtomicInline(item, &line_box, box, &text_builder); 275 line_top =
276 PlaceAtomicInline(item, &item_result, &line_box, box, &text_builder);
447 } else if (item.Type() == NGInlineItem::kOutOfFlowPositioned) { 277 } else if (item.Type() == NGInlineItem::kOutOfFlowPositioned) {
448 // TODO(layout-dev): Report the correct static position for the out of 278 // 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 279 // flow descendant. We can't do this here yet as it doesn't know the
450 // size of the line box. 280 // size of the line box.
451 container_builder_.AddOutOfFlowDescendant( 281 container_builder_.AddOutOfFlowDescendant(
452 // Absolute positioning blockifies the box's display type. 282 // Absolute positioning blockifies the box's display type.
453 // https://drafts.csswg.org/css-display/#transformations 283 // https://drafts.csswg.org/css-display/#transformations
454 new NGBlockNode(item.GetLayoutObject()), 284 new NGBlockNode(item.GetLayoutObject()),
455 NGStaticPosition::Create(ConstraintSpace().WritingMode(), 285 NGStaticPosition::Create(ConstraintSpace().WritingMode(),
456 ConstraintSpace().Direction(), 286 ConstraintSpace().Direction(),
457 NGPhysicalOffset())); 287 NGPhysicalOffset()));
458 continue; 288 continue;
459 } else { 289 } else {
460 continue; 290 continue;
461 } 291 }
462 292
463 RefPtr<NGPhysicalTextFragment> text_fragment = text_builder.ToTextFragment( 293 RefPtr<NGPhysicalTextFragment> text_fragment = text_builder.ToTextFragment(
464 line_item_chunk.index, line_item_chunk.start_offset, 294 item_result.item_index, item_result.start_offset,
465 line_item_chunk.end_offset); 295 item_result.end_offset);
466 296
467 NGLogicalOffset logical_offset( 297 NGLogicalOffset logical_offset(
468 inline_size + current_opportunity_.InlineStartOffset() - 298 inline_size + current_opportunity_.InlineStartOffset() -
469 ConstraintSpace().BfcOffset().inline_offset, 299 ConstraintSpace().BfcOffset().inline_offset,
470 line_top); 300 line_top);
471 line_box.AddChild(std::move(text_fragment), logical_offset); 301 line_box.AddChild(std::move(text_fragment), logical_offset);
472 inline_size += line_item_chunk.inline_size; 302 inline_size += item_result.inline_size;
473 } 303 }
474 304
475 if (line_box.Children().IsEmpty()) { 305 if (line_box.Children().IsEmpty()) {
476 return true; // The line was empty. 306 return true; // The line was empty.
477 } 307 }
478 308
479 box_states_.OnEndPlaceItems(&line_box); 309 box_states_.OnEndPlaceItems(&line_box);
480 310
481 // The baselines are always placed at pixel boundaries. Not doing so results 311 // The baselines are always placed at pixel boundaries. Not doing so results
482 // in incorrect layout of text decorations, most notably underlines. 312 // in incorrect layout of text decorations, most notably underlines.
483 LayoutUnit baseline = content_size_ + line_box.Metrics().ascent; 313 LayoutUnit baseline = content_size_ + line_box.Metrics().ascent;
484 baseline = LayoutUnit(baseline.Round()); 314 baseline = LayoutUnit(baseline.Round());
485 315
486 // Check if the line fits into the constraint space in block direction. 316 // Check if the line fits into the constraint space in block direction.
487 LayoutUnit line_bottom = baseline + line_box.Metrics().descent; 317 LayoutUnit line_bottom = baseline + line_box.Metrics().descent;
488 318
489 if (!container_builder_.Children().IsEmpty() && 319 if (!container_builder_.Children().IsEmpty() &&
490 ConstraintSpace().AvailableSize().block_size != NGSizeIndefinite && 320 ConstraintSpace().AvailableSize().block_size != NGSizeIndefinite &&
491 line_bottom > ConstraintSpace().AvailableSize().block_size) { 321 line_bottom > ConstraintSpace().AvailableSize().block_size) {
492 return false; 322 return false;
493 } 323 }
494 324
495 // If there are more content to consume, create an unfinished break token. 325 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 326
502 // TODO(kojii): Implement flipped line (vertical-lr). In this case, line_top 327 // TODO(kojii): Implement flipped line (vertical-lr). In this case, line_top
503 // and block_start do not match. 328 // and block_start do not match.
504 329
505 // Up until this point, children are placed so that the dominant baseline is 330 // 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 331 // at 0. Move them to the final baseline position, and set the logical top of
507 // the line box to the line top. 332 // the line box to the line top.
508 line_box.MoveChildrenInBlockDirection(baseline); 333 line_box.MoveChildrenInBlockDirection(baseline);
509 line_box.SetInlineSize(inline_size); 334 line_box.SetInlineSize(inline_size);
510 container_builder_.AddChild( 335 container_builder_.AddChild(
511 line_box.ToLineBoxFragment(), 336 line_box.ToLineBoxFragment(),
512 {LayoutUnit(), baseline + box_states_.LineBoxState().text_top}); 337 {LayoutUnit(), baseline + box_states_.LineBoxState().text_top});
513 338
514 max_inline_size_ = std::max(max_inline_size_, inline_size); 339 max_inline_size_ = std::max(max_inline_size_, inline_size);
515 content_size_ = line_bottom; 340 content_size_ = line_bottom;
516 return true; 341 return true;
517 } 342 }
518 343
519 LayoutUnit NGInlineLayoutAlgorithm::PlaceAtomicInline( 344 LayoutUnit NGInlineLayoutAlgorithm::PlaceAtomicInline(
520 const NGInlineItem& item, 345 const NGInlineItem& item,
346 NGInlineItemResult* item_result,
eae 2017/05/10 21:31:38 Can we turn this const? If so a const reference wo
kojii 2017/05/11 16:17:36 Currently we can, but when we support painting, th
521 NGLineBoxFragmentBuilder* line_box, 347 NGLineBoxFragmentBuilder* line_box,
522 NGInlineBoxState* state, 348 NGInlineBoxState* state,
523 NGTextFragmentBuilder* text_builder) { 349 NGTextFragmentBuilder* text_builder) {
350 DCHECK(item_result->layout_result);
524 NGBoxFragment fragment( 351 NGBoxFragment fragment(
525 ConstraintSpace().WritingMode(), 352 ConstraintSpace().WritingMode(),
526 ToNGPhysicalBoxFragment(LayoutItem(item)->PhysicalFragment().Get())); 353 ToNGPhysicalBoxFragment(
354 item_result->layout_result->PhysicalFragment().Get()));
527 // TODO(kojii): Margin and border in block progression not implemented yet. 355 // TODO(kojii): Margin and border in block progression not implemented yet.
528 LayoutUnit block_size = fragment.BlockSize(); 356 LayoutUnit block_size = fragment.BlockSize();
529 357
530 // 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
531 // |fragment| directly. Currently |CopyFragmentDataToLayoutBlockFlow| 359 // |fragment| directly. Currently |CopyFragmentDataToLayoutBlockFlow|
532 // requires a text fragment. 360 // requires a text fragment.
533 text_builder->SetSize({fragment.InlineSize(), block_size}); 361 text_builder->SetSize({fragment.InlineSize(), block_size});
534 362
535 // TODO(kojii): Add baseline position to NGPhysicalFragment. 363 // TODO(kojii): Add baseline position to NGPhysicalFragment.
536 LayoutBox* layout_box = ToLayoutBox(item.GetLayoutObject()); 364 LayoutBox* layout_box = ToLayoutBox(item.GetLayoutObject());
(...skipping 17 matching lines...) Expand all
554 if (container_builder_.BfcOffset()) 382 if (container_builder_.BfcOffset())
555 iter_offset = ContainerBfcOffset(); 383 iter_offset = ContainerBfcOffset();
556 iter_offset.block_offset += content_size_; 384 iter_offset.block_offset += content_size_;
557 auto* iter = MutableConstraintSpace()->LayoutOpportunityIterator(iter_offset); 385 auto* iter = MutableConstraintSpace()->LayoutOpportunityIterator(iter_offset);
558 NGLayoutOpportunity opportunity = iter->Next(); 386 NGLayoutOpportunity opportunity = iter->Next();
559 if (!opportunity.IsEmpty()) 387 if (!opportunity.IsEmpty())
560 current_opportunity_ = opportunity; 388 current_opportunity_ = opportunity;
561 } 389 }
562 390
563 RefPtr<NGLayoutResult> NGInlineLayoutAlgorithm::Layout() { 391 RefPtr<NGLayoutResult> NGInlineLayoutAlgorithm::Layout() {
564 // TODO(koji): The relationship of NGInlineLayoutAlgorithm and NGLineBreaker 392 NGLineBreaker line_breaker(Node(), constraint_space_, BreakToken());
565 // should be inverted. 393 NGInlineItemResults item_results;
566 if (!Node()->Text().IsEmpty()) { 394 while (true) {
567 // TODO(eae): Does this need the LayoutText::LocaleForLineBreakIterator 395 line_breaker.NextLine(&item_results, this);
568 // logic to switch the locale based on breaking mode? 396 if (item_results.IsEmpty())
569 NGLineBreaker line_breaker(Node()->Style().Locale()); 397 break;
570 line_breaker.BreakLines(this, Node()->Text(), start_offset_); 398 CreateLine(&item_results, line_breaker.CreateBreakToken());
399 item_results.clear();
571 } 400 }
572 401
573 // 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.
574 NGLogicalSize size(max_inline_size_, content_size_); 403 NGLogicalSize size(max_inline_size_, content_size_);
575 container_builder_.SetSize(size).SetOverflowSize(size); 404 container_builder_.SetSize(size).SetOverflowSize(size);
576 405
577 // 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.
578 // 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
579 // removed LayoutInline splitting. 408 // removed LayoutInline splitting.
580 if (!container_builder_.BfcOffset()) { 409 if (!container_builder_.BfcOffset()) {
581 container_builder_.SetEndMarginStrut(ConstraintSpace().MarginStrut()); 410 container_builder_.SetEndMarginStrut(ConstraintSpace().MarginStrut());
582 } 411 }
583 412
584 return container_builder_.ToBoxFragment(); 413 return container_builder_.ToBoxFragment();
585 } 414 }
586 415
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 416 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698