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_box_fragment.h" | 9 #include "core/layout/ng/ng_box_fragment.h" |
10 #include "core/layout/ng/ng_column_mapper.h" | 10 #include "core/layout/ng/ng_column_mapper.h" |
(...skipping 23 matching lines...) Expand all Loading... |
34 FromPlatformWritingMode(child_style.getWritingMode()); | 34 FromPlatformWritingMode(child_style.getWritingMode()); |
35 // Whether the child and the containing block are parallel to each other. | 35 // Whether the child and the containing block are parallel to each other. |
36 // Example: vertical-rl and vertical-lr | 36 // Example: vertical-rl and vertical-lr |
37 bool is_in_parallel_flow = | 37 bool is_in_parallel_flow = |
38 IsParallelWritingMode(parent_space.WritingMode(), child_writing_mode); | 38 IsParallelWritingMode(parent_space.WritingMode(), child_writing_mode); |
39 | 39 |
40 return child_style.display() == EDisplay::InlineBlock || | 40 return child_style.display() == EDisplay::InlineBlock || |
41 child_style.isFloating() || !is_in_parallel_flow; | 41 child_style.isFloating() || !is_in_parallel_flow; |
42 } | 42 } |
43 | 43 |
44 // Updates the fragment's BFC offset if it's not already set. | 44 // Returns max of 2 {@code WTF::Optional} values. |
45 void UpdateFragmentBfcOffset(const NGLogicalOffset& offset, | 45 template <typename T> |
46 NGFragmentBuilder* builder) { | 46 WTF::Optional<T> OptionalMax(const WTF::Optional<T>& value1, |
47 if (!builder->BfcOffset()) | 47 const WTF::Optional<T>& value2) { |
48 builder->SetBfcOffset(offset); | 48 if (value1 && value2) { |
| 49 return std::max(value1.value(), value2.value()); |
| 50 } else if (value1) { |
| 51 return value1; |
| 52 } |
| 53 return value2; |
49 } | 54 } |
50 | 55 |
51 // Adjusts content_size to respect the CSS "clear" property. | 56 WTF::Optional<LayoutUnit> GetClearanceOffset( |
52 // Picks up the maximum between left/right exclusions and content_size depending | 57 const std::shared_ptr<NGExclusions>& exclusions, |
53 // on the value of style.clear() property. | 58 const ComputedStyle& style) { |
54 void AdjustToClearance(const std::shared_ptr<NGExclusions>& exclusions, | |
55 const ComputedStyle& style, | |
56 const NGLogicalOffset& from_offset, | |
57 LayoutUnit* content_size) { | |
58 DCHECK(content_size) << "content_size cannot be null here"; | |
59 const NGExclusion* right_exclusion = exclusions->last_right_float; | 59 const NGExclusion* right_exclusion = exclusions->last_right_float; |
60 const NGExclusion* left_exclusion = exclusions->last_left_float; | 60 const NGExclusion* left_exclusion = exclusions->last_left_float; |
61 | 61 |
62 LayoutUnit left_block_end_offset = *content_size; | 62 WTF::Optional<LayoutUnit> left_offset; |
63 if (left_exclusion) { | 63 if (left_exclusion) { |
64 left_block_end_offset = std::max( | 64 left_offset = left_exclusion->rect.BlockEndOffset(); |
65 left_exclusion->rect.BlockEndOffset() - from_offset.block_offset, | |
66 *content_size); | |
67 } | 65 } |
68 LayoutUnit right_block_end_offset = *content_size; | 66 WTF::Optional<LayoutUnit> right_offset; |
69 if (right_exclusion) { | 67 if (right_exclusion) { |
70 right_block_end_offset = std::max( | 68 right_offset = right_exclusion->rect.BlockEndOffset(); |
71 right_exclusion->rect.BlockEndOffset() - from_offset.block_offset, | |
72 *content_size); | |
73 } | 69 } |
74 | 70 |
75 switch (style.clear()) { | 71 switch (style.clear()) { |
76 case EClear::kNone: | 72 case EClear::kNone: |
77 return; // nothing to do here. | 73 return WTF::nullopt; // nothing to do here. |
78 case EClear::kLeft: | 74 case EClear::kLeft: |
79 *content_size = left_block_end_offset; | 75 return left_offset; |
80 break; | |
81 case EClear::kRight: | 76 case EClear::kRight: |
82 *content_size = right_block_end_offset; | 77 return right_offset; |
83 break; | |
84 case EClear::kBoth: | 78 case EClear::kBoth: |
85 *content_size = std::max(left_block_end_offset, right_block_end_offset); | 79 return OptionalMax<LayoutUnit>(left_offset, right_offset); |
86 break; | |
87 default: | 80 default: |
88 ASSERT_NOT_REACHED(); | 81 ASSERT_NOT_REACHED(); |
89 } | 82 } |
| 83 return WTF::nullopt; |
90 } | 84 } |
91 | 85 |
92 // Creates an exclusion from the fragment that will be placed in the provided | 86 // Creates an exclusion from the fragment that will be placed in the provided |
93 // layout opportunity. | 87 // layout opportunity. |
94 NGExclusion CreateExclusion(const NGFragment& fragment, | 88 NGExclusion CreateExclusion(const NGFragment& fragment, |
95 const NGLayoutOpportunity& opportunity, | 89 const NGLayoutOpportunity& opportunity, |
96 const LayoutUnit float_offset, | 90 const LayoutUnit float_offset, |
97 const NGBoxStrut& margins, | 91 const NGBoxStrut& margins, |
98 NGExclusion::Type exclusion_type) { | 92 NGExclusion::Type exclusion_type) { |
99 NGExclusion exclusion; | 93 NGExclusion exclusion; |
(...skipping 249 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
349 LayoutUnit inline_offset = | 343 LayoutUnit inline_offset = |
350 border_and_padding_.inline_start + curr_child_margins_.inline_start; | 344 border_and_padding_.inline_start + curr_child_margins_.inline_start; |
351 LayoutUnit block_offset = content_size_; | 345 LayoutUnit block_offset = content_size_; |
352 if (known_fragment_offset) { | 346 if (known_fragment_offset) { |
353 block_offset = known_fragment_offset.value().block_offset - | 347 block_offset = known_fragment_offset.value().block_offset - |
354 builder_->BfcOffset().value().block_offset; | 348 builder_->BfcOffset().value().block_offset; |
355 } | 349 } |
356 return {inline_offset, block_offset}; | 350 return {inline_offset, block_offset}; |
357 } | 351 } |
358 | 352 |
| 353 void NGBlockLayoutAlgorithm::UpdateFragmentBfcOffset( |
| 354 const NGLogicalOffset& offset) { |
| 355 if (!builder_->BfcOffset()) { |
| 356 NGLogicalOffset bfc_offset = offset; |
| 357 if (ConstraintSpace().ClearanceOffset()) { |
| 358 bfc_offset.block_offset = std::max( |
| 359 ConstraintSpace().ClearanceOffset().value(), offset.block_offset); |
| 360 } |
| 361 builder_->SetBfcOffset(bfc_offset); |
| 362 } |
| 363 } |
| 364 |
359 RefPtr<NGLayoutResult> NGBlockLayoutAlgorithm::Layout() { | 365 RefPtr<NGLayoutResult> NGBlockLayoutAlgorithm::Layout() { |
360 WTF::Optional<MinAndMaxContentSizes> sizes; | 366 WTF::Optional<MinAndMaxContentSizes> sizes; |
361 if (NeedMinAndMaxContentSizes(ConstraintSpace(), Style())) | 367 if (NeedMinAndMaxContentSizes(ConstraintSpace(), Style())) |
362 sizes = ComputeMinAndMaxContentSizes(); | 368 sizes = ComputeMinAndMaxContentSizes(); |
363 | 369 |
364 border_and_padding_ = | 370 border_and_padding_ = |
365 ComputeBorders(Style()) + ComputePadding(ConstraintSpace(), Style()); | 371 ComputeBorders(Style()) + ComputePadding(ConstraintSpace(), Style()); |
366 | 372 |
367 LayoutUnit inline_size = | 373 LayoutUnit inline_size = |
368 ComputeInlineSizeForFragment(ConstraintSpace(), Style(), sizes); | 374 ComputeInlineSizeForFragment(ConstraintSpace(), Style(), sizes); |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
410 } | 416 } |
411 | 417 |
412 curr_margin_strut_ = ConstraintSpace().MarginStrut(); | 418 curr_margin_strut_ = ConstraintSpace().MarginStrut(); |
413 curr_bfc_offset_ = ConstraintSpace().BfcOffset(); | 419 curr_bfc_offset_ = ConstraintSpace().BfcOffset(); |
414 | 420 |
415 // Margins collapsing: | 421 // Margins collapsing: |
416 // Do not collapse margins between parent and its child if there is | 422 // Do not collapse margins between parent and its child if there is |
417 // border/padding between them. | 423 // border/padding between them. |
418 if (border_and_padding_.block_start) { | 424 if (border_and_padding_.block_start) { |
419 curr_bfc_offset_.block_offset += curr_margin_strut_.Sum(); | 425 curr_bfc_offset_.block_offset += curr_margin_strut_.Sum(); |
420 UpdateFragmentBfcOffset(curr_bfc_offset_, builder_.get()); | 426 UpdateFragmentBfcOffset(curr_bfc_offset_); |
421 curr_margin_strut_ = NGMarginStrut(); | 427 curr_margin_strut_ = NGMarginStrut(); |
422 } | 428 } |
423 | 429 |
424 // Block that establishes a new BFC knows its BFC offset == {} | 430 // Block that establishes a new BFC knows its BFC offset == {} |
425 // If a new formatting context hits the if branch above then the BFC offset is | 431 // If a new formatting context hits the if branch above then the BFC offset is |
426 // still {} as the margin strut from the constraint space must also be empty. | 432 // still {} as the margin strut from the constraint space must also be empty. |
427 if (ConstraintSpace().IsNewFormattingContext()) { | 433 if (ConstraintSpace().IsNewFormattingContext()) { |
428 UpdateFragmentBfcOffset(curr_bfc_offset_, builder_.get()); | 434 UpdateFragmentBfcOffset(curr_bfc_offset_); |
429 DCHECK_EQ(builder_->BfcOffset().value(), NGLogicalOffset()); | 435 DCHECK_EQ(builder_->BfcOffset().value(), NGLogicalOffset()); |
430 DCHECK_EQ(curr_margin_strut_, NGMarginStrut()); | 436 DCHECK_EQ(curr_margin_strut_, NGMarginStrut()); |
431 } | 437 } |
432 | 438 |
433 curr_bfc_offset_.block_offset += content_size_; | 439 curr_bfc_offset_.block_offset += content_size_; |
434 | 440 |
435 while (current_child_) { | 441 while (current_child_) { |
436 if (current_child_->Type() == NGLayoutInputNode::kLegacyBlock) { | 442 if (current_child_->Type() == NGLayoutInputNode::kLegacyBlock) { |
437 NGBlockNode* current_block_child = toNGBlockNode(current_child_); | 443 NGBlockNode* current_block_child = toNGBlockNode(current_child_); |
438 EPosition position = current_block_child->Style().position(); | 444 EPosition position = current_block_child->Style().position(); |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
478 block_size = | 484 block_size = |
479 ComputeBlockSizeForFragment(ConstraintSpace(), Style(), content_size_); | 485 ComputeBlockSizeForFragment(ConstraintSpace(), Style(), content_size_); |
480 builder_->SetBlockSize(block_size); | 486 builder_->SetBlockSize(block_size); |
481 | 487 |
482 // Layout our absolute and fixed positioned children. | 488 // Layout our absolute and fixed positioned children. |
483 NGOutOfFlowLayoutPart(Style(), builder_.get()).Run(); | 489 NGOutOfFlowLayoutPart(Style(), builder_.get()).Run(); |
484 | 490 |
485 // Non-empty blocks always know their position in space: | 491 // Non-empty blocks always know their position in space: |
486 if (block_size) { | 492 if (block_size) { |
487 curr_bfc_offset_.block_offset += curr_margin_strut_.Sum(); | 493 curr_bfc_offset_.block_offset += curr_margin_strut_.Sum(); |
488 UpdateFragmentBfcOffset(curr_bfc_offset_, builder_.get()); | 494 UpdateFragmentBfcOffset(curr_bfc_offset_); |
489 PositionPendingFloats(curr_bfc_offset_.block_offset, ConstraintSpace(), | 495 PositionPendingFloats(curr_bfc_offset_.block_offset, ConstraintSpace(), |
490 builder_.get()); | 496 builder_.get()); |
491 } | 497 } |
492 | 498 |
493 // Margins collapsing: | 499 // Margins collapsing: |
494 // Do not collapse margins between the last in-flow child and bottom margin | 500 // Do not collapse margins between the last in-flow child and bottom margin |
495 // of its parent if the parent has height != auto() | 501 // of its parent if the parent has height != auto() |
496 if (!Style().logicalHeight().isAuto()) { | 502 if (!Style().logicalHeight().isAuto()) { |
497 // TODO(glebl): handle minLogicalHeight, maxLogicalHeight. | 503 // TODO(glebl): handle minLogicalHeight, maxLogicalHeight. |
498 curr_margin_strut_ = NGMarginStrut(); | 504 curr_margin_strut_ = NGMarginStrut(); |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
570 // Fragment doesn't know its offset but we can still calculate its BFC | 576 // Fragment doesn't know its offset but we can still calculate its BFC |
571 // position because the parent fragment's BFC is known. | 577 // position because the parent fragment's BFC is known. |
572 // Example: | 578 // Example: |
573 // BFC Offset is known here because of the padding. | 579 // BFC Offset is known here because of the padding. |
574 // <div style="padding: 1px"> | 580 // <div style="padding: 1px"> |
575 // <div id="empty-div" style="margins: 1px"></div> | 581 // <div id="empty-div" style="margins: 1px"></div> |
576 bfc_offset = curr_bfc_offset_; | 582 bfc_offset = curr_bfc_offset_; |
577 bfc_offset.value().block_offset += curr_margin_strut_.Sum(); | 583 bfc_offset.value().block_offset += curr_margin_strut_.Sum(); |
578 } | 584 } |
579 if (bfc_offset) { | 585 if (bfc_offset) { |
580 UpdateFragmentBfcOffset(curr_bfc_offset_, builder_.get()); | 586 UpdateFragmentBfcOffset(curr_bfc_offset_); |
581 PositionPendingFloats(curr_bfc_offset_.block_offset, ConstraintSpace(), | 587 PositionPendingFloats(curr_bfc_offset_.block_offset, ConstraintSpace(), |
582 builder_.get()); | 588 builder_.get()); |
583 } | 589 } |
584 NGLogicalOffset logical_offset = CalculateLogicalOffset(bfc_offset); | 590 NGLogicalOffset logical_offset = CalculateLogicalOffset(bfc_offset); |
585 | 591 |
586 if (fragmentainer_mapper_) | 592 if (fragmentainer_mapper_) |
587 fragmentainer_mapper_->ToVisualOffset(logical_offset); | 593 fragmentainer_mapper_->ToVisualOffset(logical_offset); |
588 else | 594 else |
589 logical_offset.block_offset -= PreviousBreakOffset(); | 595 logical_offset.block_offset -= PreviousBreakOffset(); |
590 | 596 |
(...skipping 201 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
792 bool is_new_bfc = IsNewFormattingContextForInFlowBlockLevelChild( | 798 bool is_new_bfc = IsNewFormattingContextForInFlowBlockLevelChild( |
793 ConstraintSpace(), current_child_style); | 799 ConstraintSpace(), current_child_style); |
794 space_builder_->SetIsNewFormattingContext(is_new_bfc) | 800 space_builder_->SetIsNewFormattingContext(is_new_bfc) |
795 .SetIsShrinkToFit( | 801 .SetIsShrinkToFit( |
796 ShouldShrinkToFit(ConstraintSpace(), CurrentChildStyle())) | 802 ShouldShrinkToFit(ConstraintSpace(), CurrentChildStyle())) |
797 .SetTextDirection(current_child_style.direction()); | 803 .SetTextDirection(current_child_style.direction()); |
798 LayoutUnit space_available = SpaceAvailableForCurrentChild(); | 804 LayoutUnit space_available = SpaceAvailableForCurrentChild(); |
799 space_builder_->SetFragmentainerSpaceAvailable(space_available); | 805 space_builder_->SetFragmentainerSpaceAvailable(space_available); |
800 | 806 |
801 // Clearance : | 807 // Clearance : |
802 // - Collapse margins | 808 // - *Always* collapse margins and update *container*'s BFC offset. |
803 // - Update curr_bfc_offset and parent BFC offset if needed. | 809 // - Position all pending floats since the fragment's BFC offset is known. |
804 // - Position all pending floats as position is known now. | 810 // - Set the clearance offset on the constraint space's builder. |
805 // TODO(glebl): Fix the use case with clear: left and an intruding right. | |
806 // https://software.hixie.ch/utilities/js/live-dom-viewer/saved/4847 | |
807 if (current_child_style.clear() != EClear::kNone) { | 811 if (current_child_style.clear() != EClear::kNone) { |
808 curr_bfc_offset_.block_offset += curr_margin_strut_.Sum(); | 812 curr_bfc_offset_.block_offset += curr_margin_strut_.Sum(); |
809 UpdateFragmentBfcOffset(curr_bfc_offset_, builder_.get()); | 813 UpdateFragmentBfcOffset(curr_bfc_offset_); |
810 // Only collapse margins if it's an adjoining block with clearance. | 814 // Only collapse margins if it's an adjoining block with clearance. |
811 if (!content_size_) { | 815 if (!content_size_) { |
812 curr_margin_strut_ = NGMarginStrut(); | 816 curr_margin_strut_ = NGMarginStrut(); |
813 curr_child_margins_.block_start = LayoutUnit(); | 817 curr_child_margins_.block_start = LayoutUnit(); |
814 } | 818 } |
815 PositionPendingFloats(curr_bfc_offset_.block_offset, ConstraintSpace(), | 819 PositionPendingFloats(curr_bfc_offset_.block_offset, ConstraintSpace(), |
816 builder_.get()); | 820 builder_.get()); |
817 AdjustToClearance(constraint_space_->Exclusions(), current_child_style, | 821 WTF::Optional<LayoutUnit> clearance_offset = GetClearanceOffset( |
818 builder_->BfcOffset().value(), &content_size_); | 822 constraint_space_->Exclusions(), current_child_style); |
| 823 space_builder_->SetClearanceOffset(clearance_offset); |
819 } | 824 } |
820 | 825 |
821 // Set estimated BFC offset to the next child's constraint space. | 826 // Set estimated BFC offset to the next child's constraint space. |
822 curr_bfc_offset_ = builder_->BfcOffset() ? builder_->BfcOffset().value() | 827 curr_bfc_offset_ = builder_->BfcOffset() ? builder_->BfcOffset().value() |
823 : ConstraintSpace().BfcOffset(); | 828 : ConstraintSpace().BfcOffset(); |
824 curr_bfc_offset_.block_offset += content_size_; | 829 curr_bfc_offset_.block_offset += content_size_; |
825 curr_bfc_offset_.inline_offset += border_and_padding_.inline_start; | 830 curr_bfc_offset_.inline_offset += border_and_padding_.inline_start; |
826 | 831 |
827 // Floats margins are not included in child's CS because | 832 // Floats margins are not included in child's CS because |
828 // 1) Floats do not participate in margins collapsing | 833 // 1) Floats do not participate in margins collapsing |
829 // 2) Floats margins are used separately to calculate floating exclusions. | 834 // 2) Floats margins are used separately to calculate floating exclusions. |
830 if (!CurrentChildStyle().isFloating()) { | 835 if (!CurrentChildStyle().isFloating()) { |
831 curr_bfc_offset_.inline_offset += curr_child_margins_.inline_start; | 836 curr_bfc_offset_.inline_offset += curr_child_margins_.inline_start; |
832 // Append the current margin strut with child's block start margin. | 837 // Append the current margin strut with child's block start margin. |
833 // Non empty border/padding use cases are handled inside of the child's | 838 // Non empty border/padding use cases are handled inside of the child's |
834 // layout. | 839 // layout. |
835 curr_margin_strut_.Append(curr_child_margins_.block_start); | 840 curr_margin_strut_.Append(curr_child_margins_.block_start); |
836 space_builder_->SetMarginStrut(curr_margin_strut_); | 841 space_builder_->SetMarginStrut(curr_margin_strut_); |
837 } | 842 } |
838 | 843 |
839 space_builder_->SetBfcOffset(curr_bfc_offset_); | 844 space_builder_->SetBfcOffset(curr_bfc_offset_); |
840 | 845 |
841 return space_builder_->ToConstraintSpace( | 846 return space_builder_->ToConstraintSpace( |
842 FromPlatformWritingMode(current_child_style.getWritingMode())); | 847 FromPlatformWritingMode(current_child_style.getWritingMode())); |
843 } | 848 } |
844 | |
845 } // namespace blink | 849 } // namespace blink |
OLD | NEW |