OLD | NEW |
| (Empty) |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "core/layout/ng/ng_line_builder.h" | |
6 | |
7 #include "core/layout/BidiRun.h" | |
8 #include "core/layout/LayoutBlockFlow.h" | |
9 #include "core/layout/line/LineInfo.h" | |
10 #include "core/layout/line/RootInlineBox.h" | |
11 #include "core/layout/ng/layout_ng_block_flow.h" | |
12 #include "core/layout/ng/ng_bidi_paragraph.h" | |
13 #include "core/layout/ng/ng_block_layout_algorithm.h" | |
14 #include "core/layout/ng/ng_box_fragment.h" | |
15 #include "core/layout/ng/ng_constraint_space.h" | |
16 #include "core/layout/ng/ng_constraint_space_builder.h" | |
17 #include "core/layout/ng/ng_floating_object.h" | |
18 #include "core/layout/ng/ng_floats_utils.h" | |
19 #include "core/layout/ng/ng_fragment_builder.h" | |
20 #include "core/layout/ng/ng_inline_node.h" | |
21 #include "core/layout/ng/ng_length_utils.h" | |
22 #include "core/layout/ng/ng_line_box_fragment.h" | |
23 #include "core/layout/ng/ng_line_box_fragment_builder.h" | |
24 #include "core/layout/ng/ng_space_utils.h" | |
25 #include "core/layout/ng/ng_text_fragment.h" | |
26 #include "core/layout/ng/ng_text_fragment_builder.h" | |
27 #include "core/style/ComputedStyle.h" | |
28 #include "platform/text/BidiRunList.h" | |
29 | |
30 namespace blink { | |
31 namespace { | |
32 | |
33 RefPtr<NGConstraintSpace> CreateConstraintSpaceForFloat( | |
34 const ComputedStyle& style, | |
35 const NGConstraintSpace& parent_space, | |
36 NGConstraintSpaceBuilder* space_builder) { | |
37 DCHECK(space_builder) << "space_builder cannot be null here"; | |
38 bool is_new_bfc = | |
39 IsNewFormattingContextForBlockLevelChild(parent_space, style); | |
40 return space_builder->SetIsNewFormattingContext(is_new_bfc) | |
41 .SetTextDirection(style.direction()) | |
42 .SetIsShrinkToFit(ShouldShrinkToFit(parent_space, style)) | |
43 .ToConstraintSpace(FromPlatformWritingMode(style.getWritingMode())); | |
44 } | |
45 | |
46 NGLogicalOffset GetOriginPointForFloats(const NGConstraintSpace& space, | |
47 LayoutUnit content_size) { | |
48 NGLogicalOffset origin_point = space.BfcOffset(); | |
49 origin_point.block_offset += content_size; | |
50 return origin_point; | |
51 } | |
52 | |
53 void PositionPendingFloats(const NGLogicalOffset& origin_point, | |
54 NGConstraintSpace* space, | |
55 NGFragmentBuilder* builder) { | |
56 DCHECK(builder) << "Builder cannot be null here"; | |
57 | |
58 for (auto& floating_object : builder->UnpositionedFloats()) { | |
59 NGLogicalOffset offset = PositionFloat(origin_point, space->BfcOffset(), | |
60 floating_object.get(), space); | |
61 builder->AddFloatingObject(floating_object, offset); | |
62 } | |
63 builder->MutableUnpositionedFloats().clear(); | |
64 } | |
65 | |
66 } // namespace | |
67 | |
68 NGLineBuilder::NGLineBuilder(NGInlineNode* inline_box, | |
69 NGConstraintSpace* constraint_space) | |
70 : inline_box_(inline_box), | |
71 constraint_space_(constraint_space), | |
72 container_builder_(NGPhysicalFragment::kFragmentBox, inline_box_), | |
73 container_layout_result_(nullptr), | |
74 is_horizontal_writing_mode_( | |
75 blink::IsHorizontalWritingMode(constraint_space->WritingMode())), | |
76 space_builder_(constraint_space) | |
77 #if DCHECK_IS_ON() | |
78 , | |
79 is_bidi_reordered_(false) | |
80 #endif | |
81 { | |
82 if (!is_horizontal_writing_mode_) | |
83 baseline_type_ = FontBaseline::IdeographicBaseline; | |
84 } | |
85 | |
86 bool NGLineBuilder::CanFitOnLine() const { | |
87 LayoutUnit available_size = current_opportunity_.InlineSize(); | |
88 if (available_size == NGSizeIndefinite) | |
89 return true; | |
90 return end_position_ <= available_size; | |
91 } | |
92 | |
93 bool NGLineBuilder::HasItems() const { | |
94 return start_offset_ != end_offset_; | |
95 } | |
96 | |
97 bool NGLineBuilder::HasBreakOpportunity() const { | |
98 return start_offset_ != last_break_opportunity_offset_; | |
99 } | |
100 | |
101 bool NGLineBuilder::HasItemsAfterLastBreakOpportunity() const { | |
102 return last_break_opportunity_offset_ != end_offset_; | |
103 } | |
104 | |
105 void NGLineBuilder::SetStart(unsigned index, unsigned offset) { | |
106 inline_box_->AssertOffset(index, offset); | |
107 | |
108 start_index_ = last_index_ = last_break_opportunity_index_ = index; | |
109 start_offset_ = end_offset_ = last_break_opportunity_offset_ = offset; | |
110 end_position_ = last_break_opportunity_position_ = LayoutUnit(); | |
111 | |
112 FindNextLayoutOpportunity(); | |
113 } | |
114 | |
115 void NGLineBuilder::SetEnd(unsigned new_end_offset) { | |
116 DCHECK_GT(new_end_offset, end_offset_); | |
117 const Vector<NGLayoutInlineItem>& items = inline_box_->Items(); | |
118 DCHECK_LE(new_end_offset, items.back().EndOffset()); | |
119 | |
120 // SetEnd() while |new_end_offset| is beyond the current last item. | |
121 unsigned index = last_index_; | |
122 const NGLayoutInlineItem* item = &items[index]; | |
123 if (new_end_offset > item->EndOffset()) { | |
124 if (end_offset_ < item->EndOffset()) { | |
125 SetEnd(index, item->EndOffset(), | |
126 InlineSize(*item, end_offset_, item->EndOffset())); | |
127 } | |
128 item = &items[++index]; | |
129 | |
130 while (new_end_offset > item->EndOffset()) { | |
131 SetEnd(index, item->EndOffset(), InlineSize(*item)); | |
132 item = &items[++index]; | |
133 } | |
134 } | |
135 | |
136 SetEnd(index, new_end_offset, InlineSize(*item, end_offset_, new_end_offset)); | |
137 | |
138 // Include closing elements. | |
139 while (new_end_offset == item->EndOffset() && index < items.size() - 1) { | |
140 item = &items[++index]; | |
141 if (item->Type() != NGLayoutInlineItem::kCloseTag) | |
142 break; | |
143 SetEnd(index, new_end_offset, InlineSize(*item)); | |
144 } | |
145 } | |
146 | |
147 void NGLineBuilder::SetEnd(unsigned index, | |
148 unsigned new_end_offset, | |
149 LayoutUnit inline_size_since_current_end) { | |
150 const Vector<NGLayoutInlineItem>& items = inline_box_->Items(); | |
151 DCHECK_LE(new_end_offset, items.back().EndOffset()); | |
152 | |
153 // |new_end_offset| should be in the current item or next. | |
154 // TODO(kojii): Reconsider this restriction if needed. | |
155 DCHECK((index == last_index_ && new_end_offset > end_offset_) || | |
156 (index == last_index_ + 1 && new_end_offset >= end_offset_ && | |
157 end_offset_ == items[last_index_].EndOffset())); | |
158 const NGLayoutInlineItem& item = items[index]; | |
159 item.AssertEndOffset(new_end_offset); | |
160 | |
161 if (item.Type() == NGLayoutInlineItem::kFloating) { | |
162 LayoutAndPositionFloat( | |
163 LayoutUnit(end_position_) + inline_size_since_current_end, | |
164 item.GetLayoutObject()); | |
165 } | |
166 | |
167 last_index_ = index; | |
168 end_offset_ = new_end_offset; | |
169 end_position_ += inline_size_since_current_end; | |
170 } | |
171 | |
172 void NGLineBuilder::SetBreakOpportunity() { | |
173 last_break_opportunity_index_ = last_index_; | |
174 last_break_opportunity_offset_ = end_offset_; | |
175 last_break_opportunity_position_ = end_position_; | |
176 } | |
177 | |
178 void NGLineBuilder::SetStartOfHangables(unsigned offset) { | |
179 // TODO(kojii): Implement. | |
180 } | |
181 | |
182 LayoutUnit NGLineBuilder::InlineSize(const NGLayoutInlineItem& item) { | |
183 if (item.Type() == NGLayoutInlineItem::kAtomicInline) | |
184 return InlineSizeFromLayout(item); | |
185 return item.InlineSize(); | |
186 } | |
187 | |
188 LayoutUnit NGLineBuilder::InlineSize(const NGLayoutInlineItem& item, | |
189 unsigned start_offset, | |
190 unsigned end_offset) { | |
191 if (item.StartOffset() == start_offset && item.EndOffset() == end_offset) | |
192 return InlineSize(item); | |
193 return item.InlineSize(start_offset, end_offset); | |
194 } | |
195 | |
196 LayoutUnit NGLineBuilder::InlineSizeFromLayout(const NGLayoutInlineItem& item) { | |
197 return NGBoxFragment(ConstraintSpace().WritingMode(), | |
198 toNGPhysicalBoxFragment( | |
199 LayoutItem(item)->PhysicalFragment().get())) | |
200 .InlineSize(); | |
201 } | |
202 | |
203 const NGLayoutResult* NGLineBuilder::LayoutItem( | |
204 const NGLayoutInlineItem& item) { | |
205 // Returns the cached NGLayoutResult if available. | |
206 const Vector<NGLayoutInlineItem>& items = inline_box_->Items(); | |
207 if (layout_results_.isEmpty()) | |
208 layout_results_.resize(items.size()); | |
209 unsigned index = std::distance(items.begin(), &item); | |
210 RefPtr<NGLayoutResult>* layout_result = &layout_results_[index]; | |
211 if (*layout_result) | |
212 return layout_result->get(); | |
213 | |
214 DCHECK(item.Type() == NGLayoutInlineItem::kAtomicInline); | |
215 NGBlockNode* node = new NGBlockNode(item.GetLayoutObject()); | |
216 // TODO(kojii): Keep node in NGLayoutInlineItem. | |
217 const ComputedStyle& style = node->Style(); | |
218 NGConstraintSpaceBuilder constraint_space_builder(&ConstraintSpace()); | |
219 RefPtr<NGConstraintSpace> constraint_space = | |
220 constraint_space_builder.SetIsNewFormattingContext(true) | |
221 .SetIsShrinkToFit(true) | |
222 .SetTextDirection(style.direction()) | |
223 .ToConstraintSpace(FromPlatformWritingMode(style.getWritingMode())); | |
224 *layout_result = node->Layout(constraint_space.get()); | |
225 return layout_result->get(); | |
226 } | |
227 | |
228 void NGLineBuilder::CreateLine() { | |
229 if (HasItemsAfterLastBreakOpportunity()) | |
230 SetBreakOpportunity(); | |
231 CreateLineUpToLastBreakOpportunity(); | |
232 } | |
233 | |
234 void NGLineBuilder::CreateLineUpToLastBreakOpportunity() { | |
235 const Vector<NGLayoutInlineItem>& items = inline_box_->Items(); | |
236 | |
237 // Create a list of LineItemChunk from |start| and |last_break_opportunity|. | |
238 // TODO(kojii): Consider refactoring LineItemChunk once NGLineBuilder's public | |
239 // API is more finalized. It does not fit well with the current API. | |
240 Vector<LineItemChunk, 32> line_item_chunks; | |
241 unsigned start_offset = start_offset_; | |
242 for (unsigned i = start_index_; i <= last_break_opportunity_index_; i++) { | |
243 const NGLayoutInlineItem& item = items[i]; | |
244 unsigned end_offset = | |
245 std::min(item.EndOffset(), last_break_opportunity_offset_); | |
246 line_item_chunks.push_back( | |
247 LineItemChunk{i, start_offset, end_offset, | |
248 InlineSize(item, start_offset, end_offset)}); | |
249 start_offset = end_offset; | |
250 } | |
251 | |
252 if (inline_box_->IsBidiEnabled()) | |
253 BidiReorder(&line_item_chunks); | |
254 | |
255 PlaceItems(line_item_chunks); | |
256 | |
257 // Prepare for the next line. | |
258 // Move |start| to |last_break_opportunity|, keeping items after | |
259 // |last_break_opportunity|. | |
260 start_index_ = last_break_opportunity_index_; | |
261 start_offset_ = last_break_opportunity_offset_; | |
262 DCHECK_GE(end_position_, last_break_opportunity_position_); | |
263 end_position_ -= last_break_opportunity_position_; | |
264 last_break_opportunity_position_ = LayoutUnit(); | |
265 #if DCHECK_IS_ON() | |
266 is_bidi_reordered_ = false; | |
267 #endif | |
268 | |
269 NGLogicalOffset origin_point = | |
270 GetOriginPointForFloats(ConstraintSpace(), content_size_); | |
271 PositionPendingFloats(origin_point, constraint_space_, &container_builder_); | |
272 FindNextLayoutOpportunity(); | |
273 } | |
274 | |
275 void NGLineBuilder::BidiReorder(Vector<LineItemChunk, 32>* line_item_chunks) { | |
276 #if DCHECK_IS_ON() | |
277 DCHECK(!is_bidi_reordered_); | |
278 is_bidi_reordered_ = true; | |
279 #endif | |
280 | |
281 // TODO(kojii): UAX#9 L1 is not supported yet. Supporting L1 may change | |
282 // embedding levels of parts of runs, which requires to split items. | |
283 // http://unicode.org/reports/tr9/#L1 | |
284 // BidiResolver does not support L1 crbug.com/316409. | |
285 | |
286 // Create a list of chunk indices in the visual order. | |
287 // ICU |ubidi_getVisualMap()| works for a run of characters. Since we can | |
288 // handle the direction of each run, we use |ubidi_reorderVisual()| to reorder | |
289 // runs instead of characters. | |
290 Vector<UBiDiLevel, 32> levels; | |
291 levels.reserveInitialCapacity(line_item_chunks->size()); | |
292 for (const auto& chunk : *line_item_chunks) | |
293 levels.push_back(inline_box_->Items()[chunk.index].BidiLevel()); | |
294 Vector<int32_t, 32> indices_in_visual_order(line_item_chunks->size()); | |
295 NGBidiParagraph::IndicesInVisualOrder(levels, &indices_in_visual_order); | |
296 | |
297 // Reorder |line_item_chunks| in visual order. | |
298 Vector<LineItemChunk, 32> line_item_chunks_in_visual_order( | |
299 line_item_chunks->size()); | |
300 for (unsigned visual_index = 0; visual_index < indices_in_visual_order.size(); | |
301 visual_index++) { | |
302 unsigned logical_index = indices_in_visual_order[visual_index]; | |
303 line_item_chunks_in_visual_order[visual_index] = | |
304 (*line_item_chunks)[logical_index]; | |
305 } | |
306 line_item_chunks->swap(line_item_chunks_in_visual_order); | |
307 } | |
308 | |
309 // TODO(glebl): Add the support of clearance for inline floats. | |
310 void NGLineBuilder::LayoutAndPositionFloat(LayoutUnit end_position, | |
311 LayoutObject* layout_object) { | |
312 NGBlockNode* node = new NGBlockNode(layout_object); | |
313 RefPtr<NGConstraintSpace> float_space = CreateConstraintSpaceForFloat( | |
314 node->Style(), ConstraintSpace(), &space_builder_); | |
315 | |
316 // TODO(glebl): add the fragmentation support: | |
317 // same writing mode - get the inline size ComputeInlineSizeForFragment to | |
318 // determine if it fits on this line, then perform layout with the correct | |
319 // fragmentation line. | |
320 // diff writing mode - get the inline size from performing layout. | |
321 RefPtr<NGLayoutResult> layout_result = node->Layout(float_space.get()); | |
322 | |
323 NGBoxFragment float_fragment( | |
324 float_space->WritingMode(), | |
325 toNGPhysicalBoxFragment(layout_result->PhysicalFragment().get())); | |
326 | |
327 RefPtr<NGFloatingObject> floating_object = NGFloatingObject::Create( | |
328 float_space.get(), constraint_space_, node->Style(), NGBoxStrut(), | |
329 current_opportunity_.size, layout_result->PhysicalFragment().get()); | |
330 | |
331 bool float_does_not_fit = end_position + float_fragment.InlineSize() > | |
332 current_opportunity_.InlineSize(); | |
333 // Check if we already have a pending float. That's because a float cannot be | |
334 // higher than any block or floated box generated before. | |
335 if (!container_builder_.UnpositionedFloats().isEmpty() || | |
336 float_does_not_fit) { | |
337 container_builder_.AddUnpositionedFloat(floating_object); | |
338 } else { | |
339 NGLogicalOffset origin_point = | |
340 GetOriginPointForFloats(ConstraintSpace(), content_size_); | |
341 NGLogicalOffset offset = | |
342 PositionFloat(origin_point, constraint_space_->BfcOffset(), | |
343 floating_object.get(), constraint_space_); | |
344 container_builder_.AddFloatingObject(floating_object, offset); | |
345 FindNextLayoutOpportunity(); | |
346 } | |
347 } | |
348 | |
349 void NGLineBuilder::PlaceItems( | |
350 const Vector<LineItemChunk, 32>& line_item_chunks) { | |
351 const Vector<NGLayoutInlineItem>& items = inline_box_->Items(); | |
352 | |
353 NGLineBoxFragmentBuilder line_box(inline_box_); | |
354 NGTextFragmentBuilder text_builder(inline_box_); | |
355 | |
356 // Accumulate a "strut"; a zero-width inline box with the element's font and | |
357 // line height properties. https://drafts.csswg.org/css2/visudet.html#strut | |
358 NGLineHeightMetrics block_metrics(inline_box_->Style(), baseline_type_); | |
359 line_box.UniteMetrics(block_metrics); | |
360 | |
361 // Use the block style to compute the estimated baseline position because the | |
362 // baseline position is not known until we know the maximum ascent and leading | |
363 // of the line. Items are placed on this baseline, then adjusted later if the | |
364 // estimation turned out to be different. | |
365 LayoutUnit estimated_baseline = | |
366 content_size_ + LayoutUnit(block_metrics.ascent_and_leading); | |
367 | |
368 LayoutUnit inline_size; | |
369 for (const auto& line_item_chunk : line_item_chunks) { | |
370 const NGLayoutInlineItem& item = items[line_item_chunk.index]; | |
371 // Skip bidi controls. | |
372 if (!item.GetLayoutObject()) | |
373 continue; | |
374 | |
375 LayoutUnit block_start; | |
376 if (item.Type() == NGLayoutInlineItem::kText) { | |
377 DCHECK(item.GetLayoutObject()->isText()); | |
378 const ComputedStyle* style = item.Style(); | |
379 // The direction of a fragment is the CSS direction to resolve logical | |
380 // properties, not the resolved bidi direction. | |
381 text_builder.SetDirection(style->direction()) | |
382 .SetInlineSize(line_item_chunk.inline_size); | |
383 | |
384 // |InlineTextBoxPainter| sets the baseline at |top + | |
385 // ascent-of-primary-font|. Compute |top| to match. | |
386 NGLineHeightMetrics metrics(*style, baseline_type_); | |
387 block_start = estimated_baseline - LayoutUnit(metrics.ascent); | |
388 text_builder.SetBlockSize(metrics.LineHeight()); | |
389 line_box.UniteMetrics(metrics); | |
390 | |
391 // Take all used fonts into account if 'line-height: normal'. | |
392 if (style->lineHeight().isNegative()) | |
393 AccumulateUsedFonts(item, line_item_chunk, &line_box); | |
394 } else if (item.Type() == NGLayoutInlineItem::kAtomicInline) { | |
395 block_start = | |
396 PlaceAtomicInline(item, estimated_baseline, &line_box, &text_builder); | |
397 } else if (item.Type() == NGLayoutInlineItem::kOutOfFlowPositioned) { | |
398 // TODO(layout-dev): Report the correct static position for the out of | |
399 // flow descendant. We can't do this here yet as it doesn't know the | |
400 // size of the line box. | |
401 container_builder_.AddOutOfFlowDescendant( | |
402 // Absolute positioning blockifies the box's display type. | |
403 // https://drafts.csswg.org/css-display/#transformations | |
404 new NGBlockNode(item.GetLayoutObject()), | |
405 NGStaticPosition::Create(ConstraintSpace().WritingMode(), | |
406 ConstraintSpace().Direction(), | |
407 NGPhysicalOffset())); | |
408 continue; | |
409 } else { | |
410 continue; | |
411 } | |
412 | |
413 RefPtr<NGPhysicalTextFragment> text_fragment = text_builder.ToTextFragment( | |
414 line_item_chunk.index, line_item_chunk.start_offset, | |
415 line_item_chunk.end_offset); | |
416 | |
417 NGLogicalOffset logical_offset( | |
418 inline_size + current_opportunity_.InlineStartOffset() - | |
419 ConstraintSpace().BfcOffset().inline_offset, | |
420 block_start); | |
421 line_box.AddChild(std::move(text_fragment), logical_offset); | |
422 inline_size += line_item_chunk.inline_size; | |
423 } | |
424 | |
425 if (line_box.Children().isEmpty()) { | |
426 // The line was empty. | |
427 return; | |
428 } | |
429 | |
430 // If the estimated baseline position was not the actual position, move all | |
431 // fragments in the block direction. | |
432 LayoutUnit adjust_baseline(line_box.Metrics().ascent_and_leading - | |
433 block_metrics.ascent_and_leading); | |
434 if (adjust_baseline) | |
435 line_box.MoveChildrenInBlockDirection(adjust_baseline); | |
436 | |
437 line_box.SetInlineSize(inline_size); | |
438 NGLogicalOffset offset(LayoutUnit(), content_size_); | |
439 container_builder_.AddChild(line_box.ToLineBoxFragment(), offset); | |
440 max_inline_size_ = std::max(max_inline_size_, inline_size); | |
441 content_size_ += line_box.Metrics().LineHeight(); | |
442 } | |
443 | |
444 void NGLineBuilder::AccumulateUsedFonts(const NGLayoutInlineItem& item, | |
445 const LineItemChunk& line_item_chunk, | |
446 NGLineBoxFragmentBuilder* line_box) { | |
447 HashSet<const SimpleFontData*> fallback_fonts; | |
448 item.GetFallbackFonts(&fallback_fonts, line_item_chunk.start_offset, | |
449 line_item_chunk.end_offset); | |
450 for (const auto& fallback_font : fallback_fonts) { | |
451 NGLineHeightMetrics metrics(fallback_font->getFontMetrics(), | |
452 baseline_type_); | |
453 line_box->UniteMetrics(metrics); | |
454 } | |
455 } | |
456 | |
457 LayoutUnit NGLineBuilder::PlaceAtomicInline( | |
458 const NGLayoutInlineItem& item, | |
459 LayoutUnit estimated_baseline, | |
460 NGLineBoxFragmentBuilder* line_box, | |
461 NGTextFragmentBuilder* text_builder) { | |
462 NGBoxFragment fragment( | |
463 ConstraintSpace().WritingMode(), | |
464 toNGPhysicalBoxFragment(LayoutItem(item)->PhysicalFragment().get())); | |
465 // TODO(kojii): Margin and border in block progression not implemented yet. | |
466 LayoutUnit block_size = fragment.BlockSize(); | |
467 | |
468 // TODO(kojii): Try to eliminate the wrapping text fragment and use the | |
469 // |fragment| directly. Currently |CopyFragmentDataToLayoutBlockFlow| | |
470 // requires a text fragment. | |
471 text_builder->SetInlineSize(fragment.InlineSize()).SetBlockSize(block_size); | |
472 | |
473 // TODO(kojii): Add baseline position to NGPhysicalFragment. | |
474 LayoutBox* box = toLayoutBox(item.GetLayoutObject()); | |
475 LineDirectionMode line_direction_mode = | |
476 IsHorizontalWritingMode() ? LineDirectionMode::HorizontalLine | |
477 : LineDirectionMode::VerticalLine; | |
478 bool is_first_line = container_builder_.Children().isEmpty(); | |
479 int baseline_offset = | |
480 box->baselinePosition(baseline_type_, is_first_line, line_direction_mode); | |
481 LayoutUnit block_start = estimated_baseline - baseline_offset; | |
482 | |
483 NGLineHeightMetrics metrics; | |
484 metrics.ascent_and_leading = baseline_offset; | |
485 metrics.descent_and_leading = block_size - baseline_offset; | |
486 line_box->UniteMetrics(metrics); | |
487 | |
488 // TODO(kojii): Figure out what to do with OOF in NGLayoutResult. | |
489 // Floats are ok because atomic inlines are BFC? | |
490 | |
491 return block_start; | |
492 } | |
493 | |
494 void NGLineBuilder::FindNextLayoutOpportunity() { | |
495 NGLogicalOffset iter_offset = constraint_space_->BfcOffset(); | |
496 iter_offset.block_offset += content_size_; | |
497 auto* iter = constraint_space_->LayoutOpportunityIterator(iter_offset); | |
498 NGLayoutOpportunity opportunity = iter->Next(); | |
499 if (!opportunity.IsEmpty()) | |
500 current_opportunity_ = opportunity; | |
501 } | |
502 | |
503 RefPtr<NGLayoutResult> NGLineBuilder::CreateFragments() { | |
504 DCHECK(!HasItems()) << "Must call CreateLine()"; | |
505 | |
506 // TODO(kojii): Check if the line box width should be content or available. | |
507 // TODO(kojii): Need to take constraint_space into account. | |
508 container_builder_.SetInlineSize(max_inline_size_) | |
509 .SetInlineOverflow(max_inline_size_) | |
510 .SetBlockSize(content_size_) | |
511 .SetBlockOverflow(content_size_); | |
512 | |
513 container_layout_result_ = container_builder_.ToBoxFragment(); | |
514 return container_layout_result_; | |
515 } | |
516 | |
517 void NGLineBuilder::CopyFragmentDataToLayoutBlockFlow() { | |
518 LayoutBlockFlow* block = inline_box_->GetLayoutBlockFlow(); | |
519 block->deleteLineBoxTree(); | |
520 | |
521 Vector<NGLayoutInlineItem>& items = inline_box_->Items(); | |
522 Vector<unsigned, 32> text_offsets(items.size()); | |
523 inline_box_->GetLayoutTextOffsets(&text_offsets); | |
524 | |
525 Vector<const NGPhysicalFragment*, 32> fragments_for_bidi_runs; | |
526 fragments_for_bidi_runs.reserveInitialCapacity(items.size()); | |
527 BidiRunList<BidiRun> bidi_runs; | |
528 LineInfo line_info; | |
529 NGPhysicalBoxFragment* box_fragment = toNGPhysicalBoxFragment( | |
530 container_layout_result_->PhysicalFragment().get()); | |
531 for (const auto& container_child : box_fragment->Children()) { | |
532 NGPhysicalLineBoxFragment* physical_line_box = | |
533 toNGPhysicalLineBoxFragment(container_child.get()); | |
534 // Create a BidiRunList for this line. | |
535 for (const auto& line_child : physical_line_box->Children()) { | |
536 const auto* text_fragment = toNGPhysicalTextFragment(line_child.get()); | |
537 const NGLayoutInlineItem& item = items[text_fragment->ItemIndex()]; | |
538 BidiRun* run; | |
539 if (item.Type() == NGLayoutInlineItem::kText) { | |
540 LayoutObject* layout_object = item.GetLayoutObject(); | |
541 DCHECK(layout_object->isText()); | |
542 unsigned text_offset = text_offsets[text_fragment->ItemIndex()]; | |
543 run = new BidiRun(text_fragment->StartOffset() - text_offset, | |
544 text_fragment->EndOffset() - text_offset, | |
545 item.BidiLevel(), LineLayoutItem(layout_object)); | |
546 layout_object->clearNeedsLayout(); | |
547 } else if (item.Type() == NGLayoutInlineItem::kAtomicInline) { | |
548 LayoutObject* layout_object = item.GetLayoutObject(); | |
549 DCHECK(layout_object->isAtomicInlineLevel()); | |
550 run = | |
551 new BidiRun(0, 1, item.BidiLevel(), LineLayoutItem(layout_object)); | |
552 } else { | |
553 continue; | |
554 } | |
555 bidi_runs.addRun(run); | |
556 fragments_for_bidi_runs.push_back(text_fragment); | |
557 } | |
558 // TODO(kojii): bidi needs to find the logical last run. | |
559 bidi_runs.setLogicallyLastRun(bidi_runs.lastRun()); | |
560 | |
561 // Create a RootInlineBox from BidiRunList. InlineBoxes created for the | |
562 // RootInlineBox are set to Bidirun::m_box. | |
563 line_info.setEmpty(false); | |
564 // TODO(kojii): Implement setFirstLine, LastLine, etc. | |
565 RootInlineBox* root_line_box = block->constructLine(bidi_runs, line_info); | |
566 | |
567 // Copy fragments data to InlineBoxes. | |
568 DCHECK_EQ(fragments_for_bidi_runs.size(), bidi_runs.runCount()); | |
569 BidiRun* run = bidi_runs.firstRun(); | |
570 for (auto* physical_fragment : fragments_for_bidi_runs) { | |
571 DCHECK(run); | |
572 NGTextFragment fragment(ConstraintSpace().WritingMode(), | |
573 toNGPhysicalTextFragment(physical_fragment)); | |
574 InlineBox* inline_box = run->m_box; | |
575 inline_box->setLogicalWidth(fragment.InlineSize()); | |
576 inline_box->setLogicalLeft(fragment.InlineOffset()); | |
577 inline_box->setLogicalTop(fragment.BlockOffset()); | |
578 if (inline_box->getLineLayoutItem().isBox()) { | |
579 LineLayoutBox box(inline_box->getLineLayoutItem()); | |
580 box.setLocation(inline_box->location()); | |
581 } | |
582 run = run->next(); | |
583 } | |
584 DCHECK(!run); | |
585 | |
586 // Copy to RootInlineBox. | |
587 NGLineBoxFragment line_box(ConstraintSpace().WritingMode(), | |
588 physical_line_box); | |
589 root_line_box->setLogicalWidth(line_box.InlineSize()); | |
590 LayoutUnit line_top_with_leading = line_box.BlockOffset(); | |
591 root_line_box->setLogicalTop(line_top_with_leading); | |
592 const NGLineHeightMetrics& metrics = physical_line_box->Metrics(); | |
593 LayoutUnit baseline = | |
594 line_top_with_leading + LayoutUnit(metrics.ascent_and_leading); | |
595 root_line_box->setLineTopBottomPositions( | |
596 baseline - LayoutUnit(metrics.ascent), | |
597 baseline + LayoutUnit(metrics.descent), line_top_with_leading, | |
598 baseline + LayoutUnit(metrics.descent_and_leading)); | |
599 | |
600 bidi_runs.deleteRuns(); | |
601 fragments_for_bidi_runs.clear(); | |
602 } | |
603 } | |
604 } // namespace blink | |
OLD | NEW |