layout/flow/
float.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! Float layout.
6//!
7//! See CSS 2.1 § 9.5.1: <https://www.w3.org/TR/CSS2/visuren.html#float-position>
8
9use std::collections::VecDeque;
10use std::fmt::Debug;
11use std::mem;
12use std::ops::Range;
13
14use app_units::{Au, MAX_AU, MIN_AU};
15use euclid::num::Zero;
16use malloc_size_of_derive::MallocSizeOf;
17use servo_arc::Arc;
18use style::computed_values::float::T as FloatProperty;
19use style::computed_values::position::T as Position;
20use style::logical_geometry::WritingMode;
21use style::properties::ComputedValues;
22use style::values::computed::Clear as StyleClear;
23
24use crate::context::LayoutContext;
25use crate::dom_traversal::{Contents, NodeAndStyleInfo};
26use crate::formatting_contexts::IndependentFormattingContext;
27use crate::fragment_tree::{BoxFragment, CollapsedMargin};
28use crate::geom::{LogicalRect, LogicalVec2, ToLogical};
29use crate::positioned::{PositioningContext, relative_adjustement};
30use crate::style_ext::{DisplayInside, PaddingBorderMargin};
31use crate::{ContainingBlock, PropagatedBoxTreeData};
32
33/// A floating box.
34#[derive(Debug, MallocSizeOf)]
35pub(crate) struct FloatBox {
36    /// The formatting context that makes up the content of this box.
37    pub contents: IndependentFormattingContext,
38}
39
40/// `FloatContext` positions floats relative to the independent block formatting
41/// context which contains the floating elements. The Fragment tree positions
42/// elements relative to their containing blocks. This data structure is used to
43/// help map between these two coordinate systems.
44#[derive(Clone, Copy, Debug)]
45pub struct ContainingBlockPositionInfo {
46    /// The distance from the block start of the independent block formatting
47    /// context that contains the floats and the block start of the current
48    /// containing block, excluding uncollapsed block start margins. Note that
49    /// this does not include uncollapsed block start margins because we don't
50    /// know the value of collapsed margins until we lay out children.
51    pub(crate) block_start: Au,
52    /// Any uncollapsed block start margins that we have collected between the
53    /// block start of the float containing independent block formatting context
54    /// and this containing block, including for this containing block.
55    pub(crate) block_start_margins_not_collapsed: CollapsedMargin,
56    /// The distance from the inline start position of the float containing
57    /// independent formatting context and the inline start of this containing
58    /// block.
59    pub inline_start: Au,
60    /// The offset from the inline start position of the float containing
61    /// independent formatting context to the inline end of this containing
62    /// block.
63    pub inline_end: Au,
64}
65
66impl ContainingBlockPositionInfo {
67    pub fn new_with_inline_offsets(inline_start: Au, inline_end: Au) -> Self {
68        Self {
69            block_start: Au::zero(),
70            block_start_margins_not_collapsed: CollapsedMargin::zero(),
71            inline_start,
72            inline_end,
73        }
74    }
75}
76
77/// This data strucure is used to try to place non-floating content among float content.
78/// This is used primarily to place replaced content and independent formatting contexts
79/// next to floats, as the specifcation dictates.
80pub(crate) struct PlacementAmongFloats<'a> {
81    /// The [FloatContext] to use for this placement.
82    float_context: &'a FloatContext,
83    /// The current bands we are considering for this placement.
84    current_bands: VecDeque<FloatBand>,
85    /// The next band, needed to know the height of the last band in current_bands.
86    next_band: FloatBand,
87    /// The size of the object to place.
88    object_size: LogicalVec2<Au>,
89    /// The minimum position in the block direction for the placement. Objects should not
90    /// be placed before this point.
91    ceiling: Au,
92    /// The inline position where the object would be if there were no floats. The object
93    /// can be placed after it due to floats, but not before it.
94    min_inline_start: Au,
95    /// The maximum inline position that the object can attain when avoiding floats.
96    max_inline_end: Au,
97}
98
99impl<'a> PlacementAmongFloats<'a> {
100    pub(crate) fn new(
101        float_context: &'a FloatContext,
102        ceiling: Au,
103        object_size: LogicalVec2<Au>,
104        pbm: &PaddingBorderMargin,
105    ) -> Self {
106        let mut ceiling_band = float_context.bands.find(ceiling).unwrap();
107        let (current_bands, next_band) = if ceiling == MAX_AU {
108            (VecDeque::new(), ceiling_band)
109        } else {
110            ceiling_band.top = ceiling;
111            let current_bands = VecDeque::from([ceiling_band]);
112            let next_band = float_context.bands.find_next(ceiling).unwrap();
113            (current_bands, next_band)
114        };
115        let min_inline_start = float_context.containing_block_info.inline_start +
116            pbm.margin.inline_start.auto_is(Au::zero);
117        let max_inline_end = (float_context.containing_block_info.inline_end -
118            pbm.margin.inline_end.auto_is(Au::zero))
119        .max(min_inline_start + object_size.inline);
120        PlacementAmongFloats {
121            float_context,
122            current_bands,
123            next_band,
124            object_size,
125            ceiling,
126            min_inline_start,
127            max_inline_end,
128        }
129    }
130
131    /// The top of the bands under consideration. This is initially the ceiling provided
132    /// during creation of this [`PlacementAmongFloats`], but may be larger if the top
133    /// band is discarded.
134    fn top_of_bands(&self) -> Option<Au> {
135        self.current_bands.front().map(|band| band.top)
136    }
137
138    /// The height of the bands under consideration.
139    fn current_bands_height(&self) -> Au {
140        if self.next_band.top == MAX_AU {
141            // Treat MAX_AU as infinity.
142            MAX_AU
143        } else {
144            let top = self
145                .top_of_bands()
146                .expect("Should have bands before reaching the end");
147            self.next_band.top - top
148        }
149    }
150
151    /// Add a single band to the bands under consideration and calculate the new
152    /// [`PlacementAmongFloats::next_band`].
153    fn add_one_band(&mut self) {
154        assert!(self.next_band.top != MAX_AU);
155        self.current_bands.push_back(self.next_band);
156        self.next_band = self
157            .float_context
158            .bands
159            .find_next(self.next_band.top)
160            .unwrap();
161    }
162
163    /// Adds bands to the set of bands under consideration until their block size is at
164    /// least large enough to contain the block size of the object being placed.
165    fn accumulate_enough_bands_for_block_size(&mut self) {
166        while self.current_bands_height() < self.object_size.block {
167            self.add_one_band();
168        }
169    }
170
171    /// Find the start and end of the inline space provided by the current set of bands
172    /// under consideration.
173    fn calculate_inline_start_and_end(&self) -> (Au, Au) {
174        let mut max_inline_start = self.min_inline_start;
175        let mut min_inline_end = self.max_inline_end;
176        for band in self.current_bands.iter() {
177            if let Some(inline_start) = band.inline_start {
178                max_inline_start.max_assign(inline_start);
179            }
180            if let Some(inline_end) = band.inline_end {
181                min_inline_end.min_assign(inline_end);
182            }
183        }
184        (max_inline_start, min_inline_end)
185    }
186
187    /// Find the total inline size provided by the current set of bands under consideration.
188    fn calculate_viable_inline_size(&self) -> Au {
189        let (inline_start, inline_end) = self.calculate_inline_start_and_end();
190        inline_end - inline_start
191    }
192
193    fn try_place_once(&mut self) -> Option<LogicalRect<Au>> {
194        assert!(!self.current_bands.is_empty());
195        self.accumulate_enough_bands_for_block_size();
196        let (inline_start, inline_end) = self.calculate_inline_start_and_end();
197        let available_inline_size = inline_end - inline_start;
198        if available_inline_size < self.object_size.inline {
199            return None;
200        }
201        Some(LogicalRect {
202            start_corner: LogicalVec2 {
203                inline: inline_start,
204                block: self.top_of_bands().unwrap(),
205            },
206            size: LogicalVec2 {
207                inline: available_inline_size,
208                block: self.current_bands_height(),
209            },
210        })
211    }
212
213    /// Checks if we either have bands or we have gone past all of them.
214    /// This is an invariant that should hold, otherwise we are in a broken state.
215    fn has_bands_or_at_end(&self) -> bool {
216        !self.current_bands.is_empty() || self.next_band.top == MAX_AU
217    }
218
219    fn pop_front_band_ensuring_has_bands_or_at_end(&mut self) {
220        self.current_bands.pop_front();
221        if !self.has_bands_or_at_end() {
222            self.add_one_band();
223        }
224    }
225
226    /// Run the placement algorithm for this [PlacementAmongFloats].
227    pub(crate) fn place(&mut self) -> LogicalRect<Au> {
228        debug_assert!(self.has_bands_or_at_end());
229        while !self.current_bands.is_empty() {
230            if let Some(result) = self.try_place_once() {
231                return result;
232            }
233            self.pop_front_band_ensuring_has_bands_or_at_end();
234        }
235        debug_assert!(self.has_bands_or_at_end());
236
237        // We could not fit the object in among the floats, so we place it as if it
238        // cleared all floats.
239        LogicalRect {
240            start_corner: LogicalVec2 {
241                inline: self.min_inline_start,
242                block: self
243                    .ceiling
244                    .max(self.float_context.clear_inline_start_position)
245                    .max(self.float_context.clear_inline_end_position),
246            },
247            size: LogicalVec2 {
248                inline: self.max_inline_end - self.min_inline_start,
249                block: MAX_AU,
250            },
251        }
252    }
253
254    /// After placing an object with `height: auto` (and using the minimum inline and
255    /// block size as the object size) and then laying it out, try to fit the object into
256    /// the current set of bands, given block size after layout and the available inline
257    /// space from the original placement. This will return true if the object fits at the
258    /// original placement location or false if the placement and layout must be run again
259    /// (with this [PlacementAmongFloats]).
260    pub(crate) fn try_to_expand_for_auto_block_size(
261        &mut self,
262        block_size_after_layout: Au,
263        size_from_placement: &LogicalVec2<Au>,
264    ) -> bool {
265        debug_assert!(self.has_bands_or_at_end());
266        debug_assert_eq!(size_from_placement.block, self.current_bands_height());
267        debug_assert_eq!(
268            size_from_placement.inline,
269            self.calculate_viable_inline_size()
270        );
271
272        // If the object after layout fits into the originally calculated placement, then
273        // it fits without any more work.
274        if block_size_after_layout <= size_from_placement.block {
275            return true;
276        }
277
278        // Keep searching until we have found an area with enough height
279        // to contain the block after layout.
280        let old_num_bands = self.current_bands.len();
281        assert!(old_num_bands > 0);
282        while self.current_bands_height() < block_size_after_layout {
283            self.add_one_band();
284
285            // If the new inline size is narrower, we must stop and run layout again.
286            // Normally, a narrower block size means a bigger height, but in some
287            // circumstances, such as when aspect ratio is used a narrower inline size
288            // can counter-interuitively lead to a smaller block size after layout!
289            let available_inline_size = self.calculate_viable_inline_size();
290            if available_inline_size < size_from_placement.inline {
291                // If the inline size becomes smaller than the minimum inline size, then
292                // the current set of bands will never work and we must try removing the
293                // first and searching starting from the second.
294                if available_inline_size < self.object_size.inline {
295                    self.next_band = self.current_bands[old_num_bands];
296                    self.current_bands.truncate(old_num_bands);
297                    self.pop_front_band_ensuring_has_bands_or_at_end();
298                }
299                return false;
300            }
301        }
302        true
303    }
304}
305
306/// Data kept during layout about the floats in a given block formatting context.
307///
308/// This is a persistent data structure. Each float has its own private copy of the float context,
309/// although such copies may share portions of the `bands` tree.
310#[derive(Clone, Debug)]
311pub struct FloatContext {
312    /// A persistent AA tree of float bands.
313    ///
314    /// This tree is immutable; modification operations return the new tree, which may share nodes
315    /// with previous versions of the tree.
316    pub bands: FloatBandTree,
317    /// The block-direction "ceiling" defined by the placement of other floated content of
318    /// this FloatContext. No new floats can be placed at a lower block start than this value.
319    pub ceiling_from_floats: Au,
320    /// The block-direction "ceiling" defined by the placement of non-floated content that
321    /// precedes floated content in the document. Note that this may actually decrease as
322    /// content is laid out in the case that content overflows its container.
323    pub ceiling_from_non_floats: Au,
324    /// Details about the position of the containing block relative to the
325    /// independent block formatting context that contains all of the floats
326    /// this `FloatContext` positions.
327    pub containing_block_info: ContainingBlockPositionInfo,
328    /// The (logically) lowest margin edge of the last inline-start float.
329    pub clear_inline_start_position: Au,
330    /// The (logically) lowest margin edge of the last inline-end float.
331    pub clear_inline_end_position: Au,
332}
333
334impl FloatContext {
335    /// Returns a new float context representing a containing block with the given content
336    /// inline-size.
337    pub fn new(max_inline_size: Au) -> Self {
338        let mut bands = FloatBandTree::new();
339        bands = bands.insert(FloatBand {
340            top: MIN_AU,
341            inline_start: None,
342            inline_end: None,
343        });
344        bands = bands.insert(FloatBand {
345            top: MAX_AU,
346            inline_start: None,
347            inline_end: None,
348        });
349        FloatContext {
350            bands,
351            ceiling_from_floats: Au::zero(),
352            ceiling_from_non_floats: Au::zero(),
353            containing_block_info: ContainingBlockPositionInfo::new_with_inline_offsets(
354                Au::zero(),
355                max_inline_size,
356            ),
357            clear_inline_start_position: Au::zero(),
358            clear_inline_end_position: Au::zero(),
359        }
360    }
361
362    /// (Logically) lowers the ceiling to at least `new_ceiling` units.
363    ///
364    /// If the ceiling is already logically lower (i.e. larger) than this, does nothing.
365    pub fn set_ceiling_from_non_floats(&mut self, new_ceiling: Au) {
366        self.ceiling_from_non_floats = new_ceiling;
367    }
368
369    /// The "ceiling" used for float placement. This is the minimum block position value
370    /// that should be used for placing any new float.
371    fn ceiling(&mut self) -> Au {
372        self.ceiling_from_floats.max(self.ceiling_from_non_floats)
373    }
374
375    /// Determines where a float with the given placement would go, but leaves the float context
376    /// unmodified. Returns the start corner of its margin box.
377    ///
378    /// This should be used for placing inline elements and block formatting contexts so that they
379    /// don't collide with floats.
380    pub(crate) fn place_object(&self, object: &PlacementInfo, ceiling: Au) -> LogicalVec2<Au> {
381        let ceiling = match object.clear {
382            Clear::None => ceiling,
383            Clear::InlineStart => ceiling.max(self.clear_inline_start_position),
384            Clear::InlineEnd => ceiling.max(self.clear_inline_end_position),
385            Clear::Both => ceiling
386                .max(self.clear_inline_start_position)
387                .max(self.clear_inline_end_position),
388        };
389
390        // Find the first band this float fits in.
391        let mut first_band = self.bands.find(ceiling).unwrap();
392        while !first_band.object_fits(object, &self.containing_block_info) {
393            let next_band = self.bands.find_next(first_band.top).unwrap();
394            if next_band.top == MAX_AU {
395                break;
396            }
397            first_band = next_band;
398        }
399
400        // The object fits perfectly here. Place it.
401        match object.side {
402            FloatSide::InlineStart => {
403                let inline_start_object_edge = match first_band.inline_start {
404                    Some(inline_start) => inline_start.max(self.containing_block_info.inline_start),
405                    None => self.containing_block_info.inline_start,
406                };
407                LogicalVec2 {
408                    inline: inline_start_object_edge,
409                    block: first_band.top.max(ceiling),
410                }
411            },
412            FloatSide::InlineEnd => {
413                let inline_end_object_edge = match first_band.inline_end {
414                    Some(inline_end) => inline_end.min(self.containing_block_info.inline_end),
415                    None => self.containing_block_info.inline_end,
416                };
417                LogicalVec2 {
418                    inline: inline_end_object_edge - object.size.inline,
419                    block: first_band.top.max(ceiling),
420                }
421            },
422        }
423    }
424
425    /// Places a new float and adds it to the list. Returns the start corner of its margin box.
426    pub fn add_float(&mut self, new_float: &PlacementInfo) -> LogicalVec2<Au> {
427        // Place the float.
428        let ceiling = self.ceiling();
429        let new_float_origin = self.place_object(new_float, ceiling);
430        let new_float_extent = match new_float.side {
431            FloatSide::InlineStart => new_float_origin.inline + new_float.size.inline,
432            FloatSide::InlineEnd => new_float_origin.inline,
433        };
434
435        let new_float_rect = LogicalRect {
436            start_corner: new_float_origin,
437            // If this float has a negative margin, we should only consider its non-negative
438            // block size contribution when determing where to place it. When the margin is
439            // so negative that it's placed completely above the current float ceiling, then
440            // we should position it as if it had zero block size.
441            size: LogicalVec2 {
442                inline: new_float.size.inline.max(Au::zero()),
443                block: new_float.size.block.max(Au::zero()),
444            },
445        };
446
447        // Update clear.
448        match new_float.side {
449            FloatSide::InlineStart => {
450                self.clear_inline_start_position
451                    .max_assign(new_float_rect.max_block_position());
452            },
453            FloatSide::InlineEnd => {
454                self.clear_inline_end_position
455                    .max_assign(new_float_rect.max_block_position());
456            },
457        }
458
459        // Split the first band if necessary.
460        let mut first_band = self.bands.find(new_float_rect.start_corner.block).unwrap();
461        first_band.top = new_float_rect.start_corner.block;
462        self.bands = self.bands.insert(first_band);
463
464        // Split the last band if necessary.
465        let mut last_band = self
466            .bands
467            .find(new_float_rect.max_block_position())
468            .unwrap();
469        last_band.top = new_float_rect.max_block_position();
470        self.bands = self.bands.insert(last_band);
471
472        // Update all bands that contain this float to reflect the new available size.
473        let block_range = new_float_rect.start_corner.block..new_float_rect.max_block_position();
474        self.bands = self
475            .bands
476            .set_range(&block_range, new_float.side, new_float_extent);
477
478        // CSS 2.1 § 9.5.1 rule 6: The outer top of a floating box may not be higher than the outer
479        // top of any block or floated box generated by an element earlier in the source document.
480        self.ceiling_from_floats
481            .max_assign(new_float_rect.start_corner.block);
482
483        new_float_rect.start_corner
484    }
485}
486
487#[derive(Clone, Copy, Debug, PartialEq)]
488pub enum Clear {
489    None,
490    InlineStart,
491    InlineEnd,
492    Both,
493}
494
495impl Clear {
496    pub(crate) fn from_style_and_container_writing_mode(
497        style: &ComputedValues,
498        container_writing_mode: WritingMode,
499    ) -> Self {
500        match style.get_box().clear {
501            StyleClear::None => Self::None,
502            StyleClear::Both => Self::Both,
503            StyleClear::InlineStart => Self::InlineStart,
504            StyleClear::InlineEnd => Self::InlineEnd,
505            StyleClear::Left if container_writing_mode.is_bidi_ltr() => Self::InlineStart,
506            StyleClear::Left => Self::InlineEnd,
507            StyleClear::Right if container_writing_mode.is_bidi_ltr() => Self::InlineEnd,
508            StyleClear::Right => Self::InlineStart,
509        }
510    }
511}
512
513/// Information needed to place a float so that it doesn't collide with existing floats.
514#[derive(Clone, Debug)]
515pub struct PlacementInfo {
516    /// The *margin* box size of the float.
517    pub size: LogicalVec2<Au>,
518    /// Which side of the containing block the float is aligned to.
519    pub side: FloatSide,
520    /// Which side or sides to clear existing floats on.
521    pub clear: Clear,
522}
523
524/// Whether the float is aligned to the inline-start or inline-end side of its containing block.
525///
526/// See CSS 2.1 § 9.5.1: <https://www.w3.org/TR/CSS2/visuren.html#float-position>
527#[derive(Clone, Copy, Debug, PartialEq)]
528pub enum FloatSide {
529    InlineStart,
530    InlineEnd,
531}
532
533/// Internal data structure that describes a nonoverlapping vertical region in which floats may be placed.
534/// Floats must go between "inline-start edge + `inline_start`" and "inline-end edge - `inline_end`".
535#[derive(Clone, Copy, Debug, PartialEq)]
536pub struct FloatBand {
537    /// The logical vertical position of the top of this band.
538    pub top: Au,
539    /// The distance from the inline-start edge of the block formatting context to the first legal
540    /// (logically) horizontal position where floats may be placed. If `None`, there are no floats
541    /// to the inline-start; distinguishing between the cases of "a zero-width float is present" and
542    /// "no floats at all are present" is necessary to, for example, clear past zero-width floats.
543    pub inline_start: Option<Au>,
544    /// The distance from the *inline-start* edge of the block formatting context to the last legal
545    /// (logically) horizontal position where floats may be placed. If `None`, there are no floats
546    /// to the inline-end; distinguishing between the cases of "a zero-width float is present" and
547    /// "no floats at all are present" is necessary to, for example, clear past zero-width floats.
548    pub inline_end: Option<Au>,
549}
550
551impl FloatSide {
552    pub(crate) fn from_style_and_container_writing_mode(
553        style: &ComputedValues,
554        container_writing_mode: WritingMode,
555    ) -> Option<FloatSide> {
556        Some(match style.get_box().float {
557            FloatProperty::None => return None,
558            FloatProperty::InlineStart => Self::InlineStart,
559            FloatProperty::InlineEnd => Self::InlineEnd,
560            FloatProperty::Left if container_writing_mode.is_bidi_ltr() => Self::InlineStart,
561            FloatProperty::Left => Self::InlineEnd,
562            FloatProperty::Right if container_writing_mode.is_bidi_ltr() => Self::InlineEnd,
563            FloatProperty::Right => Self::InlineStart,
564        })
565    }
566}
567
568impl FloatBand {
569    /// Determines whether an object fits in a band. Returns true if the object fits.
570    fn object_fits(&self, object: &PlacementInfo, walls: &ContainingBlockPositionInfo) -> bool {
571        match object.side {
572            FloatSide::InlineStart => {
573                // Compute a candidate inline-start position for the object.
574                let candidate_inline_start = match self.inline_start {
575                    None => walls.inline_start,
576                    Some(inline_start) => inline_start.max(walls.inline_start),
577                };
578
579                // If this band has an existing inline-start float in it, then make sure that the object
580                // doesn't stick out past the inline-end edge (rule 7).
581                if self.inline_start.is_some() &&
582                    candidate_inline_start + object.size.inline > walls.inline_end
583                {
584                    return false;
585                }
586
587                // If this band has an existing inline-end float in it, make sure we don't collide with
588                // it (rule 3).
589                match self.inline_end {
590                    None => true,
591                    Some(inline_end) => object.size.inline <= inline_end - candidate_inline_start,
592                }
593            },
594
595            FloatSide::InlineEnd => {
596                // Compute a candidate inline-end position for the object.
597                let candidate_inline_end = match self.inline_end {
598                    None => walls.inline_end,
599                    Some(inline_end) => inline_end.min(walls.inline_end),
600                };
601
602                // If this band has an existing inline-end float in it, then make sure that the new
603                // object doesn't stick out past the inline-start edge (rule 7).
604                if self.inline_end.is_some() &&
605                    candidate_inline_end - object.size.inline < walls.inline_start
606                {
607                    return false;
608                }
609
610                // If this band has an existing inline-start float in it, make sure we don't collide with
611                // it (rule 3).
612                match self.inline_start {
613                    None => true,
614                    Some(inline_start) => object.size.inline <= candidate_inline_end - inline_start,
615                }
616            },
617        }
618    }
619}
620
621// Float band storage
622
623/// A persistent AA tree for float band storage.
624///
625/// Bands here are nonoverlapping, and there is guaranteed to be a band at block-position 0 and
626/// another band at block-position infinity.
627///
628/// AA trees were chosen for simplicity.
629///
630/// See: <https://en.wikipedia.org/wiki/AA_tree>
631///      <https://arxiv.org/pdf/1412.4882.pdf>
632#[derive(Clone, Debug)]
633pub struct FloatBandTree {
634    pub root: FloatBandLink,
635}
636
637/// A single edge (or lack thereof) in the float band tree.
638#[derive(Clone, Debug)]
639pub struct FloatBandLink(pub Option<Arc<FloatBandNode>>);
640
641/// A single node in the float band tree.
642#[derive(Clone, Debug)]
643pub struct FloatBandNode {
644    /// The actual band.
645    pub band: FloatBand,
646    /// The left child.
647    pub left: FloatBandLink,
648    /// The right child.
649    pub right: FloatBandLink,
650    /// The level, which increases as you go up the tree.
651    ///
652    /// This value is needed for tree balancing.
653    pub level: i32,
654}
655
656impl FloatBandTree {
657    /// Creates a new float band tree.
658    pub fn new() -> FloatBandTree {
659        FloatBandTree {
660            root: FloatBandLink(None),
661        }
662    }
663
664    /// Returns the first band whose top is less than or equal to the given `block_position`.
665    pub fn find(&self, block_position: Au) -> Option<FloatBand> {
666        self.root.find(block_position)
667    }
668
669    /// Returns the first band whose top is strictly greater than to the given `block_position`.
670    pub fn find_next(&self, block_position: Au) -> Option<FloatBand> {
671        self.root.find_next(block_position)
672    }
673
674    /// Sets the side values of all bands within the given half-open range to be at least
675    /// `new_value`.
676    #[must_use]
677    pub fn set_range(&self, range: &Range<Au>, side: FloatSide, new_value: Au) -> FloatBandTree {
678        FloatBandTree {
679            root: FloatBandLink(
680                self.root
681                    .0
682                    .as_ref()
683                    .map(|root| root.set_range(range, side, new_value)),
684            ),
685        }
686    }
687
688    /// Inserts a new band into the tree. If the band has the same level as a pre-existing one,
689    /// replaces the existing band with the new one.
690    #[must_use]
691    pub fn insert(&self, band: FloatBand) -> FloatBandTree {
692        FloatBandTree {
693            root: self.root.insert(band),
694        }
695    }
696}
697
698impl Default for FloatBandTree {
699    fn default() -> Self {
700        Self::new()
701    }
702}
703
704impl FloatBandNode {
705    fn new(band: FloatBand) -> FloatBandNode {
706        FloatBandNode {
707            band,
708            left: FloatBandLink(None),
709            right: FloatBandLink(None),
710            level: 1,
711        }
712    }
713
714    /// Sets the side values of all bands within the given half-open range to be at least
715    /// `new_value`.
716    fn set_range(&self, range: &Range<Au>, side: FloatSide, new_value: Au) -> Arc<FloatBandNode> {
717        let mut new_band = self.band;
718        if self.band.top >= range.start && self.band.top < range.end {
719            match side {
720                FloatSide::InlineStart => {
721                    new_band.inline_start = match new_band.inline_start {
722                        Some(old_value) => Some(std::cmp::max(old_value, new_value)),
723                        None => Some(new_value),
724                    };
725                },
726                FloatSide::InlineEnd => {
727                    new_band.inline_end = match new_band.inline_end {
728                        Some(old_value) => Some(std::cmp::min(old_value, new_value)),
729                        None => Some(new_value),
730                    };
731                },
732            }
733        }
734
735        let new_left = match self.left.0 {
736            None => FloatBandLink(None),
737            Some(ref old_left) if range.start < new_band.top => {
738                FloatBandLink(Some(old_left.set_range(range, side, new_value)))
739            },
740            Some(ref old_left) => FloatBandLink(Some((*old_left).clone())),
741        };
742
743        let new_right = match self.right.0 {
744            None => FloatBandLink(None),
745            Some(ref old_right) if range.end > new_band.top => {
746                FloatBandLink(Some(old_right.set_range(range, side, new_value)))
747            },
748            Some(ref old_right) => FloatBandLink(Some((*old_right).clone())),
749        };
750
751        Arc::new(FloatBandNode {
752            band: new_band,
753            left: new_left,
754            right: new_right,
755            level: self.level,
756        })
757    }
758}
759
760impl FloatBandLink {
761    /// Returns the first band whose top is less than or equal to the given `block_position`.
762    fn find(&self, block_position: Au) -> Option<FloatBand> {
763        let this = match self.0 {
764            None => return None,
765            Some(ref node) => node,
766        };
767
768        if block_position < this.band.top {
769            return this.left.find(block_position);
770        }
771
772        // It's somewhere in this subtree, but we aren't sure whether it's here or in the right
773        // subtree.
774        if let Some(band) = this.right.find(block_position) {
775            return Some(band);
776        }
777
778        Some(this.band)
779    }
780
781    /// Returns the first band whose top is strictly greater than the given `block_position`.
782    fn find_next(&self, block_position: Au) -> Option<FloatBand> {
783        let this = match self.0 {
784            None => return None,
785            Some(ref node) => node,
786        };
787
788        if block_position >= this.band.top {
789            return this.right.find_next(block_position);
790        }
791
792        // It's somewhere in this subtree, but we aren't sure whether it's here or in the left
793        // subtree.
794        if let Some(band) = this.left.find_next(block_position) {
795            return Some(band);
796        }
797
798        Some(this.band)
799    }
800
801    /// Inserts a new band into the tree. If the band has the same level as a pre-existing one,
802    /// replaces the existing band with the new one.
803    fn insert(&self, band: FloatBand) -> FloatBandLink {
804        let mut this = match self.0 {
805            None => return FloatBandLink(Some(Arc::new(FloatBandNode::new(band)))),
806            Some(ref this) => (**this).clone(),
807        };
808
809        if band.top < this.band.top {
810            this.left = this.left.insert(band);
811            return FloatBandLink(Some(Arc::new(this))).skew().split();
812        }
813        if band.top > this.band.top {
814            this.right = this.right.insert(band);
815            return FloatBandLink(Some(Arc::new(this))).skew().split();
816        }
817
818        this.band = band;
819        FloatBandLink(Some(Arc::new(this)))
820    }
821
822    /// Corrects tree balance:
823    ///```text
824    ///         T          L
825    ///        / \        / \
826    ///       L   R  →   A   T      if level(T) = level(L)
827    ///      / \            / \
828    ///     A   B          B   R
829    /// ```
830    fn skew(&self) -> FloatBandLink {
831        if let Some(ref this) = self.0 {
832            if let Some(ref left) = this.left.0 {
833                if this.level == left.level {
834                    return FloatBandLink(Some(Arc::new(FloatBandNode {
835                        level: this.level,
836                        left: left.left.clone(),
837                        band: left.band,
838                        right: FloatBandLink(Some(Arc::new(FloatBandNode {
839                            level: this.level,
840                            left: left.right.clone(),
841                            band: this.band,
842                            right: this.right.clone(),
843                        }))),
844                    })));
845                }
846            }
847        }
848
849        (*self).clone()
850    }
851
852    /// Corrects tree balance:
853    ///```text
854    ///         T            R
855    ///        / \          / \
856    ///       A   R   →    T   X    if level(T) = level(X)
857    ///          / \      / \
858    ///         B   X    A   B
859    /// ```
860    fn split(&self) -> FloatBandLink {
861        if let Some(ref this) = self.0 {
862            if let Some(ref right) = this.right.0 {
863                if let Some(ref right_right) = right.right.0 {
864                    if this.level == right_right.level {
865                        return FloatBandLink(Some(Arc::new(FloatBandNode {
866                            level: this.level + 1,
867                            left: FloatBandLink(Some(Arc::new(FloatBandNode {
868                                level: this.level,
869                                left: this.left.clone(),
870                                band: this.band,
871                                right: right.left.clone(),
872                            }))),
873                            band: right.band,
874                            right: right.right.clone(),
875                        })));
876                    }
877                }
878            }
879        }
880
881        (*self).clone()
882    }
883}
884
885impl FloatBox {
886    /// Creates a new float box.
887    pub fn construct(
888        context: &LayoutContext,
889        info: &NodeAndStyleInfo<'_>,
890        display_inside: DisplayInside,
891        contents: Contents,
892        propagated_data: PropagatedBoxTreeData,
893    ) -> Self {
894        Self {
895            contents: IndependentFormattingContext::construct(
896                context,
897                info,
898                display_inside,
899                contents,
900                propagated_data,
901            ),
902        }
903    }
904
905    /// Lay out this float box and its children. Note that the position will be relative to
906    /// the float containing block formatting context. A later step adjusts the position
907    /// to be relative to the containing block.
908    pub fn layout(
909        &self,
910        layout_context: &LayoutContext,
911        positioning_context: &mut PositioningContext,
912        containing_block: &ContainingBlock,
913    ) -> BoxFragment {
914        positioning_context.layout_maybe_position_relative_fragment(
915            layout_context,
916            containing_block,
917            &self.contents.base,
918            |positioning_context| {
919                self.contents
920                    .layout_float_or_atomic_inline(
921                        layout_context,
922                        positioning_context,
923                        containing_block,
924                    )
925                    .fragment
926            },
927        )
928    }
929}
930
931/// Layout state that we maintain when doing sequential traversals of the box tree in document
932/// order.
933///
934/// This data is only needed for float placement and float interaction, and as such is only present
935/// if the current block formatting context contains floats.
936///
937/// All coordinates here are relative to the start of the nearest ancestor block formatting context.
938///
939/// This structure is expected to be cheap to clone, in order to allow for "snapshots" that enable
940/// restarting layout at any point in the tree.
941#[derive(Clone)]
942pub(crate) struct SequentialLayoutState {
943    /// Holds all floats in this block formatting context.
944    pub(crate) floats: FloatContext,
945    /// The (logically) bottom border edge or top padding edge of the last in-flow block. Floats
946    /// cannot be placed above this line.
947    ///
948    /// This is often, but not always, the same as the float ceiling. The float ceiling can be lower
949    /// than this value because this value is calculated based on in-flow boxes only, while
950    /// out-of-flow floats can affect the ceiling as well (see CSS 2.1 § 9.5.1 rule 6).
951    pub(crate) bfc_relative_block_position: Au,
952    /// Any collapsible margins that we've encountered after `bfc_relative_block_position`.
953    pub(crate) current_margin: CollapsedMargin,
954}
955
956impl SequentialLayoutState {
957    /// Creates a new empty `SequentialLayoutState`.
958    pub(crate) fn new(max_inline_size: Au) -> SequentialLayoutState {
959        SequentialLayoutState {
960            floats: FloatContext::new(max_inline_size),
961            current_margin: CollapsedMargin::zero(),
962            bfc_relative_block_position: Au::zero(),
963        }
964    }
965
966    /// Moves the current block position (logically) down by `block_distance`. This may be
967    /// a negative advancement in the case that that block content overflows its
968    /// container, when the container is adjusting the block position of the
969    /// [`SequentialLayoutState`] after processing its overflowing content.
970    ///
971    /// Floats may not be placed higher than the current block position.
972    pub(crate) fn advance_block_position(&mut self, block_distance: Au) {
973        self.bfc_relative_block_position += block_distance;
974        self.floats
975            .set_ceiling_from_non_floats(self.bfc_relative_block_position);
976    }
977
978    /// Replace the entire [ContainingBlockPositionInfo] data structure stored
979    /// by this [SequentialLayoutState]. Return the old data structure.
980    pub(crate) fn replace_containing_block_position_info(
981        &mut self,
982        mut position_info: ContainingBlockPositionInfo,
983    ) -> ContainingBlockPositionInfo {
984        mem::swap(&mut position_info, &mut self.floats.containing_block_info);
985        position_info
986    }
987
988    /// Return the current block position in the float containing block formatting
989    /// context and any uncollapsed block margins.
990    pub(crate) fn current_block_position_including_margins(&self) -> Au {
991        self.bfc_relative_block_position + self.current_margin.solve()
992    }
993
994    /// Collapses margins, moving the block position down by the collapsed value of `current_margin`
995    /// and resetting `current_margin` to zero.
996    ///
997    /// Call this method before laying out children when it is known that the start margin of the
998    /// current fragment can't collapse with the margins of any of its children.
999    pub(crate) fn collapse_margins(&mut self) {
1000        self.advance_block_position(self.current_margin.solve());
1001        self.current_margin = CollapsedMargin::zero();
1002    }
1003
1004    /// Computes the position of the block-start border edge of an element
1005    /// with the provided `block_start_margin`, assuming no clearance.
1006    pub(crate) fn position_without_clearance(&self, block_start_margin: &CollapsedMargin) -> Au {
1007        // Adjoin `current_margin` and `block_start_margin` since there is no clearance.
1008        self.bfc_relative_block_position + self.current_margin.adjoin(block_start_margin).solve()
1009    }
1010
1011    /// Computes the position of the block-start border edge of an element
1012    /// with the provided `block_start_margin`, assuming a clearance of 0px.
1013    pub(crate) fn position_with_zero_clearance(&self, block_start_margin: &CollapsedMargin) -> Au {
1014        // Clearance prevents `current_margin` and `block_start_margin` from being
1015        // adjoining, so we need to solve them separately and then sum.
1016        self.bfc_relative_block_position + self.current_margin.solve() + block_start_margin.solve()
1017    }
1018
1019    /// Returns the block-end outer edge of the lowest float that is to be cleared (if any)
1020    /// by an element with the provided `clear` and `block_start_margin`.
1021    pub(crate) fn calculate_clear_position(
1022        &self,
1023        clear: Clear,
1024        block_start_margin: &CollapsedMargin,
1025    ) -> Option<Au> {
1026        if clear == Clear::None {
1027            return None;
1028        }
1029
1030        // Calculate the hypothetical position where the element's top border edge
1031        // would have been if the element's `clear` property had been `none`.
1032        let hypothetical_block_position = self.position_without_clearance(block_start_margin);
1033
1034        // Check if the hypothetical position is past the relevant floats,
1035        // in that case we don't need to add clearance.
1036        let clear_position = match clear {
1037            Clear::None => unreachable!(),
1038            Clear::InlineStart => self.floats.clear_inline_start_position,
1039            Clear::InlineEnd => self.floats.clear_inline_end_position,
1040            Clear::Both => self
1041                .floats
1042                .clear_inline_start_position
1043                .max(self.floats.clear_inline_end_position),
1044        };
1045        if hypothetical_block_position >= clear_position {
1046            None
1047        } else {
1048            Some(clear_position)
1049        }
1050    }
1051
1052    /// Returns the amount of clearance (if any) that a block with the given `clear` value
1053    /// needs to have at `current_block_position_including_margins()`.
1054    /// `block_start_margin` is the top margin of the block, after collapsing (if possible)
1055    /// with the margin of its contents. This must not be included in `current_margin`,
1056    /// since adding clearance will prevent `current_margin` and `block_start_margin`
1057    /// from collapsing together.
1058    ///
1059    /// <https://www.w3.org/TR/2011/REC-CSS2-20110607/visuren.html#flow-control>
1060    pub(crate) fn calculate_clearance(
1061        &self,
1062        clear: Clear,
1063        block_start_margin: &CollapsedMargin,
1064    ) -> Option<Au> {
1065        self.calculate_clear_position(clear, block_start_margin)
1066            .map(|offset| offset - self.position_with_zero_clearance(block_start_margin))
1067    }
1068
1069    /// Adds a new adjoining margin.
1070    pub(crate) fn adjoin_assign(&mut self, margin: &CollapsedMargin) {
1071        self.current_margin.adjoin_assign(margin)
1072    }
1073
1074    /// Get the offset of the current containing block and any uncollapsed margins.
1075    pub(crate) fn current_containing_block_offset(&self) -> Au {
1076        self.floats.containing_block_info.block_start +
1077            self.floats
1078                .containing_block_info
1079                .block_start_margins_not_collapsed
1080                .solve()
1081    }
1082
1083    /// This function places a Fragment that has been created for a FloatBox.
1084    pub(crate) fn place_float_fragment(
1085        &mut self,
1086        box_fragment: &mut BoxFragment,
1087        containing_block: &ContainingBlock,
1088        margins_collapsing_with_parent_containing_block: CollapsedMargin,
1089        block_offset_from_containing_block_top: Au,
1090    ) {
1091        let block_start_of_containing_block_in_bfc = self.floats.containing_block_info.block_start +
1092            self.floats
1093                .containing_block_info
1094                .block_start_margins_not_collapsed
1095                .adjoin(&margins_collapsing_with_parent_containing_block)
1096                .solve();
1097
1098        self.floats.set_ceiling_from_non_floats(
1099            block_start_of_containing_block_in_bfc + block_offset_from_containing_block_top,
1100        );
1101
1102        let container_writing_mode = containing_block.style.writing_mode;
1103        let logical_float_size = box_fragment
1104            .content_rect
1105            .size
1106            .to_logical(container_writing_mode);
1107        let pbm_sums = box_fragment
1108            .padding_border_margin()
1109            .to_logical(container_writing_mode);
1110        let margin_box_start_corner = self.floats.add_float(&PlacementInfo {
1111            size: logical_float_size + pbm_sums.sum(),
1112            side: FloatSide::from_style_and_container_writing_mode(
1113                &box_fragment.style,
1114                container_writing_mode,
1115            )
1116            .expect("Float box wasn't floated!"),
1117            clear: Clear::from_style_and_container_writing_mode(
1118                &box_fragment.style,
1119                container_writing_mode,
1120            ),
1121        });
1122
1123        // Re-calculate relative adjustment so that it is not lost when the BoxFragment's
1124        // `content_rect` is overwritten below.
1125        let relative_offset = match box_fragment.style.clone_position() {
1126            Position::Relative => relative_adjustement(&box_fragment.style, containing_block),
1127            _ => LogicalVec2::zero(),
1128        };
1129
1130        // This is the position of the float in the float-containing block formatting context. We add the
1131        // existing start corner here because we may have already gotten some relative positioning offset.
1132        let new_position_in_bfc =
1133            margin_box_start_corner + pbm_sums.start_offset() + relative_offset;
1134
1135        // This is the position of the float relative to the containing block start.
1136        let new_position_in_containing_block = LogicalVec2 {
1137            inline: new_position_in_bfc.inline - self.floats.containing_block_info.inline_start,
1138            block: new_position_in_bfc.block - block_start_of_containing_block_in_bfc,
1139        };
1140
1141        box_fragment.content_rect = LogicalRect {
1142            start_corner: new_position_in_containing_block,
1143            size: box_fragment
1144                .content_rect
1145                .size
1146                .to_logical(container_writing_mode),
1147        }
1148        .as_physical(Some(containing_block));
1149    }
1150}