| 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 |