OLD | NEW |
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/ng_block_layout_algorithm.h" | 5 #include "core/layout/ng/ng_block_layout_algorithm.h" |
6 | 6 |
7 #include "core/layout/ng/ng_absolute_utils.h" | 7 #include "core/layout/ng/ng_absolute_utils.h" |
8 #include "core/layout/ng/ng_block_break_token.h" | 8 #include "core/layout/ng/ng_block_break_token.h" |
| 9 #include "core/layout/ng/ng_block_child_iterator.h" |
9 #include "core/layout/ng/ng_box_fragment.h" | 10 #include "core/layout/ng/ng_box_fragment.h" |
10 #include "core/layout/ng/ng_column_mapper.h" | |
11 #include "core/layout/ng/ng_constraint_space.h" | 11 #include "core/layout/ng/ng_constraint_space.h" |
12 #include "core/layout/ng/ng_constraint_space_builder.h" | 12 #include "core/layout/ng/ng_constraint_space_builder.h" |
13 #include "core/layout/ng/ng_fragment.h" | 13 #include "core/layout/ng/ng_fragment.h" |
14 #include "core/layout/ng/ng_fragment_builder.h" | 14 #include "core/layout/ng/ng_fragment_builder.h" |
15 #include "core/layout/ng/ng_inline_node.h" | 15 #include "core/layout/ng/ng_inline_node.h" |
16 #include "core/layout/ng/ng_layout_opportunity_iterator.h" | 16 #include "core/layout/ng/ng_layout_opportunity_iterator.h" |
17 #include "core/layout/ng/ng_length_utils.h" | 17 #include "core/layout/ng/ng_length_utils.h" |
18 #include "core/layout/ng/ng_line_builder.h" | 18 #include "core/layout/ng/ng_line_builder.h" |
19 #include "core/layout/ng/ng_out_of_flow_layout_part.h" | 19 #include "core/layout/ng/ng_out_of_flow_layout_part.h" |
20 #include "core/layout/ng/ng_units.h" | 20 #include "core/layout/ng/ng_units.h" |
(...skipping 266 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
287 if (display == EDisplay::Grid || display == EDisplay::Flex || | 287 if (display == EDisplay::Grid || display == EDisplay::Flex || |
288 display == EDisplay::WebkitBox) | 288 display == EDisplay::WebkitBox) |
289 return true; | 289 return true; |
290 | 290 |
291 if (space.WritingMode() != FromPlatformWritingMode(style.getWritingMode())) | 291 if (space.WritingMode() != FromPlatformWritingMode(style.getWritingMode())) |
292 return true; | 292 return true; |
293 | 293 |
294 return false; | 294 return false; |
295 } | 295 } |
296 | 296 |
| 297 // Whether we've run out of space in this flow. If so, there will be no work |
| 298 // left to do for this block in this fragmentainer. |
| 299 bool IsOutOfSpace(const NGConstraintSpace& space, LayoutUnit content_size) { |
| 300 return space.HasBlockFragmentation() && |
| 301 content_size >= space.FragmentainerSpaceAvailable(); |
| 302 } |
| 303 |
297 } // namespace | 304 } // namespace |
298 | 305 |
299 NGBlockLayoutAlgorithm::NGBlockLayoutAlgorithm( | 306 NGBlockLayoutAlgorithm::NGBlockLayoutAlgorithm( |
300 NGBlockNode* node, | 307 NGBlockNode* node, |
301 NGConstraintSpace* constraint_space, | 308 NGConstraintSpace* constraint_space, |
302 NGBreakToken* break_token) | 309 NGBlockBreakToken* break_token) |
303 : node_(node), | 310 : node_(node), |
304 constraint_space_(constraint_space), | 311 constraint_space_(constraint_space), |
305 break_token_(break_token), | 312 break_token_(break_token), |
306 builder_(WTF::wrapUnique( | 313 builder_(WTF::wrapUnique( |
307 new NGFragmentBuilder(NGPhysicalFragment::kFragmentBox, node))) {} | 314 new NGFragmentBuilder(NGPhysicalFragment::kFragmentBox, node))) {} |
308 | 315 |
309 Optional<MinAndMaxContentSizes> | 316 Optional<MinAndMaxContentSizes> |
310 NGBlockLayoutAlgorithm::ComputeMinAndMaxContentSizes() const { | 317 NGBlockLayoutAlgorithm::ComputeMinAndMaxContentSizes() const { |
311 MinAndMaxContentSizes sizes; | 318 MinAndMaxContentSizes sizes; |
312 | 319 |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
360 } | 367 } |
361 builder_->SetBfcOffset(bfc_offset); | 368 builder_->SetBfcOffset(bfc_offset); |
362 } | 369 } |
363 } | 370 } |
364 | 371 |
365 RefPtr<NGLayoutResult> NGBlockLayoutAlgorithm::Layout() { | 372 RefPtr<NGLayoutResult> NGBlockLayoutAlgorithm::Layout() { |
366 WTF::Optional<MinAndMaxContentSizes> sizes; | 373 WTF::Optional<MinAndMaxContentSizes> sizes; |
367 if (NeedMinAndMaxContentSizes(ConstraintSpace(), Style())) | 374 if (NeedMinAndMaxContentSizes(ConstraintSpace(), Style())) |
368 sizes = ComputeMinAndMaxContentSizes(); | 375 sizes = ComputeMinAndMaxContentSizes(); |
369 | 376 |
370 border_and_padding_ = | 377 border_and_padding_ = ComputeBorders(ConstraintSpace(), Style()) + |
371 ComputeBorders(Style()) + ComputePadding(ConstraintSpace(), Style()); | 378 ComputePadding(ConstraintSpace(), Style()); |
372 | 379 |
373 LayoutUnit inline_size = | 380 LayoutUnit inline_size = |
374 ComputeInlineSizeForFragment(ConstraintSpace(), Style(), sizes); | 381 ComputeInlineSizeForFragment(ConstraintSpace(), Style(), sizes); |
375 LayoutUnit adjusted_inline_size = | 382 LayoutUnit adjusted_inline_size = |
376 inline_size - border_and_padding_.InlineSum(); | 383 inline_size - border_and_padding_.InlineSum(); |
377 // TODO(layout-ng): For quirks mode, should we pass blockSize instead of | 384 // TODO(layout-ng): For quirks mode, should we pass blockSize instead of |
378 // -1? | 385 // -1? |
379 LayoutUnit block_size = | 386 LayoutUnit block_size = |
380 ComputeBlockSizeForFragment(ConstraintSpace(), Style(), NGSizeIndefinite); | 387 ComputeBlockSizeForFragment(ConstraintSpace(), Style(), NGSizeIndefinite); |
381 LayoutUnit adjusted_block_size(block_size); | 388 LayoutUnit adjusted_block_size(block_size); |
382 // Our calculated block-axis size may be indefinite at this point. | 389 // Our calculated block-axis size may be indefinite at this point. |
383 // If so, just leave the size as NGSizeIndefinite instead of subtracting | 390 // If so, just leave the size as NGSizeIndefinite instead of subtracting |
384 // borders and padding. | 391 // borders and padding. |
385 if (adjusted_block_size != NGSizeIndefinite) | 392 if (adjusted_block_size != NGSizeIndefinite) |
386 adjusted_block_size -= border_and_padding_.BlockSum(); | 393 adjusted_block_size -= border_and_padding_.BlockSum(); |
387 | 394 |
388 space_builder_ = new NGConstraintSpaceBuilder(constraint_space_); | 395 space_builder_ = new NGConstraintSpaceBuilder(constraint_space_); |
389 if (Style().specifiesColumns()) { | 396 space_builder_ |
390 space_builder_->SetFragmentationType(kFragmentColumn); | 397 ->SetAvailableSize( |
391 adjusted_inline_size = | 398 NGLogicalSize(adjusted_inline_size, adjusted_block_size)) |
392 ResolveUsedColumnInlineSize(adjusted_inline_size, Style()); | 399 .SetPercentageResolutionSize( |
393 LayoutUnit inline_progression = | 400 NGLogicalSize(adjusted_inline_size, adjusted_block_size)); |
394 adjusted_inline_size + ResolveUsedColumnGap(Style()); | |
395 fragmentainer_mapper_ = | |
396 new NGColumnMapper(inline_progression, adjusted_block_size); | |
397 } | |
398 space_builder_->SetAvailableSize( | |
399 NGLogicalSize(adjusted_inline_size, adjusted_block_size)); | |
400 space_builder_->SetPercentageResolutionSize( | |
401 NGLogicalSize(adjusted_inline_size, adjusted_block_size)); | |
402 | 401 |
403 builder_->SetDirection(constraint_space_->Direction()); | 402 builder_->SetDirection(constraint_space_->Direction()); |
404 builder_->SetWritingMode(constraint_space_->WritingMode()); | 403 builder_->SetWritingMode(constraint_space_->WritingMode()); |
405 builder_->SetInlineSize(inline_size).SetBlockSize(block_size); | 404 builder_->SetInlineSize(inline_size).SetBlockSize(block_size); |
406 | 405 |
407 // TODO(glebl): fix multicol after the new margin collapsing/floats algorithm | 406 NGBlockChildIterator child_iterator(node_->FirstChild(), break_token_); |
408 // based on BFCOffset is checked in. | 407 NGBlockChildIterator::Entry entry = child_iterator.NextChild(); |
409 if (NGBlockBreakToken* token = CurrentBlockBreakToken()) { | 408 current_child_ = entry.node; |
410 // Resume after a previous break. | 409 NGBreakToken* child_break_token = entry.token; |
411 content_size_ = token->BreakOffset(); | 410 |
412 current_child_ = token->InputNode(); | 411 // If we are resuming from a break token our start border and padding is |
413 } else { | 412 // within a previous fragment. |
414 content_size_ = border_and_padding_.block_start; | 413 content_size_ = break_token_ ? LayoutUnit() : border_and_padding_.block_start; |
415 current_child_ = node_->FirstChild(); | |
416 } | |
417 | 414 |
418 curr_margin_strut_ = ConstraintSpace().MarginStrut(); | 415 curr_margin_strut_ = ConstraintSpace().MarginStrut(); |
419 curr_bfc_offset_ = ConstraintSpace().BfcOffset(); | 416 curr_bfc_offset_ = ConstraintSpace().BfcOffset(); |
420 | 417 |
421 // Margins collapsing: | 418 // Margins collapsing: |
422 // Do not collapse margins between parent and its child if there is | 419 // Do not collapse margins between parent and its child if there is |
423 // border/padding between them. | 420 // border/padding between them. |
424 if (border_and_padding_.block_start) { | 421 if (border_and_padding_.block_start) { |
425 curr_bfc_offset_.block_offset += curr_margin_strut_.Sum(); | 422 curr_bfc_offset_.block_offset += curr_margin_strut_.Sum(); |
426 UpdateFragmentBfcOffset(curr_bfc_offset_); | 423 UpdateFragmentBfcOffset(curr_bfc_offset_); |
(...skipping 11 matching lines...) Expand all Loading... |
438 | 435 |
439 curr_bfc_offset_.block_offset += content_size_; | 436 curr_bfc_offset_.block_offset += content_size_; |
440 | 437 |
441 while (current_child_) { | 438 while (current_child_) { |
442 if (current_child_->Type() == NGLayoutInputNode::kLegacyBlock) { | 439 if (current_child_->Type() == NGLayoutInputNode::kLegacyBlock) { |
443 NGBlockNode* current_block_child = toNGBlockNode(current_child_); | 440 NGBlockNode* current_block_child = toNGBlockNode(current_child_); |
444 EPosition position = current_block_child->Style().position(); | 441 EPosition position = current_block_child->Style().position(); |
445 if (position == EPosition::kAbsolute || position == EPosition::kFixed) { | 442 if (position == EPosition::kAbsolute || position == EPosition::kFixed) { |
446 builder_->AddOutOfFlowChildCandidate(current_block_child, | 443 builder_->AddOutOfFlowChildCandidate(current_block_child, |
447 GetChildSpaceOffset()); | 444 GetChildSpaceOffset()); |
448 current_child_ = current_block_child->NextSibling(); | 445 NGBlockChildIterator::Entry entry = child_iterator.NextChild(); |
| 446 current_child_ = entry.node; |
| 447 child_break_token = entry.token; |
449 continue; | 448 continue; |
450 } | 449 } |
451 } | 450 } |
452 | 451 |
453 DCHECK(!ConstraintSpace().HasBlockFragmentation() || | |
454 SpaceAvailableForCurrentChild() > LayoutUnit()); | |
455 space_for_current_child_ = CreateConstraintSpaceForCurrentChild(); | 452 space_for_current_child_ = CreateConstraintSpaceForCurrentChild(); |
456 | 453 |
457 if (current_child_->Type() == NGLayoutInputNode::kLegacyInline) { | 454 if (current_child_->Type() == NGLayoutInputNode::kLegacyInline) { |
458 LayoutInlineChildren(toNGInlineNode(current_child_)); | 455 LayoutInlineChildren(toNGInlineNode(current_child_)); |
459 continue; | 456 continue; |
460 } | 457 } |
461 | 458 |
462 RefPtr<NGLayoutResult> layout_result = | 459 RefPtr<NGLayoutResult> layout_result = |
463 current_child_->Layout(space_for_current_child_); | 460 current_child_->Layout(space_for_current_child_, child_break_token); |
464 | 461 |
465 FinishCurrentChildLayout(layout_result); | 462 FinishCurrentChildLayout(layout_result); |
466 | 463 |
467 if (!ProceedToNextUnfinishedSibling( | 464 entry = child_iterator.NextChild(); |
468 layout_result->PhysicalFragment().get())) | 465 current_child_ = entry.node; |
| 466 child_break_token = entry.token; |
| 467 |
| 468 if (IsOutOfSpace(ConstraintSpace(), content_size_)) |
469 break; | 469 break; |
470 } | 470 } |
471 | 471 |
472 // Margins collapsing: | 472 // Margins collapsing: |
473 // Bottom margins of an in-flow block box doesn't collapse with its last | 473 // Bottom margins of an in-flow block box doesn't collapse with its last |
474 // in-flow block-level child's bottom margin if the box has bottom | 474 // in-flow block-level child's bottom margin if the box has bottom |
475 // border/padding. | 475 // border/padding. |
476 content_size_ += border_and_padding_.block_end; | 476 content_size_ += border_and_padding_.block_end; |
477 if (border_and_padding_.block_end || | 477 if (border_and_padding_.block_end || |
478 ConstraintSpace().IsNewFormattingContext()) { | 478 ConstraintSpace().IsNewFormattingContext()) { |
479 content_size_ += curr_margin_strut_.Sum(); | 479 content_size_ += curr_margin_strut_.Sum(); |
480 curr_margin_strut_ = NGMarginStrut(); | 480 curr_margin_strut_ = NGMarginStrut(); |
481 } | 481 } |
482 | 482 |
483 // Recompute the block-axis size now that we know our content size. | 483 // Recompute the block-axis size now that we know our content size. |
484 block_size = | 484 block_size = |
485 ComputeBlockSizeForFragment(ConstraintSpace(), Style(), content_size_); | 485 ComputeBlockSizeForFragment(ConstraintSpace(), Style(), content_size_); |
486 builder_->SetBlockSize(block_size); | 486 builder_->SetBlockSize(block_size); |
487 | 487 |
488 // Layout our absolute and fixed positioned children. | 488 // Layout our absolute and fixed positioned children. |
489 NGOutOfFlowLayoutPart(Style(), builder_.get()).Run(); | 489 NGOutOfFlowLayoutPart(ConstraintSpace(), Style(), builder_.get()).Run(); |
490 | 490 |
491 // Non-empty blocks always know their position in space: | 491 // Non-empty blocks always know their position in space: |
492 if (block_size) { | 492 if (block_size) { |
493 curr_bfc_offset_.block_offset += curr_margin_strut_.Sum(); | 493 curr_bfc_offset_.block_offset += curr_margin_strut_.Sum(); |
494 UpdateFragmentBfcOffset(curr_bfc_offset_); | 494 UpdateFragmentBfcOffset(curr_bfc_offset_); |
495 PositionPendingFloats(curr_bfc_offset_.block_offset, ConstraintSpace(), | 495 PositionPendingFloats(curr_bfc_offset_.block_offset, ConstraintSpace(), |
496 builder_.get()); | 496 builder_.get()); |
497 } | 497 } |
498 | 498 |
499 // Margins collapsing: | 499 // Margins collapsing: |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
559 PositionPendingFloats(origin_point.block_offset, ConstraintSpace(), | 559 PositionPendingFloats(origin_point.block_offset, ConstraintSpace(), |
560 builder_.get()); | 560 builder_.get()); |
561 } | 561 } |
562 return; | 562 return; |
563 } | 563 } |
564 | 564 |
565 // Determine the fragment's position in the parent space either by using | 565 // Determine the fragment's position in the parent space either by using |
566 // content_size_ or known fragment's BFC offset. | 566 // content_size_ or known fragment's BFC offset. |
567 WTF::Optional<NGLogicalOffset> bfc_offset; | 567 WTF::Optional<NGLogicalOffset> bfc_offset; |
568 if (CurrentChildConstraintSpace().IsNewFormattingContext()) { | 568 if (CurrentChildConstraintSpace().IsNewFormattingContext()) { |
| 569 // TODO(ikilpatrick): We may need to place ourself within the BFC |
| 570 // before a new formatting context child is laid out. (Not after layout as |
| 571 // is done here). |
569 curr_bfc_offset_.block_offset += curr_margin_strut_.Sum(); | 572 curr_bfc_offset_.block_offset += curr_margin_strut_.Sum(); |
570 bfc_offset = curr_bfc_offset_; | 573 bfc_offset = curr_bfc_offset_; |
571 } else if (fragment.BfcOffset()) { | 574 } else if (fragment.BfcOffset()) { |
572 // Fragment that knows its offset can be used to set parent's BFC position. | 575 // Fragment that knows its offset can be used to set parent's BFC position. |
573 curr_bfc_offset_.block_offset = fragment.BfcOffset().value().block_offset; | 576 curr_bfc_offset_.block_offset = fragment.BfcOffset().value().block_offset; |
574 bfc_offset = curr_bfc_offset_; | 577 bfc_offset = curr_bfc_offset_; |
575 } else if (builder_->BfcOffset()) { | 578 } else if (builder_->BfcOffset()) { |
576 // Fragment doesn't know its offset but we can still calculate its BFC | 579 // Fragment doesn't know its offset but we can still calculate its BFC |
577 // position because the parent fragment's BFC is known. | 580 // position because the parent fragment's BFC is known. |
578 // Example: | 581 // Example: |
579 // BFC Offset is known here because of the padding. | 582 // BFC Offset is known here because of the padding. |
580 // <div style="padding: 1px"> | 583 // <div style="padding: 1px"> |
581 // <div id="empty-div" style="margins: 1px"></div> | 584 // <div id="empty-div" style="margins: 1px"></div> |
582 bfc_offset = curr_bfc_offset_; | 585 bfc_offset = curr_bfc_offset_; |
583 bfc_offset.value().block_offset += curr_margin_strut_.Sum(); | 586 bfc_offset.value().block_offset += curr_margin_strut_.Sum(); |
584 } | 587 } |
585 if (bfc_offset) { | 588 if (bfc_offset) { |
586 UpdateFragmentBfcOffset(curr_bfc_offset_); | 589 UpdateFragmentBfcOffset(curr_bfc_offset_); |
587 PositionPendingFloats(curr_bfc_offset_.block_offset, ConstraintSpace(), | 590 PositionPendingFloats(curr_bfc_offset_.block_offset, ConstraintSpace(), |
588 builder_.get()); | 591 builder_.get()); |
589 } | 592 } |
590 NGLogicalOffset logical_offset = CalculateLogicalOffset(bfc_offset); | 593 NGLogicalOffset logical_offset = CalculateLogicalOffset(bfc_offset); |
591 | 594 |
592 if (fragmentainer_mapper_) | |
593 fragmentainer_mapper_->ToVisualOffset(logical_offset); | |
594 else | |
595 logical_offset.block_offset -= PreviousBreakOffset(); | |
596 | |
597 // Update margin strut. | 595 // Update margin strut. |
598 curr_margin_strut_ = fragment.EndMarginStrut(); | 596 curr_margin_strut_ = fragment.EndMarginStrut(); |
599 curr_margin_strut_.Append(curr_child_margins_.block_end); | 597 curr_margin_strut_.Append(curr_child_margins_.block_end); |
600 | 598 |
601 // Only modify content_size if BlockSize is not empty. It's needed to prevent | 599 // Only modify content_size if BlockSize is not empty. It's needed to prevent |
602 // the situation when logical_offset is included in content_size for empty | 600 // the situation when logical_offset is included in content_size for empty |
603 // blocks. Example: | 601 // blocks. Example: |
604 // <div style="overflow:hidden"> | 602 // <div style="overflow:hidden"> |
605 // <div style="margin-top: 8px"></div> | 603 // <div style="margin-top: 8px"></div> |
606 // <div style="margin-top: 10px"></div> | 604 // <div style="margin-top: 10px"></div> |
607 // </div> | 605 // </div> |
608 if (fragment.BlockSize()) | 606 if (fragment.BlockSize()) |
609 content_size_ = fragment.BlockSize() + logical_offset.block_offset; | 607 content_size_ = fragment.BlockSize() + logical_offset.block_offset; |
610 max_inline_size_ = | 608 max_inline_size_ = |
611 std::max(max_inline_size_, fragment.InlineSize() + | 609 std::max(max_inline_size_, fragment.InlineSize() + |
612 curr_child_margins_.InlineSum() + | 610 curr_child_margins_.InlineSum() + |
613 border_and_padding_.InlineSum()); | 611 border_and_padding_.InlineSum()); |
614 | 612 |
615 builder_->AddChild(layout_result, logical_offset); | 613 builder_->AddChild(layout_result, logical_offset); |
616 } | 614 } |
617 | 615 |
618 bool NGBlockLayoutAlgorithm::ProceedToNextUnfinishedSibling( | 616 void NGBlockLayoutAlgorithm::FinalizeForFragmentation() { |
619 NGPhysicalFragment* child_fragment) { | 617 LayoutUnit used_block_size = |
620 DCHECK(current_child_); | 618 break_token_ ? break_token_->UsedBlockSize() : LayoutUnit(); |
621 NGBlockNode* finished_child = toNGBlockNode(current_child_); | 619 LayoutUnit block_size = ComputeBlockSizeForFragment( |
622 current_child_ = current_child_->NextSibling(); | 620 ConstraintSpace(), Style(), used_block_size + content_size_); |
623 if (!ConstraintSpace().HasBlockFragmentation() && !fragmentainer_mapper_) | |
624 return true; | |
625 // If we're resuming layout after a fragmentainer break, we need to skip | |
626 // siblings that we're done with. We may have been able to fully lay out some | |
627 // node(s) preceding a node that we had to break inside (and therefore were | |
628 // not able to fully lay out). This happens when we have parallel flows [1], | |
629 // which are caused by floats, overflow, etc. | |
630 // | |
631 // [1] https://drafts.csswg.org/css-break/#parallel-flows | |
632 if (CurrentBlockBreakToken()) { | |
633 // TODO(layout-ng): Figure out if we need a better way to determine if the | |
634 // node is finished. Maybe something to encode in a break token? | |
635 // TODO(kojii): Handle inline children. | |
636 while (current_child_ && | |
637 current_child_->Type() == NGLayoutInputNode::kLegacyBlock && | |
638 toNGBlockNode(current_child_)->IsLayoutFinished()) { | |
639 current_child_ = current_child_->NextSibling(); | |
640 } | |
641 } | |
642 LayoutUnit break_offset = NextBreakOffset(); | |
643 bool is_out_of_space = content_size_ - PreviousBreakOffset() >= break_offset; | |
644 if (!HasPendingBreakToken()) { | |
645 bool child_broke = child_fragment->BreakToken(); | |
646 // This block needs to break if the child broke, or if we're out of space | |
647 // and there's more content waiting to be laid out. Otherwise, just bail | |
648 // now. | |
649 if (!child_broke && (!is_out_of_space || !current_child_)) | |
650 return true; | |
651 // Prepare a break token for this block, so that we know where to resume | |
652 // when the time comes for that. We may not be able to abort layout of this | |
653 // block right away, due to the posibility of parallel flows. We can only | |
654 // abort when we're out of space, or when there are no siblings left to | |
655 // process. | |
656 NGBlockBreakToken* token; | |
657 if (child_broke) { | |
658 // The child we just laid out was the first one to break. So that is | |
659 // where we need to resume. | |
660 token = new NGBlockBreakToken(finished_child, break_offset); | |
661 } else { | |
662 // Resume layout at the next sibling that needs layout. | |
663 DCHECK(current_child_); | |
664 token = | |
665 new NGBlockBreakToken(toNGBlockNode(current_child_), break_offset); | |
666 } | |
667 SetPendingBreakToken(token); | |
668 } | |
669 | 621 |
670 if (!fragmentainer_mapper_) { | 622 block_size -= used_block_size; |
671 if (!is_out_of_space) | 623 DCHECK_GE(block_size, LayoutUnit()) |
672 return true; | 624 << "Adding and subtracting the used_block_size shouldn't leave the " |
673 // We have run out of space in this flow, so there's no work left to do for | 625 "block_size for this fragment smaller than zero."; |
674 // this block in this fragmentainer. We should finalize the fragment and get | |
675 // back to the remaining content when laying out the next fragmentainer(s). | |
676 return false; | |
677 } | |
678 | 626 |
679 if (is_out_of_space || !current_child_) { | 627 DCHECK(builder_->BfcOffset()) << "We must have our BfcOffset by this point " |
680 NGBlockBreakToken* token = fragmentainer_mapper_->Advance(); | 628 "to determine the space left in the flow."; |
681 DCHECK(token || !is_out_of_space); | 629 LayoutUnit space_left = ConstraintSpace().FragmentainerSpaceAvailable() - |
682 if (token) { | 630 builder_->BfcOffset().value().block_offset; |
683 break_token_ = token; | 631 DCHECK_GE(space_left, LayoutUnit()); |
684 content_size_ = token->BreakOffset(); | |
685 current_child_ = token->InputNode(); | |
686 } | |
687 } | |
688 return true; | |
689 } | |
690 | 632 |
691 void NGBlockLayoutAlgorithm::SetPendingBreakToken(NGBlockBreakToken* token) { | 633 if (builder_->DidBreak()) { |
692 if (fragmentainer_mapper_) | 634 // One of our children broke. Even if we fit within the remaining space we |
693 fragmentainer_mapper_->SetBreakToken(token); | 635 // need to prepare a break token. |
694 else | 636 builder_->SetUsedBlockSize(std::min(space_left, block_size) + |
695 builder_->SetBreakToken(token); | 637 used_block_size); |
696 } | |
697 | |
698 bool NGBlockLayoutAlgorithm::HasPendingBreakToken() const { | |
699 if (fragmentainer_mapper_) | |
700 return fragmentainer_mapper_->HasBreakToken(); | |
701 return builder_->HasBreakToken(); | |
702 } | |
703 | |
704 void NGBlockLayoutAlgorithm::FinalizeForFragmentation() { | |
705 LayoutUnit block_size = | |
706 ComputeBlockSizeForFragment(ConstraintSpace(), Style(), content_size_); | |
707 LayoutUnit previous_break_offset = PreviousBreakOffset(); | |
708 block_size -= previous_break_offset; | |
709 block_size = std::max(LayoutUnit(), block_size); | |
710 LayoutUnit space_left = ConstraintSpace().FragmentainerSpaceAvailable(); | |
711 DCHECK_GE(space_left, LayoutUnit()); | |
712 if (builder_->HasBreakToken()) { | |
713 // A break token is ready, which means that we're going to break | |
714 // before or inside a block-level child. | |
715 builder_->SetBlockSize(std::min(space_left, block_size)); | 638 builder_->SetBlockSize(std::min(space_left, block_size)); |
716 builder_->SetBlockOverflow(space_left); | 639 builder_->SetBlockOverflow(space_left); |
717 return; | 640 return; |
718 } | 641 } |
| 642 |
719 if (block_size > space_left) { | 643 if (block_size > space_left) { |
720 // Need a break inside this block. | 644 // Need a break inside this block. |
721 builder_->SetBreakToken(new NGBlockBreakToken(nullptr, NextBreakOffset())); | 645 builder_->SetUsedBlockSize(space_left + used_block_size); |
722 builder_->SetBlockSize(space_left); | 646 builder_->SetBlockSize(space_left); |
723 builder_->SetBlockOverflow(space_left); | 647 builder_->SetBlockOverflow(space_left); |
724 return; | 648 return; |
725 } | 649 } |
| 650 |
726 // The end of the block fits in the current fragmentainer. | 651 // The end of the block fits in the current fragmentainer. |
727 builder_->SetBlockSize(block_size); | 652 builder_->SetBlockSize(block_size); |
728 builder_->SetBlockOverflow(content_size_ - previous_break_offset); | 653 builder_->SetBlockOverflow(content_size_); |
729 } | |
730 | |
731 NGBlockBreakToken* NGBlockLayoutAlgorithm::CurrentBlockBreakToken() const { | |
732 NGBreakToken* token = break_token_; | |
733 if (!token || token->Type() != NGBreakToken::kBlockBreakToken) | |
734 return nullptr; | |
735 return toNGBlockBreakToken(token); | |
736 } | |
737 | |
738 LayoutUnit NGBlockLayoutAlgorithm::PreviousBreakOffset() const { | |
739 const NGBlockBreakToken* token = CurrentBlockBreakToken(); | |
740 return token ? token->BreakOffset() : LayoutUnit(); | |
741 } | |
742 | |
743 LayoutUnit NGBlockLayoutAlgorithm::NextBreakOffset() const { | |
744 if (fragmentainer_mapper_) | |
745 return fragmentainer_mapper_->NextBreakOffset(); | |
746 DCHECK(ConstraintSpace().HasBlockFragmentation()); | |
747 return PreviousBreakOffset() + | |
748 ConstraintSpace().FragmentainerSpaceAvailable(); | |
749 } | |
750 | |
751 LayoutUnit NGBlockLayoutAlgorithm::SpaceAvailableForCurrentChild() const { | |
752 LayoutUnit space_left; | |
753 if (fragmentainer_mapper_) | |
754 space_left = fragmentainer_mapper_->BlockSize(); | |
755 else if (ConstraintSpace().HasBlockFragmentation()) | |
756 space_left = ConstraintSpace().FragmentainerSpaceAvailable(); | |
757 else | |
758 return NGSizeIndefinite; | |
759 space_left -= BorderEdgeForCurrentChild() - PreviousBreakOffset(); | |
760 return space_left; | |
761 } | 654 } |
762 | 655 |
763 NGBoxStrut NGBlockLayoutAlgorithm::CalculateMargins( | 656 NGBoxStrut NGBlockLayoutAlgorithm::CalculateMargins( |
764 const NGConstraintSpace& space, | 657 const NGConstraintSpace& space, |
765 const ComputedStyle& style) { | 658 const ComputedStyle& style) { |
766 WTF::Optional<MinAndMaxContentSizes> sizes; | 659 WTF::Optional<MinAndMaxContentSizes> sizes; |
767 if (NeedMinAndMaxContentSizes(space, style)) { | 660 if (NeedMinAndMaxContentSizes(space, style)) { |
768 // TODO(ikilpatrick): Change ComputeMinAndMaxContentSizes to return | 661 // TODO(ikilpatrick): Change ComputeMinAndMaxContentSizes to return |
769 // MinAndMaxContentSizes. | 662 // MinAndMaxContentSizes. |
770 sizes = toNGBlockNode(current_child_)->ComputeMinAndMaxContentSizes(); | 663 sizes = toNGBlockNode(current_child_)->ComputeMinAndMaxContentSizes(); |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
802 CurrentChildStyle()); | 695 CurrentChildStyle()); |
803 | 696 |
804 const ComputedStyle& current_child_style = CurrentChildStyle(); | 697 const ComputedStyle& current_child_style = CurrentChildStyle(); |
805 | 698 |
806 bool is_new_bfc = IsNewFormattingContextForInFlowBlockLevelChild( | 699 bool is_new_bfc = IsNewFormattingContextForInFlowBlockLevelChild( |
807 ConstraintSpace(), current_child_style); | 700 ConstraintSpace(), current_child_style); |
808 space_builder_->SetIsNewFormattingContext(is_new_bfc) | 701 space_builder_->SetIsNewFormattingContext(is_new_bfc) |
809 .SetIsShrinkToFit( | 702 .SetIsShrinkToFit( |
810 ShouldShrinkToFit(ConstraintSpace(), CurrentChildStyle())) | 703 ShouldShrinkToFit(ConstraintSpace(), CurrentChildStyle())) |
811 .SetTextDirection(current_child_style.direction()); | 704 .SetTextDirection(current_child_style.direction()); |
812 LayoutUnit space_available = SpaceAvailableForCurrentChild(); | |
813 space_builder_->SetFragmentainerSpaceAvailable(space_available); | |
814 | 705 |
815 // Clearance : | 706 // Clearance : |
816 // - *Always* collapse margins and update *container*'s BFC offset. | 707 // - *Always* collapse margins and update *container*'s BFC offset. |
817 // - Position all pending floats since the fragment's BFC offset is known. | 708 // - Position all pending floats since the fragment's BFC offset is known. |
818 // - Set the clearance offset on the constraint space's builder. | 709 // - Set the clearance offset on the constraint space's builder. |
819 if (current_child_style.clear() != EClear::kNone) { | 710 if (current_child_style.clear() != EClear::kNone) { |
820 curr_bfc_offset_.block_offset += curr_margin_strut_.Sum(); | 711 curr_bfc_offset_.block_offset += curr_margin_strut_.Sum(); |
821 UpdateFragmentBfcOffset(curr_bfc_offset_); | 712 UpdateFragmentBfcOffset(curr_bfc_offset_); |
822 // Only collapse margins if it's an adjoining block with clearance. | 713 // Only collapse margins if it's an adjoining block with clearance. |
823 if (!content_size_) { | 714 if (!content_size_) { |
(...skipping 20 matching lines...) Expand all Loading... |
844 curr_bfc_offset_.inline_offset += curr_child_margins_.inline_start; | 735 curr_bfc_offset_.inline_offset += curr_child_margins_.inline_start; |
845 // Append the current margin strut with child's block start margin. | 736 // Append the current margin strut with child's block start margin. |
846 // Non empty border/padding use cases are handled inside of the child's | 737 // Non empty border/padding use cases are handled inside of the child's |
847 // layout. | 738 // layout. |
848 curr_margin_strut_.Append(curr_child_margins_.block_start); | 739 curr_margin_strut_.Append(curr_child_margins_.block_start); |
849 space_builder_->SetMarginStrut(curr_margin_strut_); | 740 space_builder_->SetMarginStrut(curr_margin_strut_); |
850 } | 741 } |
851 | 742 |
852 space_builder_->SetBfcOffset(curr_bfc_offset_); | 743 space_builder_->SetBfcOffset(curr_bfc_offset_); |
853 | 744 |
| 745 LayoutUnit space_available; |
| 746 if (constraint_space_->HasBlockFragmentation()) { |
| 747 space_available = ConstraintSpace().FragmentainerSpaceAvailable(); |
| 748 // If a block establishes a new formatting context we must know our |
| 749 // position in the formatting context, and are able to adjust the |
| 750 // fragmentation line. |
| 751 if (is_new_bfc) { |
| 752 DCHECK(builder_->BfcOffset()); |
| 753 space_available -= curr_bfc_offset_.block_offset; |
| 754 } |
| 755 } |
| 756 space_builder_->SetFragmentainerSpaceAvailable(space_available); |
| 757 |
854 return space_builder_->ToConstraintSpace( | 758 return space_builder_->ToConstraintSpace( |
855 FromPlatformWritingMode(current_child_style.getWritingMode())); | 759 FromPlatformWritingMode(current_child_style.getWritingMode())); |
856 } | 760 } |
857 } // namespace blink | 761 } // namespace blink |
OLD | NEW |