Skip to content

Commit a725380

Browse files
authored
Auto merge of servo#29939 - mrobinson:fix-float-placement-with-future-margins, r=mrobinson
Properly position floats when subsequent boxes collapse margins with containing block Margins should be able to collapse through floats when collapsing with parent blocks (the containing block). To properly place floats in this situation, we need to look at these subsequent floats to find out how much of the margin will collapse with the parent. This initial implementation is very basic and the second step would be to cache this in order to avoid having to constantly recalculate it. Fixes servo#29915. Co-authored-by: Oriol Brufau <[email protected]> <!-- Please describe your changes on the following line: --> --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [x] These changes fix servo#29915. - [x] There are tests for these changes <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->
2 parents ea4701c + bb13702 commit a725380

File tree

4 files changed

+233
-105
lines changed

4 files changed

+233
-105
lines changed

components/layout_2020/flow/float.rs

Lines changed: 61 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@ use crate::context::LayoutContext;
1010
use crate::dom::NodeExt;
1111
use crate::dom_traversal::{Contents, NodeAndStyleInfo};
1212
use crate::formatting_contexts::IndependentFormattingContext;
13-
use crate::fragment_tree::{
14-
BoxFragment, CollapsedBlockMargins, CollapsedMargin, FloatFragment, Fragment,
15-
};
13+
use crate::fragment_tree::{BoxFragment, CollapsedBlockMargins, CollapsedMargin, FloatFragment};
1614
use crate::geom::flow_relative::{Rect, Vec2};
1715
use crate::positioned::PositioningContext;
1816
use crate::style_ext::{ComputedValuesExt, DisplayInside};
@@ -658,34 +656,9 @@ impl FloatBox {
658656
layout_context: &LayoutContext,
659657
positioning_context: &mut PositioningContext,
660658
containing_block: &ContainingBlock,
661-
mut sequential_layout_state: Option<&mut SequentialLayoutState>,
662-
) -> Fragment {
663-
let sequential_layout_state = sequential_layout_state
664-
.as_mut()
665-
.expect("Tried to lay out a float with no sequential placement state!");
666-
667-
// Speculate that the float ceiling will be located at the current block position plus the
668-
// result of solving any margins we're building up. This is usually right, but it can be
669-
// incorrect if there are more in-flow collapsible margins yet to be seen. An example
670-
// showing when this can go wrong:
671-
//
672-
// <div style="margin: 5px"></div>
673-
// <div style="float: left"></div>
674-
// <div style="margin: 10px"></div>
675-
//
676-
// Assuming these are all in-flow, the float should be placed 10px down from the start, not
677-
// 5px, but we can't know that because we haven't seen the block after this float yet.
678-
//
679-
// FIXME(pcwalton): Implement the proper behavior when speculation fails. Either detect it
680-
// afterward and fix it up, or detect this situation ahead of time via lookahead and make
681-
// sure `current_margin` is accurate before calling this method.
682-
sequential_layout_state
683-
.floats
684-
.lower_ceiling(sequential_layout_state.current_block_position_including_margins());
685-
659+
) -> BoxFragment {
686660
let style = self.contents.style().clone();
687-
let float_context = &mut sequential_layout_state.floats;
688-
let box_fragment = positioning_context.layout_maybe_position_relative_fragment(
661+
positioning_context.layout_maybe_position_relative_fragment(
689662
layout_context,
690663
containing_block,
691664
&style,
@@ -696,7 +669,7 @@ impl FloatBox {
696669
let margin = pbm.margin.auto_is(|| Length::zero());
697670
let pbm_sums = &(&pbm.padding + &pbm.border) + &margin;
698671

699-
let (content_size, fragments);
672+
let (content_size, children);
700673
match self.contents {
701674
IndependentFormattingContext::NonReplaced(ref mut non_replaced) => {
702675
// Calculate inline size.
@@ -739,7 +712,7 @@ impl FloatBox {
739712
.block
740713
.auto_is(|| independent_layout.content_block_size),
741714
};
742-
fragments = independent_layout.fragments;
715+
children = independent_layout.fragments;
743716
},
744717
IndependentFormattingContext::Replaced(ref replaced) => {
745718
// https://drafts.csswg.org/css2/#float-replaced-width
@@ -750,40 +723,32 @@ impl FloatBox {
750723
None,
751724
&pbm,
752725
);
753-
fragments = replaced
726+
children = replaced
754727
.contents
755728
.make_fragments(&replaced.style, content_size.clone());
756729
},
757730
};
758-
let margin_box_start_corner = float_context.add_float(&PlacementInfo {
759-
size: &content_size + &pbm_sums.sum(),
760-
side: FloatSide::from_style(&style).expect("Float box wasn't floated!"),
761-
clear: ClearSide::from_style(&style),
762-
});
763731

764732
let content_rect = Rect {
765-
start_corner: &margin_box_start_corner + &pbm_sums.start_offset(),
766-
size: content_size.clone(),
733+
start_corner: Vec2::zero(),
734+
size: content_size,
767735
};
768736

769-
// Clearance is handled internally by the float placement logic, so there's no need
770-
// to store it explicitly in the fragment.
771-
let clearance = Length::zero();
772-
773737
BoxFragment::new(
774738
self.contents.base_fragment_info(),
775739
style.clone(),
776-
fragments,
740+
children,
777741
content_rect,
778742
pbm.padding,
779743
pbm.border,
780744
margin,
781-
clearance,
745+
// Clearance is handled internally by the float placement logic, so there's no need
746+
// to store it explicitly in the fragment.
747+
Length::zero(), // clearance
782748
CollapsedBlockMargins::zero(),
783749
)
784750
},
785-
);
786-
Fragment::Float(box_fragment)
751+
)
787752
}
788753
}
789754

