Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
Rust
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217//! CSS 2.1 10.6 block height resolution, isolated behind one function with plain-data//! inputs and outputs (`roadmap.md` -> "Phase 1 — Engine bootstrap" step 4, "Generated//! algorithms, one category at a time"). A generated candidate implements//! [`HeightResolver`] against the types in this module alone -- no `dom` beyond//! `LengthPercentageOrAuto` and the workspace fixed-point type, no Stylo -- which is what//! lets a candidate crate compile independently of the DOM/style layer and lets the runner//! swap resolvers without touching anything else in `fragment.rs`.//!//! [`resolve_baseline`] is the hand rule `fragment.rs` used before this extraction,//! unchanged: a box whose own `height` is a definite length uses it (10.5/10.6.4, the//! [`definite_content_height`] test), and everything else -- `auto`, a percentage against//! an indefinite basis, an unresolved `calc()` -- falls back to 10.6.3's rule, the distance//! from the content top edge to the bottom margin edge of the last in-flow child, extended//! for a box that establishes a block formatting context to the bottom margin edge of its//! lowest float (10.6.7). It is this seam's baseline candidate, and unlike the other seams'//! baselines it is already correct for every shape the corpus reaches rather than a//! deliberately-wrong placeholder.//!//! Two boundaries a candidate has to know about.//!//! `min-height`/`max-height` clamping (10.7) is **not** applied anywhere in this path and//! never was: the pre-extraction code in `fragment.rs` read neither property, and this//! extraction preserves that rather than adding clamping silently. It is an out-of-scope//! gap of this seam, not a rule a candidate is being asked to reproduce//! (`specs/block-height-equation.md` -> "Scope"), and no corpus case can reward one for//! implementing it, since nothing here hands it a `min-height` to clamp against.//!//! The 1/64px grid (`specs/device-pixel-rounding.md`) is applied on the definite branch//! only, inside [`definite_content_height`], exactly where it was before this extraction --//! that value is a basis a child's percentage `height` resolves against, so it has to be//! on-grid before it is carried down. The 10.6.3 fallback branch is returned unquantized,//! because its two callers need it at different stages: `fragment.rs`'s `layout_box`//! quantizes the result at the point it becomes that box's used content height, while//! `layout_absolute_box` hands it on as an intermediate to the absolute seam's resolver//! (`crate::absolute::AbsoluteInputs::content_height`), which quantizes downstream of its//! own equation. Quantizing here would move the second caller's value off what CSS 2.1//! 10.6.4 computed from.
use dom::computed::LengthPercentageOrAuto;use dom::px64::{from_px64, to_px64_truncating};
/// Plain-data inputs to the height equation, narrowed to what CSS 2.1 10.6 reads: the/// box's own `height` already resolved past percentages by the percentage seam, its/// `box-sizing` and top/bottom edges (needed to convert a `border-box`-sized `height` back/// to a content height), whether it establishes a block formatting context, the content/// height its stacked in-flow children reached, and the bottom margin edge of the lowest/// float in the context it owns.#[derive(Debug, Clone, Copy, PartialEq)]pub struct HeightInputs { /// This box's own computed `height`, already run through the percentage seam. A /// `Percentage` still standing here is one that found no definite basis to resolve /// against, which 10.5 sends to the same `auto` fallback as `Auto` itself. pub resolved_height: LengthPercentageOrAuto, /// Whether this box establishes a block formatting context, the condition 10.6.7's /// float extension is gated on. A float in an inner context never reaches this box. pub establishes_bfc: bool, /// The distance from this box's content top edge to the bottom margin edge of its last /// in-flow child, as the stacking walk computed it (10.6.3). pub content_height_from_children: f64, /// The bottom margin edge of the lowest float in the block formatting context this box /// owns, measured from its own content top edge, or `None` when it establishes no /// context of its own. A box that establishes one but holds no float carries /// `Some(0.0)`. pub lowest_float_bottom: Option<f64>, /// Whether `height` is measured against the border box rather than the content box. pub border_box_sizing: bool, /// `padding-top + padding-bottom + border-top-width + border-bottom-width`, the amount /// a `border-box`-sized `height` has to give back to become a content height. pub edge_tb: f64,}
/// The used content height CSS 2.1 10.6 resolves (content-box, independent of/// `box-sizing` -- CSS 2.1's box model predates it).#[derive(Debug, Clone, Copy, PartialEq)]pub struct HeightOutputs { pub content_height: f64,}
/// One candidate's implementation of the seam: plain data in, plain data out. Every/// candidate module the generation loop compiles exports a function of this type named/// `resolve`.pub type HeightResolver = fn(&HeightInputs) -> HeightOutputs;
/// The hand rule `fragment.rs` shipped before this extraction, unchanged: a definite own/// `height` is the used content height, and everything else falls back to 10.6.3's/// last-in-flow-child distance, extended by 10.6.7's lowest float for a block-formatting-/// context root. This is the seam's baseline candidate, and it is already correct for/// every shape the corpus reaches (this module's own doc comment).pub fn resolve_baseline(inputs: &HeightInputs) -> HeightOutputs { let float_bottom = inputs.lowest_float_bottom.unwrap_or(0.0); let content_height = definite_content_height(inputs.resolved_height, inputs.border_box_sizing, inputs.edge_tb) .unwrap_or(inputs.content_height_from_children.max(float_bottom)); HeightOutputs { content_height }}
/// Whether this box's own `height` gives it a definite content height, and what that/// height is (CSS 2.1 10.5/10.6.4). `None` is the answer for `auto`, for a percentage the/// percentage seam found no definite basis for, and for anything else unresolved/// (`calc()`, an intrinsic-sizing keyword) -- every shape 10.6.3's fallback is what/// [`resolve_baseline`] reaches for.////// `fragment.rs` calls this directly too, before recursing into a box's children, because/// it is also the basis a child's own percentage `height` resolves against and that has to/// be known before the children are laid out -- 10.5 branches on whether this box's own/// height is *specified* as a definite length, which never depends on its children.////// Quantized onto the 1/64px grid at the point the basis is chosen, for the same reason the/// width chain is (`fragment.rs`'s `quantize_px` doc comment): this is what a child's/// percentage `height` resolves against, so leaving it off-grid would let the block axis/// compound the way the inline axis did.pub fn definite_content_height( resolved_height: LengthPercentageOrAuto, border_box_sizing: bool, edge_tb: f64,) -> Option<f64> { match resolved_height { LengthPercentageOrAuto::Length(h) if border_box_sizing => Some(quantize_px((h - edge_tb).max(0.0))), LengthPercentageOrAuto::Length(h) => Some(quantize_px(h)), LengthPercentageOrAuto::Auto | LengthPercentageOrAuto::Percentage(_) | LengthPercentageOrAuto::Unresolved => None, }}
/// Quantizes one used value onto the 1/64px grid, through the workspace's single/// fixed-point type (`dom::px64`) rather than a second copy of the same arithmetic --/// the seam's own copy of `fragment.rs`'s `quantize_px`, kept here so a candidate crate/// compiling against this module alone reaches the same grid.fn quantize_px(v: f64) -> f64 { from_px64(to_px64_truncating(v))}
#[cfg(test)]mod tests { use super::*;
fn inputs(resolved_height: LengthPercentageOrAuto, content_height_from_children: f64) -> HeightInputs { HeightInputs { resolved_height, establishes_bfc: false, content_height_from_children, lowest_float_bottom: None, border_box_sizing: false, edge_tb: 0.0, } }
#[test] fn baseline_falls_back_to_the_last_in_flow_child_when_auto() { let out = resolve_baseline(&inputs(LengthPercentageOrAuto::Auto, 140.0)); assert_eq!(out.content_height, 140.0); }
#[test] fn baseline_passes_an_explicit_content_box_height_through() { let out = resolve_baseline(&inputs(LengthPercentageOrAuto::Length(220.0), 140.0)); assert_eq!(out.content_height, 220.0); }
#[test] fn baseline_subtracts_the_vertical_edges_under_border_box_sizing() { let mut i = inputs(LengthPercentageOrAuto::Length(220.0), 0.0); i.border_box_sizing = true; i.edge_tb = 20.0; assert_eq!(resolve_baseline(&i).content_height, 200.0); }
#[test] fn baseline_extends_an_auto_height_to_the_lowest_float() { let mut i = inputs(LengthPercentageOrAuto::Auto, 40.0); i.establishes_bfc = true; i.lowest_float_bottom = Some(90.0); assert_eq!(resolve_baseline(&i).content_height, 90.0); }
#[test] fn baseline_ignores_a_float_shorter_than_the_stacked_children() { let mut i = inputs(LengthPercentageOrAuto::Auto, 140.0); i.establishes_bfc = true; i.lowest_float_bottom = Some(90.0); assert_eq!(resolve_baseline(&i).content_height, 140.0); }
#[test] fn baseline_ignores_a_float_when_the_height_is_definite() { let mut i = inputs(LengthPercentageOrAuto::Length(50.0), 140.0); i.establishes_bfc = true; i.lowest_float_bottom = Some(90.0); assert_eq!(resolve_baseline(&i).content_height, 50.0); }
/// An unresolved percentage height reaches the same 10.6.3 fallback `auto` does -- /// the fallthrough `specs/block-height-equation.md` puts in scope precisely because /// it already existed rather than because it is new. #[test] fn baseline_falls_back_for_a_percentage_with_no_definite_basis() { let out = resolve_baseline(&inputs(LengthPercentageOrAuto::Percentage(0.5), 140.0)); assert_eq!(out.content_height, 140.0); }
#[test] fn definite_content_height_is_none_for_auto() { assert_eq!(definite_content_height(LengthPercentageOrAuto::Auto, false, 0.0), None); }
#[test] fn definite_content_height_never_goes_negative_under_border_box_sizing() { assert_eq!(definite_content_height(LengthPercentageOrAuto::Length(10.0), true, 30.0), Some(0.0)); }
/// The definite branch lands on the 1/64px grid; the fallback branch is left for the /// caller to quantize (this module's own doc comment). #[test] fn definite_content_height_quantizes_onto_the_grid() { let h = definite_content_height(LengthPercentageOrAuto::Length(10.3), false, 0.0).unwrap(); assert_eq!(h, 659.0 / 64.0); }}