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