@@ -890,4 +855,52 @@ impl SequentialLayoutState {
890855
pub(crate) fn adjoin_assign(&mut self, margin: &CollapsedMargin) {
891856
self.current_margin.adjoin_assign(margin)
892857
}
858+
859+
/// Get the offset of the current containing block and any uncollapsed margins.
860+
pub(crate) fn current_containing_block_offset(&self) -> CSSPixelLength {
861+
self.floats.containing_block_info.block_start +
862+
self.floats
863+
.containing_block_info
864+
.block_start_margins_not_collapsed
865+
.solve()
866+
}
867+
868+
/// This function places a Fragment that has been created for a FloatBox.
869+
pub(crate) fn place_float_fragment(
870+
&mut self,
871+
box_fragment: &mut BoxFragment,
872+
margins_collapsing_with_parent_containing_block: CollapsedMargin,
873+
block_offset_from_containining_block_top: CSSPixelLength,
874+
) {
875+
let block_start_of_containing_block_in_bfc = self.floats.containing_block_info.block_start +
876+
self.floats
877+
.containing_block_info
878+
.block_start_margins_not_collapsed
879+
.adjoin(&margins_collapsing_with_parent_containing_block)
880+
.solve();
881+
882+
self.floats.lower_ceiling(
883+
block_start_of_containing_block_in_bfc + block_offset_from_containining_block_top,
884+
);
885+
886+
let pbm_sums = &(&box_fragment.padding + &box_fragment.border) + &box_fragment.margin;
887+
let margin_box_start_corner = self.floats.add_float(&PlacementInfo {
888+
size: &box_fragment.content_rect.size + &pbm_sums.sum(),
889+
side: FloatSide::from_style(&box_fragment.style).expect("Float box wasn't floated!"),
890+
clear: ClearSide::from_style(&box_fragment.style),
891+
});
892+
893+
// This is the position of the float in the float-containing block formatting context. We add the
894+
// existing start corner here because we may have already gotten some relative positioning offset.
895+
let new_position_in_bfc = &(&margin_box_start_corner + &pbm_sums.start_offset()) +
896+
&box_fragment.content_rect.start_corner;
897+
898+
// This is the position of the float relative to the containing block start.
899+
let new_position_in_containing_block = Vec2 {
900+
inline: new_position_in_bfc.inline - self.floats.containing_block_info.inline_start,
901+
block: new_position_in_bfc.block - block_start_of_containing_block_in_bfc,
902+
};
903+
904+
box_fragment.content_rect.start_corner = new_position_in_containing_block;
905+
}
893906
}

components/layout_2020/flow/inline.rs

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ use crate::flow::float::{FloatBox, SequentialLayoutState};
88
use crate::flow::FlowLayout;
99
use crate::formatting_contexts::IndependentFormattingContext;
1010
use crate::fragment_tree::{
11-
AnonymousFragment, BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, FontMetrics, Fragment,
12-
TextFragment,
11+
AnonymousFragment, BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, CollapsedMargin,
12+
FontMetrics, Fragment, TextFragment,
1313
};
1414
use crate::geom::flow_relative::{Rect, Sides, Vec2};
1515
use crate::positioned::{
@@ -349,30 +349,30 @@ impl InlineFormattingContext {
349349
.fragments_so_far
350350
.push(Fragment::AbsoluteOrFixedPositioned(hoisted_fragment));
351351
},
352-
InlineLevelBox::OutOfFlowFloatBox(box_) => {
353-
let mut fragment = box_.layout(
352+
InlineLevelBox::OutOfFlowFloatBox(float_box) => {
353+
let mut box_fragment = float_box.layout(
354354
layout_context,
355355
ifc.positioning_context,
356356
containing_block,
357-
ifc.sequential_layout_state.as_mut().map(|c| &mut **c),
358357
);
359-
if let Some(state) = &ifc.sequential_layout_state {
360-
let offset_from_formatting_context_to_containing_block = Vec2 {
361-
inline: state.floats.containing_block_info.inline_start,
362-
block: state.floats.containing_block_info.block_start +
363-
state
364-
.floats
365-
.containing_block_info
366-
.block_start_margins_not_collapsed
367-
.solve(),
368-
};
369-
if let Fragment::Float(ref mut box_fragment) = &mut fragment {
370-
box_fragment.content_rect.start_corner =
371-
&box_fragment.content_rect.start_corner -
372-
&offset_from_formatting_context_to_containing_block;
373-
}
374-
}
375-
ifc.current_nesting_level.fragments_so_far.push(fragment);
358+
359+
let state = ifc
360+
.sequential_layout_state
361+
.as_mut()
362+
.expect("Tried to lay out a float with no sequential placement state!");
363+
364+
let block_offset_from_containining_block_top = state
365+
.current_block_position_including_margins() -
366+
state.current_containing_block_offset();
367+
state.place_float_fragment(
368+
&mut box_fragment,
369+
CollapsedMargin::zero(),
370+
block_offset_from_containining_block_top,
371+
);
372+
373+
ifc.current_nesting_level
374+
.fragments_so_far
375+
.push(Fragment::Float(box_fragment));
376376
},
377377
}
378378
} else

0 commit comments

Comments
 (0)