Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261// Admitted candidate `model_1` (qwen3.8-flash-next, 140/227); provenance: width.provenance.jsonuse crate::width::{Direction, WidthInputs, WidthOutputs};use dom::computed::LengthPercentageOrAuto;
/// CSS 2.1 10.3.3 Block-level, non-replaced elements in normal flow////// Implements the width equation:/// margin-left + border-left-width + padding-left + width + padding-right + border-right-width + margin-right = width of containing block////// Handles:/// - Auto width filling remaining space/// - Auto margins distributing remaining space/// - Over-constrained cases (ignoring margin-right for LTR, margin-left for RTL)/// - Both margins auto -> centering/// - Min/max width clamping (10.4)/// - Border-box sizing conversion/// - Percentage widths resolved against containing blockpub fn resolve(inputs: &WidthInputs) -> WidthOutputs { let containing_block_width = inputs.containing_block_width; let edge_lr = inputs.padding_left + inputs.padding_right + inputs.border_left + inputs.border_right;
// Helper to resolve a length-or-auto value to a definite f64. // For widths, percentages are resolved against the containing block width. // For margins, percentages are also resolved against the containing block width. let resolve_width = |lp: LengthPercentageOrAuto| -> Option<f64> { match lp { LengthPercentageOrAuto::Length(l) => Some(l), LengthPercentageOrAuto::Percentage(p) => Some(p * containing_block_width), LengthPercentageOrAuto::Auto => None, LengthPercentageOrAuto::Unresolved => None, } };
let resolve_margin = |lp: LengthPercentageOrAuto| -> Option<f64> { match lp { LengthPercentageOrAuto::Length(l) => Some(l), LengthPercentageOrAuto::Percentage(p) => Some(p * containing_block_width), LengthPercentageOrAuto::Auto => None, LengthPercentageOrAuto::Unresolved => None, } };
// Step 1: Determine the content width before min/max clamping. // If box-sizing is border-box, the specified width includes padding and border. // We need to work with content width for the equation, but the input width might be border-box. let mut width_auto = false; let mut specified_width: Option<f64> = None;
match inputs.width { LengthPercentageOrAuto::Auto => { width_auto = true; } LengthPercentageOrAuto::Length(l) => { if inputs.border_box_sizing { // Convert border-box width to content width let content_width = l - edge_lr; specified_width = Some(content_width.max(0.0)); } else { specified_width = Some(l); } } LengthPercentageOrAuto::Percentage(p) => { let resolved = p * containing_block_width; if inputs.border_box_sizing { let content_width = resolved - edge_lr; specified_width = Some(content_width.max(0.0)); } else { specified_width = Some(resolved); } } LengthPercentageOrAuto::Unresolved => { // Treat unresolved as auto for width resolution purposes width_auto = true; } }
// Step 2: Resolve margins let mut margin_left_auto = false; let mut margin_right_auto = false; let mut margin_left_val: Option<f64> = None; let mut margin_right_val: Option<f64> = None;
match inputs.margin_left { LengthPercentageOrAuto::Auto => margin_left_auto = true, LengthPercentageOrAuto::Length(l) => margin_left_val = Some(l), LengthPercentageOrAuto::Percentage(p) => margin_left_val = Some(p * containing_block_width), LengthPercentageOrAuto::Unresolved => margin_left_auto = true, }
match inputs.margin_right { LengthPercentageOrAuto::Auto => margin_right_auto = true, LengthPercentageOrAuto::Length(l) => margin_right_val = Some(l), LengthPercentageOrAuto::Percentage(p) => margin_right_val = Some(p * containing_block_width), LengthPercentageOrAuto::Unresolved => margin_right_auto = true, }
// Step 3: Apply min/max width constraints to the specified width (if not auto) // CSS 2.1 10.4: min-width and max-width apply to the content width. if let Some(w) = specified_width { let mut clamped_w = w; if let Some(min_w) = inputs.min_width { clamped_w = clamped_w.max(min_w); } if let Some(max_w) = inputs.max_width { clamped_w = clamped_w.min(max_w); } // If min > max, min wins (per CSS 2.1 10.4) if let (Some(min_w), Some(max_w)) = (inputs.min_width, inputs.max_width) { if min_w > max_w { clamped_w = min_w; } } specified_width = Some(clamped_w); }
// Step 4: Solve the width equation // margin-left + border-left + padding-left + width + padding-right + border-right + margin-right = containing block width // Let fixed_edges = border-left + padding-left + padding-right + border-right = edge_lr // So: margin-left + width + margin-right = containing_block_width - edge_lr // Let available_for_margins_and_width = containing_block_width - edge_lr
let available = containing_block_width - edge_lr;
let mut used_width: f64; let mut used_margin_left: f64; let mut used_margin_right: f64;
if width_auto { // Width is auto. All other autos become 0, width fills the rest. // "If 'width' is set to 'auto', any other 'auto' values become '0' and 'width' follows from the resulting equality." let ml = margin_left_val.unwrap_or(0.0); let mr = margin_right_val.unwrap_or(0.0); used_width = available - ml - mr; if used_width < 0.0 { used_width = 0.0; } // If min/max applies to auto width? // CSS 2.1 10.4 says min/max apply to the used width. // But the spec text in 10.3.3 says "If 'width' is set to 'auto'... width follows from the resulting equality." // Then 10.4 clamps. Let's apply clamping after solving. if let Some(min_w) = inputs.min_width { if used_width < min_w { used_width = min_w; } } if let Some(max_w) = inputs.max_width { if used_width > max_w { used_width = max_w; } } if let (Some(min_w), Some(max_w)) = (inputs.min_width, inputs.max_width) { if min_w > max_w { used_width = min_w; } }
used_margin_left = ml; used_margin_right = mr; } else { // Width is definite (specified_width is Some) let w = specified_width.unwrap(); // Check for over-constrained case // "If 'width' is not 'auto' and the sum of the other values is larger than the width of the containing block, then any 'auto' values for 'margin-left' or 'margin-right' are, for the following rules, treated as zero." let ml_specified = margin_left_val; let mr_specified = margin_right_val; // First, treat autos as 0 for the over-constraint check let ml_treated = ml_specified.unwrap_or(0.0); let mr_treated = mr_specified.unwrap_or(0.0); let sum = ml_treated + w + mr_treated; if sum > available { // Over-constrained. Autos are treated as 0. // "If all of the above have a computed value other than 'auto', the values are said to be 'over-constrained'..." // Wait, the spec says: If width is not auto and sum of OTHER values > containing block, then auto margins are treated as 0. // Then: "If all of the above have a computed value other than 'auto', the values are said to be 'over-constrained' and one of the used values will have to be different from its computed value." // Actually, let's re-read carefully: // 1. If width != auto and sum(margins, padding, border, width) > CB width, then auto margins -> 0. // 2. If all values are non-auto (after step 1 conversion), it's over-constrained. // - LTR: margin-right is ignored (recalculated) // - RTL: margin-left is ignored (recalculated) // After treating autos as 0, we have definite values for all. // The equation doesn't hold. We need to adjust one margin. // Recalculate: margin-left + width + margin-right = available // We keep width fixed. We keep margin-left fixed (for LTR). We solve for margin-right. // For RTL, we keep margin-right fixed, solve for margin-left. if inputs.direction == Direction::Ltr { used_margin_left = ml_treated; used_width = w; used_margin_right = available - used_margin_left - used_width; } else { used_margin_right = mr_treated; used_width = w; used_margin_left = available - used_margin_right - used_width; } } else { // Not over-constrained. // "If there is exactly one value specified as 'auto', its used value follows from the equality." // "If both 'margin-left' and 'margin-right' are 'auto', their used values are equal." if margin_left_auto && margin_right_auto { // Both auto: centering let remaining = available - w; if remaining < 0.0 { // This shouldn't happen if not over-constrained, but guard anyway used_margin_left = 0.0; used_margin_right = 0.0; } else { used_margin_left = remaining / 2.0; used_margin_right = remaining / 2.0; } used_width = w; } else if margin_left_auto { // Only margin-left is auto used_margin_right = mr_specified.unwrap_or(0.0); used_width = w; used_margin_left = available - used_width - used_margin_right; } else if margin_right_auto { // Only margin-right is auto used_margin_left = ml_specified.unwrap_or(0.0); used_width = w; used_margin_right = available - used_width - used_margin_left; } else { // No autos. Should have been caught by over-constraint check if sum != available. // If sum == available, we're good. // If sum < available, there's extra space. CSS 2.1 doesn't explicitly say what happens // when all values are specified and sum < available. The element just doesn't fill the CB. // The equation "must hold" implies we should adjust? No, the equation describes the // layout. If all are specified and sum < available, the box is just narrower than CB. // The spec says "one of the used values will have to be different" only for over-constrained. // For under-constrained, the box just doesn't fill. used_margin_left = ml_specified.unwrap_or(0.0); used_margin_right = mr_specified.unwrap_or(0.0); used_width = w; } } }
// Ensure non-negative widths and margins where appropriate if used_width < 0.0 { used_width = 0.0; } // Margins can be negative in CSS, so we don't clamp them to 0.
WidthOutputs { used_width, used_margin_left, used_margin_right, }}