Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
Rust
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136//! CSS 2.1 8.3.1 margin collapsing, isolated beside `width.rs`'s seam (`roadmap.md` ->//! "Phase 1 -- Engine bootstrap" step 4, the bootstrap order's second category). Two//! plain-data functions a generated candidate implements://!//! - [`classify`]-shaped ([`MarginClassifyResolver`]): decides, for one box, whether it//! is self-collapsing (CSS 2.1 8.3.1 case 4 -- its own top and bottom margins merge//! into one, since nothing separates them) and whether its top/bottom margin is free//! to adjoin with whatever precedes or follows it -- a parent's own margin (cases 1//! and 2), a preceding in-flow sibling's margin (case 3), or nothing at all, contained//! by this box's own border/padding or blocked by a new block formatting context//! (`crates/logic/src/facts.rs`'s `establishes_bfc`).//! - [`combine`]-shaped ([`MarginCombineResolver`]): reduces one group of adjoining raw//! margin values to the single value CSS 2.1 8.3.1 says is used -- the largest positive//! margin plus the most negative one.//!//! `crates/layout/src/fragment.rs` owns the structural walk that decides *which* boxes//! belong to the same adjoining group (mirroring `crates/logic/src/facts.rs`'s//! tree-shape facts, computed in plain Rust); these two functions own the CSS-semantic//! judgment calls the walk defers to (mirroring `crates/logic/src/rules.rs`, which//! `docs/margin-collapse-experiment.md` validated the same eight-condition/six-arm shape//! for -- `crates/logic/src/mutants.rs` `Mutant::ALL`).//!//! [`classify_baseline`]/[`combine_baseline`] are the hand rule `fragment.rs` used before//! this extraction: no adjoining, no self-collapsing, nothing escapes -- every margin is//! used exactly as declared. This seam's baseline candidate, not a spec-complete//! implementation (mirrors [`crate::width::resolve_block_width_baseline`]).
/// Plain-data inputs to one box's margin-collapsing classification, narrowed to what CSS/// 2.1 8.3.1 reads. `height_is_auto` and `height_is_explicit_zero` are kept separate/// rather than pre-merged into one "zero or auto" flag, since 8.3.1 treats an explicit/// `height: 0` the same as `auto` for self-collapsing/// (`docs/margin-collapse-experiment.md`'s `zero-height-not-self-collapsing` mutant is/// exactly a candidate that only recognises the `auto` case).#[derive(Debug, Clone, Copy, PartialEq, Eq)]pub struct MarginCollapseInputs { pub has_top_border_or_padding: bool, pub has_bottom_border_or_padding: bool, /// Whether this box establishes a new block formatting context (non-`visible` /// `overflow-x`/`overflow-y`), which blocks its own margins from collapsing with an /// in-flow child's (`crates/logic/src/facts.rs`'s `establishes_bfc`; the corpus's /// `recipe_collapse_bfc_root`). pub establishes_bfc: bool, pub height_is_auto: bool, pub height_is_explicit_zero: bool, /// True when this box has no in-flow children, or every in-flow child's own /// [`MarginCollapseOutputs::self_collapsing`] came back true -- `fragment.rs` /// computes this from the children's own classification, always available first /// since children are laid out before their parent. pub no_in_flow_content: bool,}
/// One box's margin-collapsing classification.#[derive(Debug, Clone, Copy, PartialEq, Eq)]pub struct MarginCollapseOutputs { /// CSS 2.1 8.3.1 case 4: this box's own top and bottom margins collapse into one, /// and the box itself takes zero height. pub self_collapsing: bool, /// Whether this box's margin-top is free to adjoin with its own parent's margin-top /// (case 1, only consulted when this box is its parent's first in-flow child) -- /// i.e. whether this box permits its own first in-flow child's margin-top to escape /// past it, from the *parent*'s point of view when this box is acting as that parent. pub top_open: bool, /// Same as `top_open`, for the last in-flow child and the box's own margin-bottom /// (case 2). pub bottom_open: bool, /// Whether this box's margin-top is free to adjoin with a preceding in-flow /// sibling's margin-bottom (case 3). Unconditionally true in a correct /// implementation on this corpus (no floats, no clearance), but exposed as its own /// decision so a candidate can get it wrong /// (`crates/logic/src/mutants.rs` `Mutant::SiblingAdjoinOff`). pub sibling_adjoin: bool,}
/// One candidate's classification function: plain data in, plain data out. Every margin/// candidate module the generation loop compiles exports a function of this type named/// `classify`.pub type MarginClassifyResolver = fn(&MarginCollapseInputs) -> MarginCollapseOutputs;
/// One candidate's combining function: the raw margin values (signed) entering one/// adjoining group, reduced to the single value CSS 2.1 8.3.1 uses -- the largest/// positive margin plus the most negative one (zero when a side has none). Always called/// with the group's *entire* raw value list at once, never folded pairwise -- folding/// partial results back through this same function is not associative (a positive value/// already summed into a partial result loses its own identity as "a positive" the next/// time the pair is reduced), so `fragment.rs` always collects a whole group before/// calling this once. Every margin candidate module exports a function of this type/// named `combine`.pub type MarginCombineResolver = fn(&[f64]) -> f64;
/// The hand rule `fragment.rs` used before this seam existed: nothing adjoins, nothing/// self-collapses, nothing escapes past a box's own border/padding. This seam's baseline/// candidate, not a spec-complete implementation.pub fn classify_baseline(_inputs: &MarginCollapseInputs) -> MarginCollapseOutputs { MarginCollapseOutputs { self_collapsing: false, top_open: false, bottom_open: false, sibling_adjoin: false }}
/// [`classify_baseline`]'s counterpart. Under `classify_baseline` no group ever grows/// past one value (nothing ever adjoins), so any function that returns a lone value/// unchanged reproduces the pre-extraction behavior; summing is the simplest one that/// also behaves sensibly if a caller ever mixes this with a `classify` that does let/// groups grow.pub fn combine_baseline(values: &[f64]) -> f64 { values.iter().sum()}
#[cfg(test)]mod tests { use super::*;
fn inputs() -> MarginCollapseInputs { MarginCollapseInputs { has_top_border_or_padding: false, has_bottom_border_or_padding: false, establishes_bfc: false, height_is_auto: true, height_is_explicit_zero: false, no_in_flow_content: true, } }
#[test] fn baseline_never_collapses_or_opens() { let out = classify_baseline(&inputs()); assert!(!out.self_collapsing); assert!(!out.top_open); assert!(!out.bottom_open); assert!(!out.sibling_adjoin); }
#[test] fn baseline_combine_passes_a_lone_value_through() { assert_eq!(combine_baseline(&[12.5]), 12.5); assert_eq!(combine_baseline(&[]), 0.0); }}