Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171//! The `margin_classify` generated function's place in the engine: CSS 2.1 8.3.1's//! decision, for one box, of how its margins take part in collapsing, isolated behind one//! function with plain-data inputs and outputs (`roadmap.md` -> "Phase 1 -- Engine//! bootstrap" step 4). The types a candidate implements against are re-exported from//! [`crate::margin_classify_types`] so `layout::margin_classify::...` names them; this//! module says how the engine fills them. [`crate::margin`] holds the other half of the//! margin-collapsing pair, `combine`, which the engine's structural walk calls once a group//! of adjoining margins is collected.//!//! `fragment.rs` calls the resolver at three places (`margin_classify_call_site.txt`, which//! a job's prompt carries): `stack_children` for a box of block children,//! `layout_inline_children` for a box of atomic inlines, and `layout_table_children` for a//! table.//!//! **Written against any types file.** A job's types file is model-written and chooses its//! inputs from the engine's fixed list ([`crate::margin_classify_engine`]), so nothing here//! names an input field. The engine builds a [`MarginClassifyEngineValues`] and calls//! through [`call`], which fills the inputs with [`inputs_from`] (written by `build.rs` from//! the types file in place). [`classify_baseline`] reads no input, so it stands in for the//! function whatever the types file holds.
pub use crate::margin_classify_engine::{MarginClassifyEngineAnswer, MarginClassifyEngineValues};pub use crate::margin_classify_types::{MarginClassifyInputs, MarginClassifyOutputs, MarginClassifyResolver};
use std::cell::Cell;
use dom::node::NodeId;
include!(concat!(env!("OUT_DIR"), "/margin_classify_inputs_from.rs"));
thread_local! { /// The engine's values for the call [`call`] is making, so [`resolve_reference`] can /// answer it; `None` outside a call. static CALL_VALUES: Cell<Option<MarginClassifyEngineValues>> = const { Cell::new(None) };}
/// One call the engine made to the function for a box, as `fragment.rs` records it/// (`crate::call_recording`): the box and the engine's values for it.#[derive(Debug, Clone, Copy, PartialEq, Eq)]pub struct MarginClassifyCall { pub node: NodeId, /// The engine's values at the call (`inputs_from` turns them into what the resolver /// was handed). pub values: MarginClassifyEngineValues,}
/// Asks `resolver` about the box `values` describes: fills the types file's inputs from/// `values` ([`inputs_from`]) and returns the answer. How the engine calls every/// `margin_classify` resolver.pub fn call(resolver: MarginClassifyResolver, values: &MarginClassifyEngineValues) -> MarginClassifyOutputs { let previous = CALL_VALUES.with(|v| v.replace(Some(*values))); let out = resolver(&inputs_from(values)); CALL_VALUES.with(|v| v.set(previous)); out}
/// CSS 2.1 8.3.1 as the engine's own hand rule, over the engine's values: a box's own/// margins collapse when nothing separates them (no top or bottom border or padding, a/// `height` of `auto` or zero, no in-flow content); its top (bottom) margin is open to its/// parent's and its first (last) child's unless a border or padding, or a new block/// formatting context, sits between; and it adjoins the sibling before it.pub fn reference(values: &MarginClassifyEngineValues) -> MarginClassifyEngineAnswer { MarginClassifyEngineAnswer { self_collapsing: !values.has_top_border_or_padding && !values.has_bottom_border_or_padding && (values.height_is_auto || values.height_is_explicit_zero) && values.no_in_flow_content, top_open: !values.has_top_border_or_padding && !values.establishes_bfc, bottom_open: !values.has_bottom_border_or_padding && !values.establishes_bfc, sibling_adjoin: true, }}
/// [`reference`] as a resolver: the stand-in a page is laid out with when the function's/// own inputs must not depend on the types file in place or on a candidate, so what a/// recorded call shows does not change when a candidate is admitted. It ignores `inputs` and/// reads the values of the call in progress, so it answers only a call made through/// [`call`], which is every call the engine makes; called any other way it panics.pub fn resolve_reference(_inputs: &MarginClassifyInputs) -> MarginClassifyOutputs { let values = CALL_VALUES .with(Cell::get) .expect("margin_classify::resolve_reference answers only a call made through margin_classify::call"); let a = reference(&values); MarginClassifyOutputs { self_collapsing: a.self_collapsing, top_open: a.top_open, bottom_open: a.bottom_open, sibling_adjoin: a.sibling_adjoin, }}
/// The hand rule `fragment.rs` used before margin collapsing was extracted: nothing/// adjoins, nothing self-collapses, nothing escapes past a box's own border or padding./// The baseline candidate, not a spec-complete implementation.pub fn classify_baseline(_inputs: &MarginClassifyInputs) -> MarginClassifyOutputs { MarginClassifyOutputs { self_collapsing: false, top_open: false, bottom_open: false, sibling_adjoin: false }}
#[cfg(test)]mod tests { use super::*;
fn values() -> MarginClassifyEngineValues { MarginClassifyEngineValues { 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 = call(classify_baseline, &values()); assert!(!out.self_collapsing); assert!(!out.top_open); assert!(!out.bottom_open); assert!(!out.sibling_adjoin); }
#[test] fn the_reference_rule_collapses_an_empty_unseparated_box() { let out = call(resolve_reference, &values()); assert!(out.self_collapsing && out.top_open && out.bottom_open && out.sibling_adjoin); assert!(CALL_VALUES.with(Cell::get).is_none(), "the values outlive the call"); }
#[test] fn the_reference_rule_keeps_a_border_or_a_formatting_context_closed() { let mut v = values(); v.has_top_border_or_padding = true; let out = call(resolve_reference, &v); assert!(!out.self_collapsing && !out.top_open && out.bottom_open); let mut v = values(); v.establishes_bfc = true; let out = call(resolve_reference, &v); assert!(out.self_collapsing && !out.top_open && !out.bottom_open); }
#[test] fn the_reference_rule_does_not_collapse_a_box_with_content_or_a_height() { let mut v = values(); v.no_in_flow_content = false; assert!(!call(resolve_reference, &v).self_collapsing); let mut v = values(); v.height_is_auto = false; assert!(!call(resolve_reference, &v).self_collapsing); v.height_is_explicit_zero = true; assert!(call(resolve_reference, &v).self_collapsing); }
#[test] #[should_panic(expected = "answers only a call made through margin_classify::call")] fn the_reference_resolver_refuses_a_call_the_engine_did_not_make() { resolve_reference(&inputs_from(&values())); }
#[test] fn inputs_are_the_engine_values_of_the_same_name() { let mut v = values(); v.establishes_bfc = true; v.height_is_auto = false; let i = inputs_from(&v); assert!(i.establishes_bfc); assert!(!i.height_is_auto); assert!(i.no_in_flow_content); }}