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 310 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
410 } | 404 } |
411 | 405 |
412 curr_margin_strut_ = ConstraintSpace().MarginStrut(); | 406 curr_margin_strut_ = ConstraintSpace().MarginStrut(); |
413 curr_bfc_offset_ = ConstraintSpace().BfcOffset(); | 407 curr_bfc_offset_ = ConstraintSpace().BfcOffset(); |
414 | 408 |
415 // Margins collapsing: | 409 // Margins collapsing: |
416 // Do not collapse margins between parent and its child if there is | 410 // Do not collapse margins between parent and its child if there is |
417 // border/padding between them. | 411 // border/padding between them. |
418 if (border_and_padding_.block_start) { | 412 if (border_and_padding_.block_start) { |
419 curr_bfc_offset_.block_offset += curr_margin_strut_.Sum(); | 413 curr_bfc_offset_.block_offset += curr_margin_strut_.Sum(); |
420 UpdateFragmentBfcOffset(curr_bfc_offset_, builder_.get()); | 414 UpdateFragmentBfcOffset(curr_bfc_offset_); |
421 curr_margin_strut_ = NGMarginStrut(); | 415 curr_margin_strut_ = NGMarginStrut(); |
422 } | 416 } |
423 | 417 |
424 // Block that establishes a new BFC knows its BFC offset == {} | 418 // 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 | 419 // 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. | 420 // still {} as the margin strut from the constraint space must also be empty. |
427 if (ConstraintSpace().IsNewFormattingContext()) { | 421 if (ConstraintSpace().IsNewFormattingContext()) { |
428 UpdateFragmentBfcOffset(curr_bfc_offset_, builder_.get()); | 422 UpdateFragmentBfcOffset(curr_bfc_offset_); |
429 DCHECK_EQ(builder_->BfcOffset().value(), NGLogicalOffset()); | 423 DCHECK_EQ(builder_->BfcOffset().value(), NGLogicalOffset()); |
430 DCHECK_EQ(curr_margin_strut_, NGMarginStrut()); | 424 DCHECK_EQ(curr_margin_strut_, NGMarginStrut()); |
431 } | 425 } |
432 | 426 |
433 curr_bfc_offset_.block_offset += content_size_; | 427 curr_bfc_offset_.block_offset += content_size_; |
434 | 428 |
435 while (current_child_) { | 429 while (current_child_) { |
436 if (current_child_->Type() == NGLayoutInputNode::kLegacyBlock) { | 430 if (current_child_->Type() == NGLayoutInputNode::kLegacyBlock) { |
437 NGBlockNode* current_block_child = toNGBlockNode(current_child_); | 431 NGBlockNode* current_block_child = toNGBlockNode(current_child_); |
438 EPosition position = current_block_child->Style().position(); | 432 EPosition position = current_block_child->Style().position(); |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
477 block_size = | 471 block_size = |
478 ComputeBlockSizeForFragment(ConstraintSpace(), Style(), content_size_); | 472 ComputeBlockSizeForFragment(ConstraintSpace(), Style(), content_size_); |
479 builder_->SetBlockSize(block_size); | 473 builder_->SetBlockSize(block_size); |
480 | 474 |
481 // Layout our absolute and fixed positioned children. | 475 // Layout our absolute and fixed positioned children. |
482 NGOutOfFlowLayoutPart(Style(), builder_.get()).Run(); | 476 NGOutOfFlowLayoutPart(Style(), builder_.get()).Run(); |
483 | 477 |
484 // Non-empty blocks always know their position in space: | 478 // Non-empty blocks always know their position in space: |
485 if (block_size) { | 479 if (block_size) { |
486 curr_bfc_offset_.block_offset += curr_margin_strut_.Sum(); | 480 curr_bfc_offset_.block_offset += curr_margin_strut_.Sum(); |
487 UpdateFragmentBfcOffset(curr_bfc_offset_, builder_.get()); | 481 UpdateFragmentBfcOffset(curr_bfc_offset_); |
488 PositionPendingFloats(curr_bfc_offset_.block_offset, ConstraintSpace(), | 482 PositionPendingFloats(curr_bfc_offset_.block_offset, ConstraintSpace(), |
489 builder_.get()); | 483 builder_.get()); |
490 } | 484 } |
491 | 485 |
492 // Margins collapsing: | 486 // Margins collapsing: |
493 // Do not collapse margins between the last in-flow child and bottom margin | 487 // Do not collapse margins between the last in-flow child and bottom margin |
494 // of its parent if the parent has height != auto() | 488 // of its parent if the parent has height != auto() |
495 if (!Style().logicalHeight().isAuto()) { | 489 if (!Style().logicalHeight().isAuto()) { |
496 // TODO(glebl): handle minLogicalHeight, maxLogicalHeight. | 490 // TODO(glebl): handle minLogicalHeight, maxLogicalHeight. |
497 curr_margin_strut_ = NGMarginStrut(); | 491 curr_margin_strut_ = NGMarginStrut(); |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
568 // Fragment doesn't know its offset but we can still calculate its BFC | 562 // Fragment doesn't know its offset but we can still calculate its BFC |
569 // position because the parent fragment's BFC is known. | 563 // position because the parent fragment's BFC is known. |
570 // Example: | 564 // Example: |
571 // BFC Offset is known here because of the padding. | 565 // BFC Offset is known here because of the padding. |
572 // <div style="padding: 1px"> | 566 // <div style="padding: 1px"> |
573 // <div id="empty-div" style="margins: 1px"></div> | 567 // <div id="empty-div" style="margins: 1px"></div> |
574 bfc_offset = curr_bfc_offset_; | 568 bfc_offset = curr_bfc_offset_; |
575 bfc_offset.value().block_offset += curr_margin_strut_.Sum(); | 569 bfc_offset.value().block_offset += curr_margin_strut_.Sum(); |
576 } | 570 } |
577 if (bfc_offset) { | 571 if (bfc_offset) { |
578 UpdateFragmentBfcOffset(curr_bfc_offset_, builder_.get()); | 572 UpdateFragmentBfcOffset(curr_bfc_offset_); |
579 PositionPendingFloats(curr_bfc_offset_.block_offset, ConstraintSpace(), | 573 PositionPendingFloats(curr_bfc_offset_.block_offset, ConstraintSpace(), |
580 builder_.get()); | 574 builder_.get()); |
581 } | 575 } |
582 NGLogicalOffset logical_offset = CalculateLogicalOffset(bfc_offset); | 576 NGLogicalOffset logical_offset = CalculateLogicalOffset(bfc_offset); |
583 | 577 |
584 if (fragmentainer_mapper_) | 578 if (fragmentainer_mapper_) |
585 fragmentainer_mapper_->ToVisualOffset(logical_offset); | 579 fragmentainer_mapper_->ToVisualOffset(logical_offset); |
586 else | 580 else |
587 logical_offset.block_offset -= PreviousBreakOffset(); | 581 logical_offset.block_offset -= PreviousBreakOffset(); |
588 | 582 |
(...skipping 203 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
792 space_builder_->SetIsNewFormattingContext(is_new_bfc) | 786 space_builder_->SetIsNewFormattingContext(is_new_bfc) |
793 .SetIsShrinkToFit( | 787 .SetIsShrinkToFit( |
794 ShouldShrinkToFit(ConstraintSpace(), CurrentChildStyle())) | 788 ShouldShrinkToFit(ConstraintSpace(), CurrentChildStyle())) |
795 .SetTextDirection(current_child_style.direction()); | 789 .SetTextDirection(current_child_style.direction()); |
796 LayoutUnit space_available = SpaceAvailableForCurrentChild(); | 790 LayoutUnit space_available = SpaceAvailableForCurrentChild(); |
797 space_builder_->SetFragmentainerSpaceAvailable(space_available); | 791 space_builder_->SetFragmentainerSpaceAvailable(space_available); |
798 | 792 |
799 // Clearance : | 793 // Clearance : |
800 // - Collapse margins | 794 // - Collapse margins |
801 // - Update curr_bfc_offset and parent BFC offset if needed. | 795 // - Update curr_bfc_offset and parent BFC offset if needed. |
802 // - Position all pending floats as position is known now. | 796 // - Position all pending floats since the fragment's BFC offset is known. |
803 // TODO(glebl): Fix the use case with clear: left and an intruding right. | 797 // - Set the clearance offset on constraint space's builder. |
804 // https://software.hixie.ch/utilities/js/live-dom-viewer/saved/4847 | |
805 if (current_child_style.clear() != EClear::kNone) { | 798 if (current_child_style.clear() != EClear::kNone) { |
806 curr_bfc_offset_.block_offset += curr_margin_strut_.Sum(); | 799 curr_bfc_offset_.block_offset += curr_margin_strut_.Sum(); |
ikilpatrick
2017/02/24 17:36:57
Are we able to do this here? I.e. does it handle s
Gleb Lanbin
2017/02/24 18:37:00
thanks for the test case. It works as expected, up
| |
807 UpdateFragmentBfcOffset(curr_bfc_offset_, builder_.get()); | 800 UpdateFragmentBfcOffset(curr_bfc_offset_); |
808 // Only collapse margins if it's an adjoining block with clearance. | 801 // Only collapse margins if it's an adjoining block with clearance. |
809 if (!content_size_) { | 802 if (!content_size_) { |
810 curr_margin_strut_ = NGMarginStrut(); | 803 curr_margin_strut_ = NGMarginStrut(); |
811 curr_child_margins_.block_start = LayoutUnit(); | 804 curr_child_margins_.block_start = LayoutUnit(); |
812 } | 805 } |
813 PositionPendingFloats(curr_bfc_offset_.block_offset, ConstraintSpace(), | 806 PositionPendingFloats(curr_bfc_offset_.block_offset, ConstraintSpace(), |
814 builder_.get()); | 807 builder_.get()); |
815 AdjustToClearance(constraint_space_->Exclusions(), current_child_style, | 808 WTF::Optional<LayoutUnit> clearance_offset = GetClearanceOffset( |
816 builder_->BfcOffset().value(), &content_size_); | 809 constraint_space_->Exclusions(), current_child_style); |
810 space_builder_->SetClearanceOffset(clearance_offset); | |
817 } | 811 } |
818 | 812 |
819 // Set estimated BFC offset to the next child's constraint space. | 813 // Set estimated BFC offset to the next child's constraint space. |
820 curr_bfc_offset_ = builder_->BfcOffset() ? builder_->BfcOffset().value() | 814 curr_bfc_offset_ = builder_->BfcOffset() ? builder_->BfcOffset().value() |
821 : ConstraintSpace().BfcOffset(); | 815 : ConstraintSpace().BfcOffset(); |
822 curr_bfc_offset_.block_offset += content_size_; | 816 curr_bfc_offset_.block_offset += content_size_; |
823 curr_bfc_offset_.inline_offset += border_and_padding_.inline_start; | 817 curr_bfc_offset_.inline_offset += border_and_padding_.inline_start; |
824 | 818 |
825 // Floats margins are not included in child's CS because | 819 // Floats margins are not included in child's CS because |
826 // 1) Floats do not participate in margins collapsing | 820 // 1) Floats do not participate in margins collapsing |
827 // 2) Floats margins are used separately to calculate floating exclusions. | 821 // 2) Floats margins are used separately to calculate floating exclusions. |
828 if (!CurrentChildStyle().isFloating()) { | 822 if (!CurrentChildStyle().isFloating()) { |
829 curr_bfc_offset_.inline_offset += curr_child_margins_.inline_start; | 823 curr_bfc_offset_.inline_offset += curr_child_margins_.inline_start; |
830 // Append the current margin strut with child's block start margin. | 824 // Append the current margin strut with child's block start margin. |
831 // Non empty border/padding use cases are handled inside of the child's | 825 // Non empty border/padding use cases are handled inside of the child's |
832 // layout. | 826 // layout. |
833 curr_margin_strut_.Append(curr_child_margins_.block_start); | 827 curr_margin_strut_.Append(curr_child_margins_.block_start); |
834 space_builder_->SetMarginStrut(curr_margin_strut_); | 828 space_builder_->SetMarginStrut(curr_margin_strut_); |
835 } | 829 } |
836 | 830 |
837 space_builder_->SetBfcOffset(curr_bfc_offset_); | 831 space_builder_->SetBfcOffset(curr_bfc_offset_); |
838 | 832 |
839 return space_builder_->ToConstraintSpace( | 833 return space_builder_->ToConstraintSpace( |
840 FromPlatformWritingMode(current_child_style.getWritingMode())); | 834 FromPlatformWritingMode(current_child_style.getWritingMode())); |
841 } | 835 } |
842 | 836 |
837 void NGBlockLayoutAlgorithm::UpdateFragmentBfcOffset( | |
838 const NGLogicalOffset& offset) { | |
839 if (!builder_->BfcOffset()) { | |
840 NGLogicalOffset bfc_offset = offset; | |
841 if (ConstraintSpace().ClearanceOffset()) { | |
842 bfc_offset.block_offset = std::max( | |
843 ConstraintSpace().ClearanceOffset().value(), offset.block_offset); | |
844 } | |
845 builder_->SetBfcOffset(bfc_offset); | |
846 } | |
847 } | |
848 | |
843 } // namespace blink | 849 } // namespace blink |
OLD | NEW |