Chromium Code Reviews| 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 |