diff --git a/benches/src/taffy_03_helpers.rs b/benches/src/taffy_03_helpers.rs index 33c2b8faf..8ab461178 100644 --- a/benches/src/taffy_03_helpers.rs +++ b/benches/src/taffy_03_helpers.rs @@ -1,4 +1,4 @@ -use rand::distributions::uniform::SampleRange; +use rand::distr::uniform::SampleRange; use rand::Rng; use rand_chacha::ChaCha8Rng; use taffy::style::Style as TaffyStyle; @@ -120,25 +120,31 @@ fn convert_point U>(input: taffy::geometry::Point, map: F) } fn convert_dimension(input: taffy::style::Dimension) -> taffy_03::style::Dimension { - match input { - taffy::style::Dimension::Length(val) => taffy_03::style::Dimension::Points(val), - taffy::style::Dimension::Percent(val) => taffy_03::style::Dimension::Percent(val), - taffy::style::Dimension::Auto => taffy_03::style::Dimension::Auto, + let raw = input.into_raw(); + match raw.tag() { + taffy::style::CompactLength::LENGTH_TAG => taffy_03::style::Dimension::Points(raw.value()), + taffy::style::CompactLength::PERCENT_TAG => taffy_03::style::Dimension::Percent(raw.value()), + taffy::style::CompactLength::AUTO_TAG => taffy_03::style::Dimension::Auto, + _ => panic!("unsupported Dimension variant"), } } fn convert_length_percentage_auto(input: taffy::style::LengthPercentageAuto) -> taffy_03::style::LengthPercentageAuto { - match input { - taffy::style::LengthPercentageAuto::Length(val) => taffy_03::style::LengthPercentageAuto::Points(val), - taffy::style::LengthPercentageAuto::Percent(val) => taffy_03::style::LengthPercentageAuto::Percent(val), - taffy::style::LengthPercentageAuto::Auto => taffy_03::style::LengthPercentageAuto::Auto, + let raw = input.into_raw(); + match raw.tag() { + taffy::style::CompactLength::LENGTH_TAG => taffy_03::style::LengthPercentageAuto::Points(raw.value()), + taffy::style::CompactLength::PERCENT_TAG => taffy_03::style::LengthPercentageAuto::Percent(raw.value()), + taffy::style::CompactLength::AUTO_TAG => taffy_03::style::LengthPercentageAuto::Auto, + _ => panic!("unsupported LengthPercentageAuto variant"), } } fn convert_length_percentage(input: taffy::style::LengthPercentage) -> taffy_03::style::LengthPercentage { - match input { - taffy::style::LengthPercentage::Length(val) => taffy_03::style::LengthPercentage::Points(val), - taffy::style::LengthPercentage::Percent(val) => taffy_03::style::LengthPercentage::Percent(val), + let raw = input.into_raw(); + match raw.tag() { + taffy::style::CompactLength::LENGTH_TAG => taffy_03::style::LengthPercentage::Points(raw.value()), + taffy::style::CompactLength::PERCENT_TAG => taffy_03::style::LengthPercentage::Percent(raw.value()), + _ => panic!("unsupported LengthPercentage variant"), } } diff --git a/benches/src/yoga_helpers.rs b/benches/src/yoga_helpers.rs index f91280dee..9f61c34ae 100644 --- a/benches/src/yoga_helpers.rs +++ b/benches/src/yoga_helpers.rs @@ -143,45 +143,41 @@ fn into_pixels(dim: impl Into) -> f32 { } fn items_into_align(align: Option) -> yg::Align { - match align { - None => yg::Align::Auto, - Some(tf::AlignSelf::FlexStart) => yg::Align::FlexStart, - Some(tf::AlignSelf::FlexEnd) => yg::Align::FlexEnd, - Some(tf::AlignSelf::Center) => yg::Align::Center, - Some(tf::AlignSelf::Baseline) => yg::Align::Baseline, - Some(tf::AlignSelf::Stretch) => yg::Align::Stretch, - Some(tf::AlignSelf::Start) => unimplemented!(), - Some(tf::AlignSelf::End) => unimplemented!(), + // Yoga has no safe/unsafe overflow-position concept — drop the safety field and dispatch + // on the bare keyword. Safe and unsafe alike fold to the same yoga keyword. + let Some(align) = align else { return yg::Align::Auto }; + match align.keyword { + tf::AlignItemsKeyword::FlexStart => yg::Align::FlexStart, + tf::AlignItemsKeyword::FlexEnd => yg::Align::FlexEnd, + tf::AlignItemsKeyword::Center => yg::Align::Center, + tf::AlignItemsKeyword::Baseline => yg::Align::Baseline, + tf::AlignItemsKeyword::Stretch => yg::Align::Stretch, + tf::AlignItemsKeyword::Start | tf::AlignItemsKeyword::End => unimplemented!(), } } fn content_into_align(align: Option) -> yg::Align { - match align { - None => yg::Align::Auto, - Some(tf::AlignContent::FlexStart) => yg::Align::FlexStart, - Some(tf::AlignContent::Start) => yg::Align::FlexStart, - Some(tf::AlignContent::FlexEnd) => yg::Align::FlexEnd, - Some(tf::AlignContent::End) => yg::Align::FlexEnd, - Some(tf::AlignContent::Center) => yg::Align::Center, - Some(tf::AlignContent::Stretch) => yg::Align::Stretch, - Some(tf::AlignContent::SpaceBetween) => yg::Align::SpaceBetween, - Some(tf::AlignContent::SpaceAround) => yg::Align::SpaceAround, - Some(tf::AlignContent::SpaceEvenly) => unimplemented!(), + let Some(align) = align else { return yg::Align::Auto }; + match align.keyword { + tf::AlignContentKeyword::FlexStart | tf::AlignContentKeyword::Start => yg::Align::FlexStart, + tf::AlignContentKeyword::FlexEnd | tf::AlignContentKeyword::End => yg::Align::FlexEnd, + tf::AlignContentKeyword::Center => yg::Align::Center, + tf::AlignContentKeyword::Stretch => yg::Align::Stretch, + tf::AlignContentKeyword::SpaceBetween => yg::Align::SpaceBetween, + tf::AlignContentKeyword::SpaceAround => yg::Align::SpaceAround, + tf::AlignContentKeyword::SpaceEvenly => unimplemented!(), } } fn content_into_justify(align: Option) -> yg::Justify { - match align { - None => yg::Justify::FlexStart, - Some(tf::JustifyContent::FlexStart) => yg::Justify::FlexStart, - Some(tf::JustifyContent::Start) => yg::Justify::FlexStart, - Some(tf::JustifyContent::FlexEnd) => yg::Justify::FlexEnd, - Some(tf::JustifyContent::End) => yg::Justify::FlexEnd, - Some(tf::JustifyContent::Center) => yg::Justify::Center, - Some(tf::JustifyContent::SpaceBetween) => yg::Justify::SpaceBetween, - Some(tf::JustifyContent::SpaceAround) => yg::Justify::SpaceAround, - Some(tf::JustifyContent::Stretch) => unimplemented!(), - Some(tf::JustifyContent::SpaceEvenly) => unimplemented!(), + let Some(align) = align else { return yg::Justify::FlexStart }; + match align.keyword { + tf::AlignContentKeyword::FlexStart | tf::AlignContentKeyword::Start => yg::Justify::FlexStart, + tf::AlignContentKeyword::FlexEnd | tf::AlignContentKeyword::End => yg::Justify::FlexEnd, + tf::AlignContentKeyword::Center => yg::Justify::Center, + tf::AlignContentKeyword::SpaceBetween => yg::Justify::SpaceBetween, + tf::AlignContentKeyword::SpaceAround => yg::Justify::SpaceAround, + tf::AlignContentKeyword::Stretch | tf::AlignContentKeyword::SpaceEvenly => unimplemented!(), } } diff --git a/docs/style-properties.md b/docs/style-properties.md index 6b3013f57..e32d5d679 100644 --- a/docs/style-properties.md +++ b/docs/style-properties.md @@ -24,12 +24,12 @@ N = Supported in spec but not implemented in Taffy | `margin` | Y | ~Y | `Rect` | 32 | - | How large should the margin be on each side? | | `gap` | Y | Y | `Size` | 16 | - | The size of the vertical and horizontal gaps between flex items / grid rows | | **Alignment** | | | | | | | -| `align_content` | Y | Y | `AlignContent` | 1 | - | How should content contained within this item be aligned relative to the cross axis? | -| `justify_content` | Y | Y | `AlignContent` | 1 | - | How should content contained within this item be aligned relative to the main axis? | -| `align_items` | Y | Y | `AlignItems` | 1 | - | How should items be aligned relative to the cross axis? | -| `align_self` | Y | Y | `Option` | 1 | - | Should this item violate the cross axis alignment specified by its parent's [`AlignItems`]? | -| `justify_items` | - | Y | `AlignItems` | 1 | - | How should items be aligned relative to the main axis? | -| `justify_self` | - | Y | `Option` | 1 | - | Should this item violate the main axis alignment specified by its parent's [`AlignItems`]? | +| `align_content` | Y | Y | `AlignContent` | 2 | - | How should content contained within this item be aligned relative to the cross axis? | +| `justify_content` | Y | Y | `AlignContent` | 2 | - | How should content contained within this item be aligned relative to the main axis? | +| `align_items` | Y | Y | `AlignItems` | 2 | - | How should items be aligned relative to the cross axis? | +| `align_self` | Y | Y | `Option` | 2 | - | Should this item violate the cross axis alignment specified by its parent's [`AlignItems`]? | +| `justify_items` | - | Y | `AlignItems` | 2 | - | How should items be aligned relative to the main axis? | +| `justify_self` | - | Y | `Option` | 2 | - | Should this item violate the main axis alignment specified by its parent's [`AlignItems`]? | | **Flexbox** | | | | | | | | `flex_direction` | Y | - | `FlexDirection` | 1 | - | Which direction does the main axis flow in? | | `flex_wrap` | Y | - | `FlexWrap` | 1 | - | Should elements wrap, or stay in a single line? | @@ -47,3 +47,37 @@ N = Supported in spec but not implemented in Taffy | `grid_row` | - | Y | `Line` | 8 | - | The vertical (row) placement of a grid item | | `grid_column` | - | Y | `Line` | 8 | - | The horizontal (row) placement of a grid item | | `grid_area` | - | 5 | - | - | - | Accepts either shorthand row/column-start/end or a named grid area | + +## Safe and unsafe overflow alignment + +`AlignItems` and `AlignContent` (and their aliases `AlignSelf`, `JustifyItems`, `JustifySelf`, +`JustifyContent`) are structs with two orthogonal fields: + +```rust +pub struct AlignContent { + pub keyword: AlignContentKeyword, // Start, End, FlexStart, FlexEnd, Center, + // Stretch, SpaceBetween, SpaceEvenly, SpaceAround + pub safety: AlignmentSafety, // Safe | Unsafe +} +``` + +For ergonomics every CSS spelling is exposed as an associated constant — `AlignContent::Start`, +`AlignContent::SafeStart`, `AlignItems::SafeFlexEnd`, etc. — so call sites read identically to +the previous enum form. + +The `safety` field corresponds to the spec's +[overflow-position keyword](https://www.w3.org/TR/css-align-3/#overflow-values). When the +alignment subject would overflow its alignment container, `AlignmentSafety::Safe` falls back +to logical `Start` so the start edge of the content stays visible. `AlignmentSafety::Unsafe` +(the default) keeps the requested alignment even when that causes overflow at the start edge. + +The spec only defines `safe`/`unsafe` against the position keywords `start`, `end`, +`flex-start`, `flex-end`, `center`. Combinations such as `safe stretch`, `safe baseline`, and +`safe space-between` are rejected by the parser when the `parse` feature is enabled, and the +compute pass treats them as `Unsafe` if a value somehow reaches it via manual struct +construction. + +Use the `is_safe()` method to check the safety modifier and `keyword()` to retrieve the bare +position keyword. The `safe`/`unsafe` overflow-position keyword is supported by both the +Flexbox and CSS Grid layout algorithms for both content-level (`align-content`, +`justify-content`) and self-level (`align-self`, `justify-self`) alignment. diff --git a/src/compute/common/alignment.rs b/src/compute/common/alignment.rs index bebd7f74a..43cb1472c 100644 --- a/src/compute/common/alignment.rs +++ b/src/compute/common/alignment.rs @@ -1,36 +1,38 @@ //! Generic CSS alignment code that is shared between both the Flexbox and CSS Grid algorithms. -use crate::style::AlignContent; +use crate::style::{AlignContent, AlignContentKeyword, AlignmentSafety}; -/// Implement fallback alignment. +/// Resolve any spec-defined fallbacks for the given [`AlignContent`] value, returning the +/// bare position keyword the alignment math should use. /// -/// In addition to the spec at https://www.w3.org/TR/css-align-3/ this implementation follows -/// the resolution of https://github.com/w3c/csswg-drafts/issues/10154 +/// In addition to the spec at this implementation follows +/// the resolution of . pub(crate) fn apply_alignment_fallback( free_space: f32, num_items: usize, - mut alignment_mode: AlignContent, - mut is_safe: bool, -) -> AlignContent { - // Fallback occurs in two cases: + alignment_mode: AlignContent, +) -> AlignContentKeyword { + let mut keyword = alignment_mode.keyword; + let mut is_safe = matches!(alignment_mode.safety, AlignmentSafety::Safe); - // 1. If there is only a single item being aligned and alignment is a distributed alignment keyword + // 1. If there is only a single item being aligned or the items overflow the container, the + // distributed alignment keywords (`stretch`, `space-*`) fall back to a positional keyword + // and gain implicit `safe` semantics so step 2 can flip them to `Start` on overflow. // https://www.w3.org/TR/css-align-3/#distribution-values if num_items <= 1 || free_space <= 0.0 { - (alignment_mode, is_safe) = match alignment_mode { - AlignContent::Stretch => (AlignContent::FlexStart, true), - AlignContent::SpaceBetween => (AlignContent::FlexStart, true), - AlignContent::SpaceAround => (AlignContent::Center, true), - AlignContent::SpaceEvenly => (AlignContent::Center, true), - _ => (alignment_mode, is_safe), - } - }; - - // 2. If free space is negative the "safe" alignment variants all fallback to Start alignment - if free_space <= 0.0 && is_safe { - alignment_mode = AlignContent::Start; + (keyword, is_safe) = match keyword { + AlignContentKeyword::Stretch | AlignContentKeyword::SpaceBetween => (AlignContentKeyword::FlexStart, true), + AlignContentKeyword::SpaceAround | AlignContentKeyword::SpaceEvenly => (AlignContentKeyword::Center, true), + other => (other, is_safe), + }; } - alignment_mode + // 2. Safe alignment falls back to `Start` whenever the alignment subject would overflow the + // alignment container. + if free_space <= 0.0 && is_safe { + keyword = AlignContentKeyword::Start; + } + + keyword } /// Generic alignment function that is used: @@ -43,39 +45,39 @@ pub(crate) fn compute_alignment_offset( free_space: f32, num_items: usize, gap: f32, - alignment_mode: AlignContent, + alignment_mode: AlignContentKeyword, layout_is_flex_reversed: bool, is_first: bool, ) -> f32 { if is_first { match alignment_mode { - AlignContent::Start => 0.0, - AlignContent::FlexStart => { + AlignContentKeyword::Start => 0.0, + AlignContentKeyword::FlexStart => { if layout_is_flex_reversed { free_space } else { 0.0 } } - AlignContent::End => free_space, - AlignContent::FlexEnd => { + AlignContentKeyword::End => free_space, + AlignContentKeyword::FlexEnd => { if layout_is_flex_reversed { 0.0 } else { free_space } } - AlignContent::Center => free_space / 2.0, - AlignContent::Stretch => 0.0, - AlignContent::SpaceBetween => 0.0, - AlignContent::SpaceAround => { + AlignContentKeyword::Center => free_space / 2.0, + AlignContentKeyword::Stretch => 0.0, + AlignContentKeyword::SpaceBetween => 0.0, + AlignContentKeyword::SpaceAround => { if free_space >= 0.0 { (free_space / num_items as f32) / 2.0 } else { free_space / 2.0 } } - AlignContent::SpaceEvenly => { + AlignContentKeyword::SpaceEvenly => { if free_space >= 0.0 { free_space / (num_items + 1) as f32 } else { @@ -86,15 +88,15 @@ pub(crate) fn compute_alignment_offset( } else { let free_space = free_space.max(0.0); gap + match alignment_mode { - AlignContent::Start => 0.0, - AlignContent::FlexStart => 0.0, - AlignContent::End => 0.0, - AlignContent::FlexEnd => 0.0, - AlignContent::Center => 0.0, - AlignContent::Stretch => 0.0, - AlignContent::SpaceBetween => free_space / (num_items - 1) as f32, - AlignContent::SpaceAround => free_space / num_items as f32, - AlignContent::SpaceEvenly => free_space / (num_items + 1) as f32, + AlignContentKeyword::Start + | AlignContentKeyword::FlexStart + | AlignContentKeyword::End + | AlignContentKeyword::FlexEnd + | AlignContentKeyword::Center + | AlignContentKeyword::Stretch => 0.0, + AlignContentKeyword::SpaceBetween => free_space / (num_items - 1) as f32, + AlignContentKeyword::SpaceAround => free_space / num_items as f32, + AlignContentKeyword::SpaceEvenly => free_space / (num_items + 1) as f32, } } } diff --git a/src/compute/flexbox.rs b/src/compute/flexbox.rs index eb56619a9..c1127c0eb 100644 --- a/src/compute/flexbox.rs +++ b/src/compute/flexbox.rs @@ -2,8 +2,8 @@ use crate::compute::common::alignment::compute_alignment_offset; use crate::geometry::{Line, Point, Rect, Size}; use crate::style::{ - AlignContent, AlignItems, AlignSelf, AvailableSpace, FlexWrap, JustifyContent, LengthPercentageAuto, Overflow, - Position, + AlignContent, AlignContentKeyword, AlignItems, AlignItemsKeyword, AlignSelf, AvailableSpace, FlexWrap, + JustifyContent, LengthPercentageAuto, Overflow, Position, }; use crate::style::{CoreStyle, FlexDirection, FlexboxContainerStyle, FlexboxItemStyle}; use crate::style_helpers::{TaffyMaxContent, TaffyMinContent}; @@ -1698,9 +1698,8 @@ fn distribute_remaining_free_space(flex_lines: &mut [FlexLine], constants: &Algo let num_items = line.items.len(); let layout_reverse = constants.dir.is_reverse(); let gap = constants.gap.main(constants.dir); - let is_safe = false; // TODO: Implement safe alignment let raw_justify_content_mode = constants.justify_content.unwrap_or(JustifyContent::FlexStart); - let justify_content_mode = apply_alignment_fallback(free_space, num_items, raw_justify_content_mode, is_safe); + let justify_content_mode = apply_alignment_fallback(free_space, num_items, raw_justify_content_mode); let justify_item = |(i, child): (usize, &mut FlexItem)| { child.offset_main = @@ -1779,37 +1778,47 @@ fn align_flex_items_along_cross_axis( ) -> f32 { let cross_axis_should_reverse = constants.is_column && matches!(constants.layout_direction, Direction::Rtl); - match child.align_self { - AlignSelf::Start => { + // If align-self uses a "safe" overflow-position keyword and the item would overflow its + // line cross size, fall back to logical Start to avoid data loss. See CSS Box Alignment 3 + // §4.3 . Otherwise, drop the safety + // field so the match below operates on a bare keyword and stays exhaustive. + let align_keyword = if child.align_self.is_safe() && free_space < 0.0 { + AlignItemsKeyword::Start + } else { + child.align_self.keyword + }; + + match align_keyword { + AlignItemsKeyword::Start => { if cross_axis_should_reverse { free_space } else { 0.0 } } - AlignSelf::FlexStart => { + AlignItemsKeyword::FlexStart => { if constants.is_wrap_reverse ^ cross_axis_should_reverse { free_space } else { 0.0 } } - AlignSelf::End => { + AlignItemsKeyword::End => { if cross_axis_should_reverse { 0.0 } else { free_space } } - AlignSelf::FlexEnd => { + AlignItemsKeyword::FlexEnd => { if constants.is_wrap_reverse ^ cross_axis_should_reverse { 0.0 } else { free_space } } - AlignSelf::Center => free_space / 2.0, - AlignSelf::Baseline => { + AlignItemsKeyword::Center => free_space / 2.0, + AlignItemsKeyword::Baseline => { if constants.is_row { max_baseline - child.baseline } else { @@ -1823,7 +1832,7 @@ fn align_flex_items_along_cross_axis( } } } - AlignSelf::Stretch => { + AlignItemsKeyword::Stretch => { if constants.is_wrap_reverse ^ cross_axis_should_reverse { free_space } else { @@ -1880,9 +1889,8 @@ fn align_flex_lines_per_align_content(flex_lines: &mut [FlexLine], constants: &A let gap = constants.gap.cross(constants.dir); let total_cross_axis_gap = sum_axis_gaps(gap, num_lines); let free_space = constants.inner_container_size.cross(constants.dir) - total_cross_size - total_cross_axis_gap; - let is_safe = false; // TODO: Implement safe alignment - let align_content_mode = apply_alignment_fallback(free_space, num_lines, constants.align_content, is_safe); + let align_content_mode = apply_alignment_fallback(free_space, num_lines, constants.align_content); let align_line = |(i, line): (usize, &mut FlexLine)| { line.offset_cross = @@ -2326,40 +2334,45 @@ fn perform_absolute_layout_on_absolute_children( } else { // Stretch is an invalid value for justify_content in the flexbox algorithm, so we // treat it as if it wasn't set (and thus we default to FlexStart behaviour) - match (constants.justify_content.unwrap_or(JustifyContent::Start), main_axis_flex_start_reversed) { - (JustifyContent::SpaceBetween, _) - | (JustifyContent::Stretch, false) - | (JustifyContent::FlexStart, false) - | (JustifyContent::FlexEnd, true) => { + // TODO (safe alignment): apply Start fallback when justify_content.is_safe() and the + // resolved size overflows the containing block. Wired in a follow-up commit. + match (constants.justify_content.unwrap_or(JustifyContent::Start).keyword(), main_axis_flex_start_reversed) + { + (AlignContentKeyword::SpaceBetween, _) + | (AlignContentKeyword::Stretch, false) + | (AlignContentKeyword::FlexStart, false) + | (AlignContentKeyword::FlexEnd, true) => { constants.content_box_inset.main_start(constants.dir) + resolved_margin.main_start(constants.dir) } - (JustifyContent::Start, false) => { + (AlignContentKeyword::Start, false) => { constants.content_box_inset.main_start(constants.dir) + resolved_margin.main_start(constants.dir) } - (JustifyContent::Start, true) => { + (AlignContentKeyword::Start, true) => { constants.container_size.main(constants.dir) - constants.content_box_inset.main_end(constants.dir) - final_size.main(constants.dir) - resolved_margin.main_end(constants.dir) } - (JustifyContent::End, false) => { + (AlignContentKeyword::End, false) => { constants.container_size.main(constants.dir) - constants.content_box_inset.main_end(constants.dir) - final_size.main(constants.dir) - resolved_margin.main_end(constants.dir) } - (JustifyContent::End, true) => { + (AlignContentKeyword::End, true) => { constants.content_box_inset.main_start(constants.dir) + resolved_margin.main_start(constants.dir) } - (JustifyContent::FlexEnd, false) - | (JustifyContent::FlexStart, true) - | (JustifyContent::Stretch, true) => { + (AlignContentKeyword::FlexEnd, false) + | (AlignContentKeyword::FlexStart, true) + | (AlignContentKeyword::Stretch, true) => { constants.container_size.main(constants.dir) - constants.content_box_inset.main_end(constants.dir) - final_size.main(constants.dir) - resolved_margin.main_end(constants.dir) } - (JustifyContent::SpaceEvenly, _) | (JustifyContent::SpaceAround, _) | (JustifyContent::Center, _) => { + (AlignContentKeyword::SpaceEvenly, _) + | (AlignContentKeyword::SpaceAround, _) + | (AlignContentKeyword::Center, _) => { (constants.container_size.main(constants.dir) + constants.content_box_inset.main_start(constants.dir) - constants.content_box_inset.main_end(constants.dir) @@ -2395,40 +2408,42 @@ fn perform_absolute_layout_on_absolute_children( - resolved_margin.cross_end(constants.dir) } } else { - match (align_self, cross_axis_flex_start_reversed) { + // TODO (safe alignment): apply Start fallback when align_self.is_safe() and the + // resolved size overflows the containing block. Wired in a follow-up commit. + match (align_self.keyword(), cross_axis_flex_start_reversed) { // Stretch alignment does not apply to absolutely positioned items // See "Example 3" at https://www.w3.org/TR/css-flexbox-1/#abspos-items // Note: Stretch should be FlexStart not Start when we support both - (AlignSelf::Start, false) => { + (AlignItemsKeyword::Start, false) => { constants.content_box_inset.cross_start(constants.dir) + resolved_margin.cross_start(constants.dir) } - (AlignSelf::Start, true) => { + (AlignItemsKeyword::Start, true) => { constants.container_size.cross(constants.dir) - constants.content_box_inset.cross_end(constants.dir) - final_size.cross(constants.dir) - resolved_margin.cross_end(constants.dir) } - (AlignSelf::End, false) => { + (AlignItemsKeyword::End, false) => { constants.container_size.cross(constants.dir) - constants.content_box_inset.cross_end(constants.dir) - final_size.cross(constants.dir) - resolved_margin.cross_end(constants.dir) } - (AlignSelf::End, true) => { + (AlignItemsKeyword::End, true) => { constants.content_box_inset.cross_start(constants.dir) + resolved_margin.cross_start(constants.dir) } - (AlignSelf::Baseline | AlignSelf::Stretch | AlignSelf::FlexStart, false) - | (AlignSelf::FlexEnd, true) => { + (AlignItemsKeyword::Baseline | AlignItemsKeyword::Stretch | AlignItemsKeyword::FlexStart, false) + | (AlignItemsKeyword::FlexEnd, true) => { constants.content_box_inset.cross_start(constants.dir) + resolved_margin.cross_start(constants.dir) } - (AlignSelf::Baseline | AlignSelf::Stretch | AlignSelf::FlexStart, true) - | (AlignSelf::FlexEnd, false) => { + (AlignItemsKeyword::Baseline | AlignItemsKeyword::Stretch | AlignItemsKeyword::FlexStart, true) + | (AlignItemsKeyword::FlexEnd, false) => { constants.container_size.cross(constants.dir) - constants.content_box_inset.cross_end(constants.dir) - final_size.cross(constants.dir) - resolved_margin.cross_end(constants.dir) } - (AlignSelf::Center, _) => { + (AlignItemsKeyword::Center, _) => { (constants.container_size.cross(constants.dir) + constants.content_box_inset.cross_start(constants.dir) - constants.content_box_inset.cross_end(constants.dir) diff --git a/src/compute/grid/alignment.rs b/src/compute/grid/alignment.rs index c4b946fe5..4ede43369 100644 --- a/src/compute/grid/alignment.rs +++ b/src/compute/grid/alignment.rs @@ -2,7 +2,10 @@ use super::types::GridTrack; use crate::compute::common::alignment::{apply_alignment_fallback, compute_alignment_offset}; use crate::geometry::{InBothAbsAxis, Line, Point, Rect, Size}; -use crate::style::{AlignContent, AlignItems, AlignSelf, AvailableSpace, CoreStyle, GridItemStyle, Overflow, Position}; +use crate::style::{ + AlignContent, AlignItems, AlignItemsKeyword, AlignSelf, AvailableSpace, CoreStyle, GridItemStyle, Overflow, + Position, +}; use crate::tree::{Layout, LayoutPartialTreeExt, NodeId, SizingMode}; use crate::util::sys::f32_max; use crate::util::{MaybeMath, MaybeResolve, ResolveOrZero}; @@ -33,8 +36,7 @@ pub(super) fn align_tracks( // simply pass zero here. Grid layout is never reversed. let gap = 0.0; let layout_is_reversed = false; - let is_safe = false; // TODO: Implement safe alignment - let track_alignment = apply_alignment_fallback(free_space, num_tracks, track_alignment_style, is_safe); + let track_alignment = apply_alignment_fallback(free_space, num_tracks, track_alignment_style); let track_alignment = if axis_is_reversed { track_alignment.reversed() } else { track_alignment }; // Compute offsets @@ -315,24 +317,37 @@ pub(super) fn align_item_within_area( end: margin.end.unwrap_or(auto_margin_size), }; + // If the alignment uses a "safe" overflow-position keyword and the item would overflow + // its grid area, fall back to logical Start to avoid data loss. See CSS Box Alignment 3 + // §4.3 . Otherwise, drop the safety + // field so the match below operates on a bare keyword and stays exhaustive. + let overflows = resolved_size + non_auto_margin.sum() > grid_area_size; + let alignment_keyword = + if alignment_style.is_safe() && overflows { AlignItemsKeyword::Start } else { alignment_style.keyword() }; + // Compute offset in the axis - let alignment_based_offset = match alignment_style { + let alignment_based_offset = match alignment_keyword { // TODO: Add support for baseline alignment. For now we treat it as "start". - AlignSelf::Start | AlignSelf::FlexStart | AlignSelf::Baseline | AlignSelf::Stretch => { + AlignItemsKeyword::Start + | AlignItemsKeyword::FlexStart + | AlignItemsKeyword::Baseline + | AlignItemsKeyword::Stretch => { if direction.is_rtl() { grid_area_size - resolved_size - resolved_margin.end } else { resolved_margin.start } } - AlignSelf::End | AlignSelf::FlexEnd => { + AlignItemsKeyword::End | AlignItemsKeyword::FlexEnd => { if direction.is_rtl() { resolved_margin.start } else { grid_area_size - resolved_size - resolved_margin.end } } - AlignSelf::Center => (grid_area_size - resolved_size + resolved_margin.start - resolved_margin.end) / 2.0, + AlignItemsKeyword::Center => { + (grid_area_size - resolved_size + resolved_margin.start - resolved_margin.end) / 2.0 + } }; let offset_within_area = if position == Position::Absolute { diff --git a/src/compute/grid/track_sizing.rs b/src/compute/grid/track_sizing.rs index 2773cefd2..1f409c167 100644 --- a/src/compute/grid/track_sizing.rs +++ b/src/compute/grid/track_sizing.rs @@ -2,7 +2,7 @@ //! use super::types::{GridItem, GridTrack, TrackCounts}; use crate::geometry::{AbstractAxis, Line, Size}; -use crate::style::{AlignContent, AlignSelf, AvailableSpace}; +use crate::style::{AlignContent, AlignContentKeyword, AlignSelf, AvailableSpace}; use crate::style_helpers::TaffyMinContent; use crate::tree::{LayoutPartialTree, LayoutPartialTreeExt, SizingMode}; use crate::util::sys::{f32_max, f32_min, Vec}; @@ -188,30 +188,31 @@ pub(super) fn compute_alignment_gutter_adjustment( return 0.0; } - // As items never cross the outermost gutters in a grid, we can simplify our calculations by treating - // AlignContent::Start and AlignContent::End the same - let outer_gutter_weight = match alignment { - AlignContent::Start => 1, - AlignContent::FlexStart => 1, - AlignContent::End => 1, - AlignContent::FlexEnd => 1, - AlignContent::Center => 1, - AlignContent::Stretch => 0, - AlignContent::SpaceBetween => 0, - AlignContent::SpaceAround => 1, - AlignContent::SpaceEvenly => 1, + // As items never cross the outermost gutters in a grid, we can simplify our calculations by + // treating Start and End the same. The safety modifier doesn't influence gutter weight; + // overflow fallback is handled when offsets are computed. + let outer_gutter_weight = match alignment.keyword() { + AlignContentKeyword::Start + | AlignContentKeyword::FlexStart + | AlignContentKeyword::End + | AlignContentKeyword::FlexEnd + | AlignContentKeyword::Center => 1, + AlignContentKeyword::Stretch => 0, + AlignContentKeyword::SpaceBetween => 0, + AlignContentKeyword::SpaceAround => 1, + AlignContentKeyword::SpaceEvenly => 1, }; - let inner_gutter_weight = match alignment { - AlignContent::FlexStart => 0, - AlignContent::Start => 0, - AlignContent::FlexEnd => 0, - AlignContent::End => 0, - AlignContent::Center => 0, - AlignContent::Stretch => 0, - AlignContent::SpaceBetween => 1, - AlignContent::SpaceAround => 2, - AlignContent::SpaceEvenly => 1, + let inner_gutter_weight = match alignment.keyword() { + AlignContentKeyword::FlexStart + | AlignContentKeyword::Start + | AlignContentKeyword::FlexEnd + | AlignContentKeyword::End + | AlignContentKeyword::Center + | AlignContentKeyword::Stretch => 0, + AlignContentKeyword::SpaceBetween => 1, + AlignContentKeyword::SpaceAround => 2, + AlignContentKeyword::SpaceEvenly => 1, }; if inner_gutter_weight == 0 { diff --git a/src/prelude.rs b/src/prelude.rs index 1367e8642..cc3381d4a 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -3,8 +3,9 @@ pub use crate::{ geometry::{Line, Rect, Size}, style::{ - AlignContent, AlignItems, AlignSelf, AvailableSpace, BoxSizing, CompactLength, Dimension, Display, - JustifyContent, JustifyItems, JustifySelf, LengthPercentage, LengthPercentageAuto, Position, Style, + AlignContent, AlignContentKeyword, AlignItems, AlignItemsKeyword, AlignSelf, AlignmentSafety, AvailableSpace, + BoxSizing, CompactLength, Dimension, Display, JustifyContent, JustifyItems, JustifySelf, LengthPercentage, + LengthPercentageAuto, Position, Style, }, style_helpers::{ auto, fit_content, length, max_content, min_content, percent, zero, FromFr, FromLength, FromPercent, TaffyAuto, diff --git a/src/style/alignment.rs b/src/style/alignment.rs index 9e7ea9248..35da2e203 100644 --- a/src/style/alignment.rs +++ b/src/style/alignment.rs @@ -1,121 +1,84 @@ -//! Style types for controlling alignment +//! Style types for controlling alignment. +//! +//! The public alignment types ([`AlignItems`], [`AlignContent`], and their aliases) are +//! structs with two orthogonal fields: a *position* keyword +//! ([`AlignItemsKeyword`] / [`AlignContentKeyword`]) and an *overflow-position* +//! modifier ([`AlignmentSafety`]). The pre-existing CSS spellings — `Start`, `End`, +//! `FlexStart`, `FlexEnd`, `Center`, `Stretch`, `SpaceBetween`, …, `SafeStart`, +//! `SafeEnd`, `SafeFlexStart`, `SafeFlexEnd`, `SafeCenter` — are exposed as associated +//! constants on the structs, so call sites read identically to the previous enum form. -/// Used to control how child nodes are aligned. -/// For Flexbox it controls alignment in the cross axis -/// For Grid it controls alignment in the block axis +#[cfg(feature = "parse")] +use crate::util::parse::{CssParseResult, FromCss, Parser, Token}; + +/// The position-keyword half of [`AlignItems`] (and its aliases `AlignSelf`, +/// `JustifyItems`, `JustifySelf`). /// -/// [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/align-items) +/// Compute paths match on this enum directly so every match is exhaustive and +/// requires no `Safe*` siblings. #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum AlignItems { - /// Items are packed toward the start of the axis +#[repr(u8)] +pub enum AlignItemsKeyword { + /// Items are packed toward the start of the axis. Start, - /// Items are packed toward the end of the axis + /// Items are packed toward the end of the axis. End, /// Items are packed towards the flex-relative start of the axis. /// - /// For flex containers with flex_direction RowReverse or ColumnReverse this is equivalent - /// to End. In all other cases it is equivalent to Start. + /// For flex containers with flex_direction RowReverse or ColumnReverse this is + /// equivalent to End. In all other cases it is equivalent to Start. FlexStart, /// Items are packed towards the flex-relative end of the axis. /// - /// For flex containers with flex_direction RowReverse or ColumnReverse this is equivalent - /// to Start. In all other cases it is equivalent to End. + /// For flex containers with flex_direction RowReverse or ColumnReverse this is + /// equivalent to Start. In all other cases it is equivalent to End. FlexEnd, - /// Items are packed along the center of the cross axis + /// Items are packed along the center of the cross axis. Center, - /// Items are aligned such as their baselines align + /// Items are aligned such as their baselines align. Baseline, - /// Stretch to fill the container + /// Stretch to fill the container. Stretch, } -#[cfg(feature = "parse")] -crate::util::parse::impl_parse_for_keyword_enum!(AlignItems, - "start" => Start, - "end" => End, - "flex-start" => FlexStart, - "flex-end" => FlexEnd, - "center" => Center, - "baseline" => Baseline, - "stretch" => Stretch, -); - -/// Used to control how child nodes are aligned. -/// Does not apply to Flexbox, and will be ignored if specified on a flex container -/// For Grid it controls alignment in the inline axis +/// The position-keyword half of [`AlignContent`] (and its alias `JustifyContent`). /// -/// [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/justify-items) -pub type JustifyItems = AlignItems; -/// Controls alignment of an individual node -/// -/// Overrides the parent Node's `AlignItems` property. -/// For Flexbox it controls alignment in the cross axis -/// For Grid it controls alignment in the block axis -/// -/// [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/align-self) -pub type AlignSelf = AlignItems; -/// Controls alignment of an individual node -/// -/// Overrides the parent Node's `JustifyItems` property. -/// Does not apply to Flexbox, and will be ignored if specified on a flex child -/// For Grid it controls alignment in the inline axis -/// -/// [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/justify-self) -pub type JustifySelf = AlignItems; - -/// Sets the distribution of space between and around content items -/// For Flexbox it controls alignment in the cross axis -/// For Grid it controls alignment in the block axis -/// -/// [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/align-content) +/// Compute paths match on this enum directly so every match is exhaustive and +/// requires no `Safe*` siblings. #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum AlignContent { - /// Items are packed toward the start of the axis +#[repr(u8)] +pub enum AlignContentKeyword { + /// Items are packed toward the start of the axis. Start, - /// Items are packed toward the end of the axis + /// Items are packed toward the end of the axis. End, /// Items are packed towards the flex-relative start of the axis. - /// - /// For flex containers with flex_direction RowReverse or ColumnReverse this is equivalent - /// to End. In all other cases it is equivalent to Start. FlexStart, /// Items are packed towards the flex-relative end of the axis. - /// - /// For flex containers with flex_direction RowReverse or ColumnReverse this is equivalent - /// to Start. In all other cases it is equivalent to End. FlexEnd, - /// Items are centered around the middle of the axis + /// Items are centered around the middle of the axis. Center, - /// Items are stretched to fill the container + /// Items are stretched to fill the container. Stretch, - /// The first and last items are aligned flush with the edges of the container (no gap) - /// The gap between items is distributed evenly. + /// The first and last items are aligned flush with the edges of the container + /// (no gap). The gap between items is distributed evenly. SpaceBetween, - /// The gap between the first and last items is exactly THE SAME as the gap between items. - /// The gaps are distributed evenly + /// The gap between the first and last items is exactly THE SAME as the gap + /// between items. The gaps are distributed evenly. SpaceEvenly, - /// The gap between the first and last items is exactly HALF the gap between items. - /// The gaps are distributed evenly in proportion to these ratios. + /// The gap between the first and last items is exactly HALF the gap between + /// items. The gaps are distributed evenly in proportion to these ratios. SpaceAround, } -#[cfg(feature = "parse")] -crate::util::parse::impl_parse_for_keyword_enum!(AlignContent, - "start" => Start, - "end" => End, - "flex-start" => FlexStart, - "flex-end" => FlexEnd, - "center" => Center, - "stretch" => Stretch, - "space-between" => SpaceBetween, - "space-evenly" => SpaceEvenly, - "space-around" => SpaceAround, -); - -impl AlignContent { - /// Returns the reversed alignment for RTL (right-to-left) contexts. +impl AlignContentKeyword { + /// Returns the reversed keyword for RTL (right-to-left) contexts: `Start`↔`End`, + /// `FlexStart`↔`FlexEnd`. `Stretch` maps to `End` to preserve the layout + /// algorithms' historical handling. Center and the distribution keywords + /// (`SpaceBetween`, `SpaceEvenly`, `SpaceAround`) are unaffected because their + /// visual placement is direction-symmetric. pub(crate) fn reversed(self) -> Self { match self { Self::Start => Self::End, @@ -123,14 +86,642 @@ impl AlignContent { Self::FlexStart => Self::FlexEnd, Self::FlexEnd => Self::FlexStart, Self::Stretch => Self::End, - style => style, + Self::Center | Self::SpaceBetween | Self::SpaceEvenly | Self::SpaceAround => self, } } } -/// Sets the distribution of space between and around content items -/// For Flexbox it controls alignment in the main axis -/// For Grid it controls alignment in the inline axis +/// The overflow-position modifier per [CSS Box Alignment §4.3][css-align-overflow]. +/// +/// `Safe` falls back to start-edge alignment when the alignment subject would +/// overflow the alignment container, so the start of the content stays visible. +/// `Unsafe` (the default) keeps the requested alignment even when that causes +/// overflow at the start edge. +/// +/// CSS only defines `safe` / `unsafe` against the position values `start`, `end`, +/// `flex-start`, `flex-end`, `center`. The struct shape does not enforce that +/// constraint at the type level — the parser rejects invalid combinations, and +/// the compute pass treats `Safe` paired with a non-position keyword (`Stretch`, +/// `Baseline`, `Space*`) the same as `Unsafe`. +/// +/// [css-align-overflow]: https://www.w3.org/TR/css-align-3/#overflow-values +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[repr(u8)] +pub enum AlignmentSafety { + /// Default — keeps the requested alignment even when the subject overflows the + /// alignment container at the start edge. + Unsafe, + /// Falls back to the start edge when the subject would overflow, to avoid data + /// loss. + Safe, +} + +/// Used to control how child nodes are aligned. +/// For Flexbox it controls alignment in the cross axis. +/// For Grid it controls alignment in the block axis. +/// +/// [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/align-items) +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct AlignItems { + /// Position keyword. + pub keyword: AlignItemsKeyword, + /// Overflow-position modifier (`safe` / `unsafe`). + pub safety: AlignmentSafety, +} + +#[allow(non_upper_case_globals)] +impl AlignItems { + /// Items are packed toward the start of the axis. + pub const Start: Self = Self { keyword: AlignItemsKeyword::Start, safety: AlignmentSafety::Unsafe }; + /// Items are packed toward the end of the axis. + pub const End: Self = Self { keyword: AlignItemsKeyword::End, safety: AlignmentSafety::Unsafe }; + /// Items are packed towards the flex-relative start of the axis. + pub const FlexStart: Self = Self { keyword: AlignItemsKeyword::FlexStart, safety: AlignmentSafety::Unsafe }; + /// Items are packed towards the flex-relative end of the axis. + pub const FlexEnd: Self = Self { keyword: AlignItemsKeyword::FlexEnd, safety: AlignmentSafety::Unsafe }; + /// Items are packed along the center of the cross axis. + pub const Center: Self = Self { keyword: AlignItemsKeyword::Center, safety: AlignmentSafety::Unsafe }; + /// Items are aligned such as their baselines align. + pub const Baseline: Self = Self { keyword: AlignItemsKeyword::Baseline, safety: AlignmentSafety::Unsafe }; + /// Stretch to fill the container. + pub const Stretch: Self = Self { keyword: AlignItemsKeyword::Stretch, safety: AlignmentSafety::Unsafe }; + /// Like [`AlignItems::Start`], but falls back to [`AlignItems::Start`] when the + /// alignment subject overflows the alignment container, to avoid data loss. + pub const SafeStart: Self = Self { keyword: AlignItemsKeyword::Start, safety: AlignmentSafety::Safe }; + /// Like [`AlignItems::End`], but falls back to [`AlignItems::Start`] when the + /// alignment subject overflows the alignment container, to avoid data loss. + pub const SafeEnd: Self = Self { keyword: AlignItemsKeyword::End, safety: AlignmentSafety::Safe }; + /// Like [`AlignItems::FlexStart`], but falls back to [`AlignItems::Start`] when the + /// alignment subject overflows the alignment container, to avoid data loss. + pub const SafeFlexStart: Self = Self { keyword: AlignItemsKeyword::FlexStart, safety: AlignmentSafety::Safe }; + /// Like [`AlignItems::FlexEnd`], but falls back to [`AlignItems::Start`] when the + /// alignment subject overflows the alignment container, to avoid data loss. + pub const SafeFlexEnd: Self = Self { keyword: AlignItemsKeyword::FlexEnd, safety: AlignmentSafety::Safe }; + /// Like [`AlignItems::Center`], but falls back to [`AlignItems::Start`] when the + /// alignment subject overflows the alignment container, to avoid data loss. + pub const SafeCenter: Self = Self { keyword: AlignItemsKeyword::Center, safety: AlignmentSafety::Safe }; + + /// Returns `true` iff this carries the `safe` overflow-position modifier. + #[inline] + pub const fn is_safe(self) -> bool { + matches!(self.safety, AlignmentSafety::Safe) + } + + /// Returns the underlying position keyword, discarding the safety modifier. + #[inline] + pub const fn keyword(self) -> AlignItemsKeyword { + self.keyword + } +} + +#[cfg(feature = "parse")] +impl FromCss for AlignItems { + fn from_css<'i>(input: &mut Parser<'i, '_>) -> CssParseResult<'i, Self> { + let first = input.expect_ident()?.clone(); + cssparser::match_ignore_ascii_case! { &*first, + "safe" => { + let pos = input.expect_ident()?.clone(); + cssparser::match_ignore_ascii_case! { &*pos, + "start" => Ok(Self::SafeStart), + "end" => Ok(Self::SafeEnd), + "flex-start" => Ok(Self::SafeFlexStart), + "flex-end" => Ok(Self::SafeFlexEnd), + "center" => Ok(Self::SafeCenter), + _ => Err(input.new_unexpected_token_error(Token::Ident(pos))), + } + }, + "unsafe" => { + let pos = input.expect_ident()?.clone(); + cssparser::match_ignore_ascii_case! { &*pos, + "start" => Ok(Self::Start), + "end" => Ok(Self::End), + "flex-start" => Ok(Self::FlexStart), + "flex-end" => Ok(Self::FlexEnd), + "center" => Ok(Self::Center), + _ => Err(input.new_unexpected_token_error(Token::Ident(pos))), + } + }, + "start" => Ok(Self::Start), + "end" => Ok(Self::End), + "flex-start" => Ok(Self::FlexStart), + "flex-end" => Ok(Self::FlexEnd), + "center" => Ok(Self::Center), + "baseline" => Ok(Self::Baseline), + "stretch" => Ok(Self::Stretch), + _ => Err(input.new_unexpected_token_error(Token::Ident(first))), + } + } +} + +#[cfg(feature = "parse")] +crate::util::parse::from_str_from_css!(AlignItems); + +/// Used to control how child nodes are aligned. +/// Does not apply to Flexbox, and will be ignored if specified on a flex container. +/// For Grid it controls alignment in the inline axis. +/// +/// [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/justify-items) +pub type JustifyItems = AlignItems; +/// Controls alignment of an individual node. +/// +/// Overrides the parent Node's `AlignItems` property. +/// For Flexbox it controls alignment in the cross axis. +/// For Grid it controls alignment in the block axis. +/// +/// [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/align-self) +pub type AlignSelf = AlignItems; +/// Controls alignment of an individual node. +/// +/// Overrides the parent Node's `JustifyItems` property. +/// Does not apply to Flexbox, and will be ignored if specified on a flex child. +/// For Grid it controls alignment in the inline axis. +/// +/// [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/justify-self) +pub type JustifySelf = AlignItems; + +/// Sets the distribution of space between and around content items. +/// For Flexbox it controls alignment in the cross axis. +/// For Grid it controls alignment in the block axis. +/// +/// [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/align-content) +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct AlignContent { + /// Position keyword. + pub keyword: AlignContentKeyword, + /// Overflow-position modifier (`safe` / `unsafe`). + pub safety: AlignmentSafety, +} + +#[allow(non_upper_case_globals)] +impl AlignContent { + /// Items are packed toward the start of the axis. + pub const Start: Self = Self { keyword: AlignContentKeyword::Start, safety: AlignmentSafety::Unsafe }; + /// Items are packed toward the end of the axis. + pub const End: Self = Self { keyword: AlignContentKeyword::End, safety: AlignmentSafety::Unsafe }; + /// Items are packed towards the flex-relative start of the axis. + pub const FlexStart: Self = Self { keyword: AlignContentKeyword::FlexStart, safety: AlignmentSafety::Unsafe }; + /// Items are packed towards the flex-relative end of the axis. + pub const FlexEnd: Self = Self { keyword: AlignContentKeyword::FlexEnd, safety: AlignmentSafety::Unsafe }; + /// Items are centered around the middle of the axis. + pub const Center: Self = Self { keyword: AlignContentKeyword::Center, safety: AlignmentSafety::Unsafe }; + /// Items are stretched to fill the container. + pub const Stretch: Self = Self { keyword: AlignContentKeyword::Stretch, safety: AlignmentSafety::Unsafe }; + /// The first and last items are aligned flush with the edges of the container. + pub const SpaceBetween: Self = Self { keyword: AlignContentKeyword::SpaceBetween, safety: AlignmentSafety::Unsafe }; + /// The gap between the first and last items equals the gap between items. + pub const SpaceEvenly: Self = Self { keyword: AlignContentKeyword::SpaceEvenly, safety: AlignmentSafety::Unsafe }; + /// The gap between the first and last items is half the gap between items. + pub const SpaceAround: Self = Self { keyword: AlignContentKeyword::SpaceAround, safety: AlignmentSafety::Unsafe }; + /// Like [`AlignContent::Start`], but falls back to [`AlignContent::Start`] when the + /// content overflows the alignment container, to avoid data loss. + pub const SafeStart: Self = Self { keyword: AlignContentKeyword::Start, safety: AlignmentSafety::Safe }; + /// Like [`AlignContent::End`], but falls back to [`AlignContent::Start`] when the + /// content overflows the alignment container, to avoid data loss. + pub const SafeEnd: Self = Self { keyword: AlignContentKeyword::End, safety: AlignmentSafety::Safe }; + /// Like [`AlignContent::FlexStart`], but falls back to [`AlignContent::Start`] when + /// the content overflows the alignment container, to avoid data loss. + pub const SafeFlexStart: Self = Self { keyword: AlignContentKeyword::FlexStart, safety: AlignmentSafety::Safe }; + /// Like [`AlignContent::FlexEnd`], but falls back to [`AlignContent::Start`] when the + /// content overflows the alignment container, to avoid data loss. + pub const SafeFlexEnd: Self = Self { keyword: AlignContentKeyword::FlexEnd, safety: AlignmentSafety::Safe }; + /// Like [`AlignContent::Center`], but falls back to [`AlignContent::Start`] when the + /// content overflows the alignment container, to avoid data loss. + pub const SafeCenter: Self = Self { keyword: AlignContentKeyword::Center, safety: AlignmentSafety::Safe }; + + /// Returns `true` iff this carries the `safe` overflow-position modifier. + #[inline] + pub const fn is_safe(self) -> bool { + matches!(self.safety, AlignmentSafety::Safe) + } + + /// Returns the underlying position keyword, discarding the safety modifier. + #[inline] + pub const fn keyword(self) -> AlignContentKeyword { + self.keyword + } +} + +#[cfg(feature = "parse")] +impl FromCss for AlignContent { + fn from_css<'i>(input: &mut Parser<'i, '_>) -> CssParseResult<'i, Self> { + let first = input.expect_ident()?.clone(); + cssparser::match_ignore_ascii_case! { &*first, + "safe" => { + let pos = input.expect_ident()?.clone(); + cssparser::match_ignore_ascii_case! { &*pos, + "start" => Ok(Self::SafeStart), + "end" => Ok(Self::SafeEnd), + "flex-start" => Ok(Self::SafeFlexStart), + "flex-end" => Ok(Self::SafeFlexEnd), + "center" => Ok(Self::SafeCenter), + _ => Err(input.new_unexpected_token_error(Token::Ident(pos))), + } + }, + "unsafe" => { + let pos = input.expect_ident()?.clone(); + cssparser::match_ignore_ascii_case! { &*pos, + "start" => Ok(Self::Start), + "end" => Ok(Self::End), + "flex-start" => Ok(Self::FlexStart), + "flex-end" => Ok(Self::FlexEnd), + "center" => Ok(Self::Center), + _ => Err(input.new_unexpected_token_error(Token::Ident(pos))), + } + }, + "start" => Ok(Self::Start), + "end" => Ok(Self::End), + "flex-start" => Ok(Self::FlexStart), + "flex-end" => Ok(Self::FlexEnd), + "center" => Ok(Self::Center), + "stretch" => Ok(Self::Stretch), + "space-between" => Ok(Self::SpaceBetween), + "space-evenly" => Ok(Self::SpaceEvenly), + "space-around" => Ok(Self::SpaceAround), + _ => Err(input.new_unexpected_token_error(Token::Ident(first))), + } + } +} + +#[cfg(feature = "parse")] +crate::util::parse::from_str_from_css!(AlignContent); + +/// Sets the distribution of space between and around content items. +/// For Flexbox it controls alignment in the main axis. +/// For Grid it controls alignment in the inline axis. /// /// [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/justify-content) pub type JustifyContent = AlignContent; + +// --------------------------------------------------------------------------- +// Serde — custom impls preserve the pre-struct wire format (single tag string +// per public spelling) so consumers reading data serialized before the refactor +// continue to deserialize correctly. +// --------------------------------------------------------------------------- + +/// Canonical tag-string set accepted by [`AlignItems`] serde deserialization, used in +/// `unknown_variant` errors. Mirrors the spellings produced by `Serialize`. +#[cfg(feature = "serde")] +const ALIGN_ITEMS_NAMES: &[&str] = &[ + "Start", + "End", + "FlexStart", + "FlexEnd", + "Center", + "Baseline", + "Stretch", + "SafeStart", + "SafeEnd", + "SafeFlexStart", + "SafeFlexEnd", + "SafeCenter", +]; + +#[cfg(feature = "serde")] +impl serde::Serialize for AlignItems { + fn serialize(&self, serializer: S) -> Result { + let name = match (self.keyword, self.safety) { + (AlignItemsKeyword::Start, AlignmentSafety::Unsafe) => "Start", + (AlignItemsKeyword::End, AlignmentSafety::Unsafe) => "End", + (AlignItemsKeyword::FlexStart, AlignmentSafety::Unsafe) => "FlexStart", + (AlignItemsKeyword::FlexEnd, AlignmentSafety::Unsafe) => "FlexEnd", + (AlignItemsKeyword::Center, AlignmentSafety::Unsafe) => "Center", + (AlignItemsKeyword::Baseline, _) => "Baseline", + (AlignItemsKeyword::Stretch, _) => "Stretch", + (AlignItemsKeyword::Start, AlignmentSafety::Safe) => "SafeStart", + (AlignItemsKeyword::End, AlignmentSafety::Safe) => "SafeEnd", + (AlignItemsKeyword::FlexStart, AlignmentSafety::Safe) => "SafeFlexStart", + (AlignItemsKeyword::FlexEnd, AlignmentSafety::Safe) => "SafeFlexEnd", + (AlignItemsKeyword::Center, AlignmentSafety::Safe) => "SafeCenter", + }; + serializer.serialize_str(name) + } +} + +#[cfg(feature = "serde")] +impl<'de> serde::Deserialize<'de> for AlignItems { + fn deserialize>(deserializer: D) -> Result { + struct AlignItemsVisitor; + impl<'de> serde::de::Visitor<'de> for AlignItemsVisitor { + type Value = AlignItems; + fn expecting(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { + fmt.write_str("an AlignItems variant tag string") + } + fn visit_str(self, v: &str) -> Result { + Ok(match v { + "Start" => AlignItems::Start, + "End" => AlignItems::End, + "FlexStart" => AlignItems::FlexStart, + "FlexEnd" => AlignItems::FlexEnd, + "Center" => AlignItems::Center, + "Baseline" => AlignItems::Baseline, + "Stretch" => AlignItems::Stretch, + "SafeStart" => AlignItems::SafeStart, + "SafeEnd" => AlignItems::SafeEnd, + "SafeFlexStart" => AlignItems::SafeFlexStart, + "SafeFlexEnd" => AlignItems::SafeFlexEnd, + "SafeCenter" => AlignItems::SafeCenter, + other => return Err(E::unknown_variant(other, ALIGN_ITEMS_NAMES)), + }) + } + } + deserializer.deserialize_str(AlignItemsVisitor) + } +} + +/// Canonical tag-string set accepted by [`AlignContent`] serde deserialization, used in +/// `unknown_variant` errors. Mirrors the spellings produced by `Serialize`. +#[cfg(feature = "serde")] +const ALIGN_CONTENT_NAMES: &[&str] = &[ + "Start", + "End", + "FlexStart", + "FlexEnd", + "Center", + "Stretch", + "SpaceBetween", + "SpaceEvenly", + "SpaceAround", + "SafeStart", + "SafeEnd", + "SafeFlexStart", + "SafeFlexEnd", + "SafeCenter", +]; + +#[cfg(feature = "serde")] +impl serde::Serialize for AlignContent { + fn serialize(&self, serializer: S) -> Result { + let name = match (self.keyword, self.safety) { + (AlignContentKeyword::Start, AlignmentSafety::Unsafe) => "Start", + (AlignContentKeyword::End, AlignmentSafety::Unsafe) => "End", + (AlignContentKeyword::FlexStart, AlignmentSafety::Unsafe) => "FlexStart", + (AlignContentKeyword::FlexEnd, AlignmentSafety::Unsafe) => "FlexEnd", + (AlignContentKeyword::Center, AlignmentSafety::Unsafe) => "Center", + (AlignContentKeyword::Stretch, _) => "Stretch", + (AlignContentKeyword::SpaceBetween, _) => "SpaceBetween", + (AlignContentKeyword::SpaceEvenly, _) => "SpaceEvenly", + (AlignContentKeyword::SpaceAround, _) => "SpaceAround", + (AlignContentKeyword::Start, AlignmentSafety::Safe) => "SafeStart", + (AlignContentKeyword::End, AlignmentSafety::Safe) => "SafeEnd", + (AlignContentKeyword::FlexStart, AlignmentSafety::Safe) => "SafeFlexStart", + (AlignContentKeyword::FlexEnd, AlignmentSafety::Safe) => "SafeFlexEnd", + (AlignContentKeyword::Center, AlignmentSafety::Safe) => "SafeCenter", + }; + serializer.serialize_str(name) + } +} + +#[cfg(feature = "serde")] +impl<'de> serde::Deserialize<'de> for AlignContent { + fn deserialize>(deserializer: D) -> Result { + struct AlignContentVisitor; + impl<'de> serde::de::Visitor<'de> for AlignContentVisitor { + type Value = AlignContent; + fn expecting(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { + fmt.write_str("an AlignContent variant tag string") + } + fn visit_str(self, v: &str) -> Result { + Ok(match v { + "Start" => AlignContent::Start, + "End" => AlignContent::End, + "FlexStart" => AlignContent::FlexStart, + "FlexEnd" => AlignContent::FlexEnd, + "Center" => AlignContent::Center, + "Stretch" => AlignContent::Stretch, + "SpaceBetween" => AlignContent::SpaceBetween, + "SpaceEvenly" => AlignContent::SpaceEvenly, + "SpaceAround" => AlignContent::SpaceAround, + "SafeStart" => AlignContent::SafeStart, + "SafeEnd" => AlignContent::SafeEnd, + "SafeFlexStart" => AlignContent::SafeFlexStart, + "SafeFlexEnd" => AlignContent::SafeFlexEnd, + "SafeCenter" => AlignContent::SafeCenter, + other => return Err(E::unknown_variant(other, ALIGN_CONTENT_NAMES)), + }) + } + } + deserializer.deserialize_str(AlignContentVisitor) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use core::mem::size_of; + + // Size budget — struct = 1B keyword + 1B safety, no niche packing. + // Pre-refactor each was a single-byte enum; spec §V13 caps regression at +2B. + #[test] + fn align_types_within_size_budget() { + assert!(size_of::() <= 2, "AlignItems grew to {}", size_of::()); + assert!(size_of::() <= 2, "AlignContent grew to {}", size_of::()); + assert!(size_of::>() <= 3); + assert!(size_of::>() <= 3); + } + + #[test] + fn align_items_is_safe() { + assert!(AlignItems::SafeStart.is_safe()); + assert!(AlignItems::SafeEnd.is_safe()); + assert!(AlignItems::SafeFlexStart.is_safe()); + assert!(AlignItems::SafeFlexEnd.is_safe()); + assert!(AlignItems::SafeCenter.is_safe()); + assert!(!AlignItems::Start.is_safe()); + assert!(!AlignItems::End.is_safe()); + assert!(!AlignItems::FlexStart.is_safe()); + assert!(!AlignItems::FlexEnd.is_safe()); + assert!(!AlignItems::Center.is_safe()); + assert!(!AlignItems::Baseline.is_safe()); + assert!(!AlignItems::Stretch.is_safe()); + } + + #[test] + fn align_items_keyword_strips_safe() { + assert_eq!(AlignItems::SafeStart.keyword(), AlignItemsKeyword::Start); + assert_eq!(AlignItems::SafeEnd.keyword(), AlignItemsKeyword::End); + assert_eq!(AlignItems::SafeFlexStart.keyword(), AlignItemsKeyword::FlexStart); + assert_eq!(AlignItems::SafeFlexEnd.keyword(), AlignItemsKeyword::FlexEnd); + assert_eq!(AlignItems::SafeCenter.keyword(), AlignItemsKeyword::Center); + } + + #[test] + fn align_items_keyword_passthrough() { + assert_eq!(AlignItems::Start.keyword(), AlignItemsKeyword::Start); + assert_eq!(AlignItems::Stretch.keyword(), AlignItemsKeyword::Stretch); + assert_eq!(AlignItems::Baseline.keyword(), AlignItemsKeyword::Baseline); + assert_eq!(AlignItems::FlexStart.keyword(), AlignItemsKeyword::FlexStart); + } + + #[test] + fn align_content_is_safe() { + assert!(AlignContent::SafeStart.is_safe()); + assert!(AlignContent::SafeCenter.is_safe()); + assert!(!AlignContent::SpaceBetween.is_safe()); + assert!(!AlignContent::Stretch.is_safe()); + } + + #[test] + fn align_content_keyword_strips_safe() { + assert_eq!(AlignContent::SafeStart.keyword(), AlignContentKeyword::Start); + assert_eq!(AlignContent::SafeFlexEnd.keyword(), AlignContentKeyword::FlexEnd); + assert_eq!(AlignContent::SafeCenter.keyword(), AlignContentKeyword::Center); + assert_eq!(AlignContent::SpaceBetween.keyword(), AlignContentKeyword::SpaceBetween); + } + + #[test] + fn align_content_keyword_reversed_swaps_start_end() { + assert_eq!(AlignContentKeyword::Start.reversed(), AlignContentKeyword::End); + assert_eq!(AlignContentKeyword::End.reversed(), AlignContentKeyword::Start); + assert_eq!(AlignContentKeyword::FlexStart.reversed(), AlignContentKeyword::FlexEnd); + assert_eq!(AlignContentKeyword::FlexEnd.reversed(), AlignContentKeyword::FlexStart); + // Stretch reverses to End — preserves pre-refactor behaviour. + assert_eq!(AlignContentKeyword::Stretch.reversed(), AlignContentKeyword::End); + assert_eq!(AlignContentKeyword::Center.reversed(), AlignContentKeyword::Center); + assert_eq!(AlignContentKeyword::SpaceBetween.reversed(), AlignContentKeyword::SpaceBetween); + assert_eq!(AlignContentKeyword::SpaceEvenly.reversed(), AlignContentKeyword::SpaceEvenly); + assert_eq!(AlignContentKeyword::SpaceAround.reversed(), AlignContentKeyword::SpaceAround); + } + + #[cfg(feature = "parse")] + #[test] + fn parse_align_items_plain() { + assert_eq!("start".parse::().unwrap(), AlignItems::Start); + assert_eq!("end".parse::().unwrap(), AlignItems::End); + assert_eq!("flex-start".parse::().unwrap(), AlignItems::FlexStart); + assert_eq!("flex-end".parse::().unwrap(), AlignItems::FlexEnd); + assert_eq!("center".parse::().unwrap(), AlignItems::Center); + assert_eq!("baseline".parse::().unwrap(), AlignItems::Baseline); + assert_eq!("stretch".parse::().unwrap(), AlignItems::Stretch); + } + + #[cfg(feature = "parse")] + #[test] + fn parse_align_items_safe() { + assert_eq!("safe start".parse::().unwrap(), AlignItems::SafeStart); + assert_eq!("safe end".parse::().unwrap(), AlignItems::SafeEnd); + assert_eq!("safe flex-start".parse::().unwrap(), AlignItems::SafeFlexStart); + assert_eq!("safe flex-end".parse::().unwrap(), AlignItems::SafeFlexEnd); + assert_eq!("safe center".parse::().unwrap(), AlignItems::SafeCenter); + } + + #[cfg(feature = "parse")] + #[test] + fn parse_align_items_safe_case_insensitive() { + assert_eq!("SAFE Start".parse::().unwrap(), AlignItems::SafeStart); + assert_eq!("Safe FLEX-end".parse::().unwrap(), AlignItems::SafeFlexEnd); + } + + #[cfg(feature = "parse")] + #[test] + fn parse_align_items_unsafe_drops_modifier() { + assert_eq!("unsafe start".parse::().unwrap(), AlignItems::Start); + assert_eq!("unsafe end".parse::().unwrap(), AlignItems::End); + assert_eq!("unsafe center".parse::().unwrap(), AlignItems::Center); + } + + #[cfg(feature = "parse")] + #[test] + fn parse_align_items_rejects_invalid_safe_combos() { + assert!("safe stretch".parse::().is_err()); + assert!("safe baseline".parse::().is_err()); + assert!("safe space-between".parse::().is_err()); + assert!("safe".parse::().is_err()); + assert!("safe garbage".parse::().is_err()); + assert!("unsafe stretch".parse::().is_err()); + assert!("unsafe baseline".parse::().is_err()); + } + + #[cfg(feature = "parse")] + #[test] + fn parse_align_content_plain() { + assert_eq!("start".parse::().unwrap(), AlignContent::Start); + assert_eq!("space-between".parse::().unwrap(), AlignContent::SpaceBetween); + assert_eq!("space-evenly".parse::().unwrap(), AlignContent::SpaceEvenly); + assert_eq!("space-around".parse::().unwrap(), AlignContent::SpaceAround); + assert_eq!("stretch".parse::().unwrap(), AlignContent::Stretch); + } + + #[cfg(feature = "parse")] + #[test] + fn parse_align_content_safe() { + assert_eq!("safe start".parse::().unwrap(), AlignContent::SafeStart); + assert_eq!("safe end".parse::().unwrap(), AlignContent::SafeEnd); + assert_eq!("safe flex-start".parse::().unwrap(), AlignContent::SafeFlexStart); + assert_eq!("safe flex-end".parse::().unwrap(), AlignContent::SafeFlexEnd); + assert_eq!("safe center".parse::().unwrap(), AlignContent::SafeCenter); + } + + #[cfg(feature = "parse")] + #[test] + fn parse_align_content_unsafe_drops_modifier() { + assert_eq!("unsafe start".parse::().unwrap(), AlignContent::Start); + assert_eq!("unsafe flex-end".parse::().unwrap(), AlignContent::FlexEnd); + } + + #[cfg(feature = "parse")] + #[test] + fn parse_align_content_rejects_invalid_safe_combos() { + assert!("safe stretch".parse::().is_err()); + assert!("safe space-between".parse::().is_err()); + assert!("safe space-evenly".parse::().is_err()); + assert!("safe space-around".parse::().is_err()); + assert!("safe".parse::().is_err()); + assert!("unsafe stretch".parse::().is_err()); + assert!("unsafe space-between".parse::().is_err()); + } + + #[cfg(feature = "serde")] + #[test] + fn serde_align_items_round_trip() { + let cases = [ + (AlignItems::Start, "\"Start\""), + (AlignItems::End, "\"End\""), + (AlignItems::FlexStart, "\"FlexStart\""), + (AlignItems::FlexEnd, "\"FlexEnd\""), + (AlignItems::Center, "\"Center\""), + (AlignItems::Baseline, "\"Baseline\""), + (AlignItems::Stretch, "\"Stretch\""), + (AlignItems::SafeStart, "\"SafeStart\""), + (AlignItems::SafeEnd, "\"SafeEnd\""), + (AlignItems::SafeFlexStart, "\"SafeFlexStart\""), + (AlignItems::SafeFlexEnd, "\"SafeFlexEnd\""), + (AlignItems::SafeCenter, "\"SafeCenter\""), + ]; + for (value, expected) in cases { + let serialized = serde_json::to_string(&value).unwrap(); + assert_eq!(serialized, expected, "serialize {:?}", value); + let deserialized: AlignItems = serde_json::from_str(expected).unwrap(); + assert_eq!(deserialized, value, "round-trip {:?}", value); + } + assert!(serde_json::from_str::("\"NotAVariant\"").is_err()); + } + + #[cfg(feature = "serde")] + #[test] + fn serde_align_content_round_trip() { + let cases = [ + (AlignContent::Start, "\"Start\""), + (AlignContent::End, "\"End\""), + (AlignContent::FlexStart, "\"FlexStart\""), + (AlignContent::FlexEnd, "\"FlexEnd\""), + (AlignContent::Center, "\"Center\""), + (AlignContent::Stretch, "\"Stretch\""), + (AlignContent::SpaceBetween, "\"SpaceBetween\""), + (AlignContent::SpaceEvenly, "\"SpaceEvenly\""), + (AlignContent::SpaceAround, "\"SpaceAround\""), + (AlignContent::SafeStart, "\"SafeStart\""), + (AlignContent::SafeEnd, "\"SafeEnd\""), + (AlignContent::SafeFlexStart, "\"SafeFlexStart\""), + (AlignContent::SafeFlexEnd, "\"SafeFlexEnd\""), + (AlignContent::SafeCenter, "\"SafeCenter\""), + ]; + for (value, expected) in cases { + let serialized = serde_json::to_string(&value).unwrap(); + assert_eq!(serialized, expected, "serialize {:?}", value); + let deserialized: AlignContent = serde_json::from_str(expected).unwrap(); + assert_eq!(deserialized, value, "round-trip {:?}", value); + } + assert!(serde_json::from_str::("\"NotAVariant\"").is_err()); + } +} diff --git a/src/style/available_space.rs b/src/style/available_space.rs index d2def420c..2f2715fe8 100644 --- a/src/style/available_space.rs +++ b/src/style/available_space.rs @@ -6,7 +6,7 @@ use crate::{ }; #[cfg(feature = "parse")] -use crate::util::parse::{from_str_from_css, parse_css_str_entirely, CssParseResult, FromCss, Parser, Token}; +use crate::util::parse::{from_str_from_css, CssParseResult, FromCss, Parser, Token}; /// The amount of space available to a node in a given axis /// diff --git a/src/style/dimension.rs b/src/style/dimension.rs index 1307b194f..444bd979b 100644 --- a/src/style/dimension.rs +++ b/src/style/dimension.rs @@ -3,7 +3,7 @@ use super::CompactLength; use crate::geometry::Rect; use crate::style_helpers::{FromLength, FromPercent, TaffyAuto, TaffyZero}; #[cfg(feature = "parse")] -use crate::util::parse::{from_str_from_css, parse_css_str_entirely, CssParseResult, FromCss, Parser, Token}; +use crate::util::parse::{from_str_from_css, CssParseResult, FromCss, Parser, Token}; /// A unit of linear measurement /// diff --git a/src/style/mod.rs b/src/style/mod.rs index e32018624..50f1d194c 100644 --- a/src/style/mod.rs +++ b/src/style/mod.rs @@ -13,7 +13,10 @@ mod float; #[cfg(feature = "grid")] mod grid; -pub use self::alignment::{AlignContent, AlignItems, AlignSelf, JustifyContent, JustifyItems, JustifySelf}; +pub use self::alignment::{ + AlignContent, AlignContentKeyword, AlignItems, AlignItemsKeyword, AlignSelf, AlignmentSafety, JustifyContent, + JustifyItems, JustifySelf, +}; pub use self::available_space::AvailableSpace; pub use self::compact_length::CompactLength; pub use self::dimension::{Dimension, LengthPercentage, LengthPercentageAuto}; @@ -1306,10 +1309,16 @@ mod tests { assert_type_size::>(32); assert_type_size::>(32); - // Alignment - assert_type_size::(1); - assert_type_size::(1); - assert_type_size::>(1); + // Alignment — `AlignContent` and `AlignItems` are structs of two `#[repr(u8)]` enums + // (position keyword + safety modifier). Niche-packing in the safety byte (only 2 of + // 256 values used) lets `Option<_>` stay the same size as the bare struct. + assert_type_size::(1); + assert_type_size::(1); + assert_type_size::(1); + assert_type_size::(2); + assert_type_size::(2); + assert_type_size::>(2); + assert_type_size::>(2); // Flexbox Container assert_type_size::(1); @@ -1327,12 +1336,12 @@ mod tests { assert_type_size::>(56); assert_type_size::>(32); assert_type_size::>>(64); - assert_type_size::>(536); + assert_type_size::>(544); // String-type dependent (Arc) assert_type_size::>>(56); assert_type_size::>>(24); assert_type_size::>>>(48); - assert_type_size::>>(504); + assert_type_size::>>(512); } } diff --git a/src/util/parse.rs b/src/util/parse.rs index 71dd249f6..24743fbb5 100644 --- a/src/util/parse.rs +++ b/src/util/parse.rs @@ -56,7 +56,7 @@ macro_rules! from_str_from_css { impl core::str::FromStr for $ty { type Err = $crate::ParseError; fn from_str(input: &str) -> Result { - parse_css_str_entirely(input) + $crate::util::parse::parse_css_str_entirely(input) } } }; diff --git a/test_fixtures/flex/flex_safe_align_content_end_overflow.html b/test_fixtures/flex/flex_safe_align_content_end_overflow.html new file mode 100644 index 000000000..6be1d1e92 --- /dev/null +++ b/test_fixtures/flex/flex_safe_align_content_end_overflow.html @@ -0,0 +1,18 @@ + + + + + + + Test description + + + + +
+
+
+
+ + + diff --git a/test_fixtures/flex/flex_safe_align_self_center_overflow.html b/test_fixtures/flex/flex_safe_align_self_center_overflow.html new file mode 100644 index 000000000..d830fd02e --- /dev/null +++ b/test_fixtures/flex/flex_safe_align_self_center_overflow.html @@ -0,0 +1,17 @@ + + + + + + + Test description + + + + +
+
+
+ + + diff --git a/test_fixtures/flex/flex_safe_align_self_end_overflow.html b/test_fixtures/flex/flex_safe_align_self_end_overflow.html new file mode 100644 index 000000000..486cbcdbf --- /dev/null +++ b/test_fixtures/flex/flex_safe_align_self_end_overflow.html @@ -0,0 +1,17 @@ + + + + + + + Test description + + + + +
+
+
+ + + diff --git a/test_fixtures/flex/flex_safe_justify_content_center_overflow.html b/test_fixtures/flex/flex_safe_justify_content_center_overflow.html new file mode 100644 index 000000000..1b2bd77b9 --- /dev/null +++ b/test_fixtures/flex/flex_safe_justify_content_center_overflow.html @@ -0,0 +1,18 @@ + + + + + + + Test description + + + + +
+
+
+
+ + + diff --git a/test_fixtures/flex/flex_safe_justify_content_end_overflow.html b/test_fixtures/flex/flex_safe_justify_content_end_overflow.html new file mode 100644 index 000000000..e25c8849b --- /dev/null +++ b/test_fixtures/flex/flex_safe_justify_content_end_overflow.html @@ -0,0 +1,18 @@ + + + + + + + Test description + + + + +
+
+
+
+ + + diff --git a/test_fixtures/flex/flex_unsafe_align_self_end_overflow.html b/test_fixtures/flex/flex_unsafe_align_self_end_overflow.html new file mode 100644 index 000000000..6efb571e9 --- /dev/null +++ b/test_fixtures/flex/flex_unsafe_align_self_end_overflow.html @@ -0,0 +1,17 @@ + + + + + + + Test description + + + + +
+
+
+ + + diff --git a/test_fixtures/grid/grid_safe_align_content_center_overflow.html b/test_fixtures/grid/grid_safe_align_content_center_overflow.html new file mode 100644 index 000000000..d7158ac58 --- /dev/null +++ b/test_fixtures/grid/grid_safe_align_content_center_overflow.html @@ -0,0 +1,25 @@ + + + + + + + Test description + + + + +
+
+
+
+
+
+
+
+
+
+
+ + + diff --git a/test_fixtures/grid/grid_safe_align_content_end_overflow.html b/test_fixtures/grid/grid_safe_align_content_end_overflow.html new file mode 100644 index 000000000..f570d0ab8 --- /dev/null +++ b/test_fixtures/grid/grid_safe_align_content_end_overflow.html @@ -0,0 +1,25 @@ + + + + + + + Test description + + + + +
+
+
+
+
+
+
+
+
+
+
+ + + diff --git a/test_fixtures/grid/grid_safe_align_self_end_no_overflow.html b/test_fixtures/grid/grid_safe_align_self_end_no_overflow.html new file mode 100644 index 000000000..386fa0639 --- /dev/null +++ b/test_fixtures/grid/grid_safe_align_self_end_no_overflow.html @@ -0,0 +1,17 @@ + + + + + + + Test description + + + + +
+
+
+ + + diff --git a/test_fixtures/grid/grid_safe_align_self_end_overflow.html b/test_fixtures/grid/grid_safe_align_self_end_overflow.html new file mode 100644 index 000000000..64d7cc74a --- /dev/null +++ b/test_fixtures/grid/grid_safe_align_self_end_overflow.html @@ -0,0 +1,17 @@ + + + + + + + Test description + + + + +
+
+
+ + + diff --git a/test_fixtures/grid/grid_safe_justify_content_center_overflow.html b/test_fixtures/grid/grid_safe_justify_content_center_overflow.html new file mode 100644 index 000000000..71396f0fc --- /dev/null +++ b/test_fixtures/grid/grid_safe_justify_content_center_overflow.html @@ -0,0 +1,25 @@ + + + + + + + Test description + + + + +
+
+
+
+
+
+
+
+
+
+
+ + + diff --git a/test_fixtures/grid/grid_safe_justify_content_end_no_overflow.html b/test_fixtures/grid/grid_safe_justify_content_end_no_overflow.html new file mode 100644 index 000000000..2910b59f0 --- /dev/null +++ b/test_fixtures/grid/grid_safe_justify_content_end_no_overflow.html @@ -0,0 +1,25 @@ + + + + + + + Test description + + + + +
+
+
+
+
+
+
+
+
+
+
+ + + diff --git a/test_fixtures/grid/grid_safe_justify_content_end_overflow.html b/test_fixtures/grid/grid_safe_justify_content_end_overflow.html new file mode 100644 index 000000000..52ef818b3 --- /dev/null +++ b/test_fixtures/grid/grid_safe_justify_content_end_overflow.html @@ -0,0 +1,25 @@ + + + + + + + Test description + + + + +
+
+
+
+
+
+
+
+
+
+
+ + + diff --git a/test_fixtures/grid/grid_safe_justify_self_center_overflow.html b/test_fixtures/grid/grid_safe_justify_self_center_overflow.html new file mode 100644 index 000000000..2dff71228 --- /dev/null +++ b/test_fixtures/grid/grid_safe_justify_self_center_overflow.html @@ -0,0 +1,17 @@ + + + + + + + Test description + + + + +
+
+
+ + + diff --git a/test_fixtures/grid/grid_safe_justify_self_end_overflow.html b/test_fixtures/grid/grid_safe_justify_self_end_overflow.html new file mode 100644 index 000000000..f0977fe7e --- /dev/null +++ b/test_fixtures/grid/grid_safe_justify_self_end_overflow.html @@ -0,0 +1,17 @@ + + + + + + + Test description + + + + +
+
+
+ + + diff --git a/test_fixtures/grid/grid_unsafe_align_self_end_overflow.html b/test_fixtures/grid/grid_unsafe_align_self_end_overflow.html new file mode 100644 index 000000000..68f8d2433 --- /dev/null +++ b/test_fixtures/grid/grid_unsafe_align_self_end_overflow.html @@ -0,0 +1,17 @@ + + + + + + + Test description + + + + +
+
+
+ + + diff --git a/test_fixtures/grid/grid_unsafe_justify_content_end_overflow.html b/test_fixtures/grid/grid_unsafe_justify_content_end_overflow.html new file mode 100644 index 000000000..d5007e793 --- /dev/null +++ b/test_fixtures/grid/grid_unsafe_justify_content_end_overflow.html @@ -0,0 +1,25 @@ + + + + + + + Test description + + + + +
+
+
+
+
+
+
+
+
+
+
+ + + diff --git a/tests/hand_written.rs b/tests/hand_written.rs index 965aa5d28..150f7845f 100644 --- a/tests/hand_written.rs +++ b/tests/hand_written.rs @@ -6,5 +6,6 @@ mod hand_written { mod relayout; mod root_constraints; mod rounding; + mod safe_alignment; mod serde; } diff --git a/tests/hand_written/safe_alignment.rs b/tests/hand_written/safe_alignment.rs new file mode 100644 index 000000000..e2f8eee2e --- /dev/null +++ b/tests/hand_written/safe_alignment.rs @@ -0,0 +1,308 @@ +//! Tests for the `safe` and `unsafe` overflow-position keywords on align-* and justify-*. +//! +//! Spec: +//! +//! When the alignment subject overflows the alignment container, a `safe` value behaves +//! as logical `start`; an `unsafe` value (the default) keeps its requested position even +//! when that causes data loss at the start edge. + +use taffy::prelude::*; +use taffy::style::Direction; +use taffy_test_helpers::new_test_tree; + +#[cfg(feature = "grid")] +#[test] +fn grid_safe_align_self_falls_back_to_start_on_overflow() { + let mut taffy = new_test_tree(); + let child = taffy + .new_leaf(Style { + size: Size { width: length(50.0), height: length(150.0) }, + align_self: Some(AlignSelf::SafeEnd), + ..Default::default() + }) + .unwrap(); + let root = taffy + .new_with_children( + Style { + display: Display::Grid, + size: Size { width: length(100.0), height: length(100.0) }, + grid_template_rows: vec![length(100.0)], + grid_template_columns: vec![length(100.0)], + ..Default::default() + }, + &[child], + ) + .unwrap(); + + taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(); + + // Child overflows the 100-tall grid area, so safe end falls back to start (y == 0). + assert_eq!(taffy.layout(child).unwrap().location.y, 0.0); +} + +#[cfg(feature = "grid")] +#[test] +fn grid_safe_align_self_behaves_as_end_when_no_overflow() { + let mut taffy = new_test_tree(); + let child = taffy + .new_leaf(Style { + size: Size { width: length(50.0), height: length(40.0) }, + align_self: Some(AlignSelf::SafeEnd), + ..Default::default() + }) + .unwrap(); + let root = taffy + .new_with_children( + Style { + display: Display::Grid, + size: Size { width: length(100.0), height: length(100.0) }, + grid_template_rows: vec![length(100.0)], + grid_template_columns: vec![length(100.0)], + ..Default::default() + }, + &[child], + ) + .unwrap(); + + taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(); + + // No overflow, so safe end behaves as end: y == 100 - 40 == 60 + assert_eq!(taffy.layout(child).unwrap().location.y, 60.0); +} + +#[cfg(feature = "grid")] +#[test] +fn grid_unsafe_align_self_keeps_overflowing_position() { + let mut taffy = new_test_tree(); + let child = taffy + .new_leaf(Style { + size: Size { width: length(50.0), height: length(150.0) }, + align_self: Some(AlignSelf::End), + ..Default::default() + }) + .unwrap(); + let root = taffy + .new_with_children( + Style { + display: Display::Grid, + size: Size { width: length(100.0), height: length(100.0) }, + grid_template_rows: vec![length(100.0)], + grid_template_columns: vec![length(100.0)], + ..Default::default() + }, + &[child], + ) + .unwrap(); + + taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(); + + // Plain End with overflow keeps end alignment: y == 100 - 150 == -50 + assert_eq!(taffy.layout(child).unwrap().location.y, -50.0); +} + +#[cfg(feature = "grid")] +#[test] +fn grid_safe_justify_self_falls_back_to_start_on_overflow() { + let mut taffy = new_test_tree(); + let child = taffy + .new_leaf(Style { + size: Size { width: length(150.0), height: length(50.0) }, + justify_self: Some(JustifySelf::SafeEnd), + ..Default::default() + }) + .unwrap(); + let root = taffy + .new_with_children( + Style { + display: Display::Grid, + size: Size { width: length(100.0), height: length(100.0) }, + grid_template_rows: vec![length(100.0)], + grid_template_columns: vec![length(100.0)], + ..Default::default() + }, + &[child], + ) + .unwrap(); + + taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(); + + assert_eq!(taffy.layout(child).unwrap().location.x, 0.0); +} + +#[cfg(feature = "grid")] +#[test] +fn grid_safe_justify_self_rtl_falls_back_to_rtl_start_edge() { + let mut taffy = new_test_tree(); + let child = taffy + .new_leaf(Style { + size: Size { width: length(150.0), height: length(50.0) }, + justify_self: Some(JustifySelf::SafeEnd), + ..Default::default() + }) + .unwrap(); + let root = taffy + .new_with_children( + Style { + display: Display::Grid, + direction: Direction::Rtl, + size: Size { width: length(100.0), height: length(100.0) }, + grid_template_rows: vec![length(100.0)], + grid_template_columns: vec![length(100.0)], + ..Default::default() + }, + &[child], + ) + .unwrap(); + + taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(); + + // In RTL the inline-start edge is the right side. The 150-wide child in the + // 100-wide container, anchored at the RTL Start edge, sits at x == -50. + assert_eq!(taffy.layout(child).unwrap().location.x, -50.0); +} + +#[cfg(feature = "grid")] +#[test] +fn grid_safe_align_content_overflow_falls_back_to_start() { + let mut taffy = new_test_tree(); + let child_a = taffy + .new_leaf(Style { size: Size { width: length(40.0), height: length(80.0) }, ..Default::default() }) + .unwrap(); + let child_b = taffy + .new_leaf(Style { size: Size { width: length(40.0), height: length(80.0) }, ..Default::default() }) + .unwrap(); + let root = taffy + .new_with_children( + Style { + display: Display::Grid, + size: Size { width: length(100.0), height: length(100.0) }, + grid_template_rows: vec![length(80.0), length(80.0)], + grid_template_columns: vec![length(40.0)], + align_content: Some(AlignContent::SafeEnd), + ..Default::default() + }, + &[child_a, child_b], + ) + .unwrap(); + + taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(); + + // Tracks total 160 in a 100-tall container — overflow. Safe end falls back to + // start: first track at y == 0. + assert_eq!(taffy.layout(child_a).unwrap().location.y, 0.0); +} + +#[cfg(feature = "flexbox")] +#[test] +fn flex_safe_align_self_falls_back_to_start_on_overflow() { + let mut taffy = new_test_tree(); + let child = taffy + .new_leaf(Style { + size: Size { width: length(50.0), height: length(150.0) }, + align_self: Some(AlignSelf::SafeEnd), + ..Default::default() + }) + .unwrap(); + let root = taffy + .new_with_children( + Style { + display: Display::Flex, + size: Size { width: length(100.0), height: length(100.0) }, + ..Default::default() + }, + &[child], + ) + .unwrap(); + + taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(); + + // Cross-axis overflow: safe end falls back to start (y == 0). + assert_eq!(taffy.layout(child).unwrap().location.y, 0.0); +} + +#[cfg(feature = "flexbox")] +#[test] +fn flex_unsafe_align_self_keeps_overflowing_position() { + let mut taffy = new_test_tree(); + let child = taffy + .new_leaf(Style { + size: Size { width: length(50.0), height: length(150.0) }, + align_self: Some(AlignSelf::End), + ..Default::default() + }) + .unwrap(); + let root = taffy + .new_with_children( + Style { + display: Display::Flex, + size: Size { width: length(100.0), height: length(100.0) }, + ..Default::default() + }, + &[child], + ) + .unwrap(); + + taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(); + + // Plain End with cross-axis overflow keeps end alignment: y == 100 - 150 == -50 + assert_eq!(taffy.layout(child).unwrap().location.y, -50.0); +} + +#[cfg(feature = "flexbox")] +#[test] +fn flex_safe_justify_content_falls_back_to_start_on_overflow() { + let mut taffy = new_test_tree(); + let child_a = taffy + .new_leaf(Style { size: Size { width: length(80.0), height: length(50.0) }, ..Default::default() }) + .unwrap(); + let child_b = taffy + .new_leaf(Style { size: Size { width: length(80.0), height: length(50.0) }, ..Default::default() }) + .unwrap(); + let root = taffy + .new_with_children( + Style { + display: Display::Flex, + size: Size { width: length(100.0), height: length(100.0) }, + justify_content: Some(JustifyContent::SafeEnd), + ..Default::default() + }, + &[child_a, child_b], + ) + .unwrap(); + + taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(); + + // Items total 160 in a 100-wide container — overflow. Safe end falls back to + // start: first item anchored at x == 0. + assert_eq!(taffy.layout(child_a).unwrap().location.x, 0.0); +} + +#[cfg(feature = "flexbox")] +#[test] +fn flex_safe_align_content_falls_back_to_start_on_multiline_overflow() { + let mut taffy = new_test_tree(); + let child_a = taffy + .new_leaf(Style { size: Size { width: length(60.0), height: length(80.0) }, ..Default::default() }) + .unwrap(); + let child_b = taffy + .new_leaf(Style { size: Size { width: length(60.0), height: length(80.0) }, ..Default::default() }) + .unwrap(); + let root = taffy + .new_with_children( + Style { + display: Display::Flex, + flex_wrap: FlexWrap::Wrap, + size: Size { width: length(100.0), height: length(100.0) }, + align_content: Some(AlignContent::SafeEnd), + ..Default::default() + }, + &[child_a, child_b], + ) + .unwrap(); + + taffy.compute_layout(root, Size::MAX_CONTENT).unwrap(); + + // Two 80-tall lines wrap and overflow the 100-tall container. Safe end falls back + // to start: first line at y == 0. + assert_eq!(taffy.layout(child_a).unwrap().location.y, 0.0); +} diff --git a/tests/xml/flex/flex_safe_align_content_end_overflow__border_box_ltr.xml b/tests/xml/flex/flex_safe_align_content_end_overflow__border_box_ltr.xml new file mode 100644 index 000000000..fb329887a --- /dev/null +++ b/tests/xml/flex/flex_safe_align_content_end_overflow__border_box_ltr.xml @@ -0,0 +1,15 @@ + + + +
+
+
+
+ + + + + + + + diff --git a/tests/xml/flex/flex_safe_align_content_end_overflow__border_box_rtl.xml b/tests/xml/flex/flex_safe_align_content_end_overflow__border_box_rtl.xml new file mode 100644 index 000000000..ce2530ac4 --- /dev/null +++ b/tests/xml/flex/flex_safe_align_content_end_overflow__border_box_rtl.xml @@ -0,0 +1,15 @@ + + + +
+
+
+
+ + + + + + + + diff --git a/tests/xml/flex/flex_safe_align_content_end_overflow__content_box_ltr.xml b/tests/xml/flex/flex_safe_align_content_end_overflow__content_box_ltr.xml new file mode 100644 index 000000000..a5eb19215 --- /dev/null +++ b/tests/xml/flex/flex_safe_align_content_end_overflow__content_box_ltr.xml @@ -0,0 +1,15 @@ + + + +
+
+
+
+ + + + + + + + diff --git a/tests/xml/flex/flex_safe_align_content_end_overflow__content_box_rtl.xml b/tests/xml/flex/flex_safe_align_content_end_overflow__content_box_rtl.xml new file mode 100644 index 000000000..f933ba0eb --- /dev/null +++ b/tests/xml/flex/flex_safe_align_content_end_overflow__content_box_rtl.xml @@ -0,0 +1,15 @@ + + + +
+
+
+
+ + + + + + + + diff --git a/tests/xml/flex/flex_safe_align_self_center_overflow__border_box_ltr.xml b/tests/xml/flex/flex_safe_align_self_center_overflow__border_box_ltr.xml new file mode 100644 index 000000000..26cacd394 --- /dev/null +++ b/tests/xml/flex/flex_safe_align_self_center_overflow__border_box_ltr.xml @@ -0,0 +1,13 @@ + + + +
+
+
+ + + + + + + diff --git a/tests/xml/flex/flex_safe_align_self_center_overflow__border_box_rtl.xml b/tests/xml/flex/flex_safe_align_self_center_overflow__border_box_rtl.xml new file mode 100644 index 000000000..36640778b --- /dev/null +++ b/tests/xml/flex/flex_safe_align_self_center_overflow__border_box_rtl.xml @@ -0,0 +1,13 @@ + + + +
+
+
+ + + + + + + diff --git a/tests/xml/flex/flex_safe_align_self_center_overflow__content_box_ltr.xml b/tests/xml/flex/flex_safe_align_self_center_overflow__content_box_ltr.xml new file mode 100644 index 000000000..f9d72dc64 --- /dev/null +++ b/tests/xml/flex/flex_safe_align_self_center_overflow__content_box_ltr.xml @@ -0,0 +1,13 @@ + + + +
+
+
+ + + + + + + diff --git a/tests/xml/flex/flex_safe_align_self_center_overflow__content_box_rtl.xml b/tests/xml/flex/flex_safe_align_self_center_overflow__content_box_rtl.xml new file mode 100644 index 000000000..116da54df --- /dev/null +++ b/tests/xml/flex/flex_safe_align_self_center_overflow__content_box_rtl.xml @@ -0,0 +1,13 @@ + + + +
+
+
+ + + + + + + diff --git a/tests/xml/flex/flex_safe_align_self_end_overflow__border_box_ltr.xml b/tests/xml/flex/flex_safe_align_self_end_overflow__border_box_ltr.xml new file mode 100644 index 000000000..c04b63d02 --- /dev/null +++ b/tests/xml/flex/flex_safe_align_self_end_overflow__border_box_ltr.xml @@ -0,0 +1,13 @@ + + + +
+
+
+ + + + + + + diff --git a/tests/xml/flex/flex_safe_align_self_end_overflow__border_box_rtl.xml b/tests/xml/flex/flex_safe_align_self_end_overflow__border_box_rtl.xml new file mode 100644 index 000000000..5b25290e3 --- /dev/null +++ b/tests/xml/flex/flex_safe_align_self_end_overflow__border_box_rtl.xml @@ -0,0 +1,13 @@ + + + +
+
+
+ + + + + + + diff --git a/tests/xml/flex/flex_safe_align_self_end_overflow__content_box_ltr.xml b/tests/xml/flex/flex_safe_align_self_end_overflow__content_box_ltr.xml new file mode 100644 index 000000000..ce3486a7e --- /dev/null +++ b/tests/xml/flex/flex_safe_align_self_end_overflow__content_box_ltr.xml @@ -0,0 +1,13 @@ + + + +
+
+
+ + + + + + + diff --git a/tests/xml/flex/flex_safe_align_self_end_overflow__content_box_rtl.xml b/tests/xml/flex/flex_safe_align_self_end_overflow__content_box_rtl.xml new file mode 100644 index 000000000..8fcd2adb3 --- /dev/null +++ b/tests/xml/flex/flex_safe_align_self_end_overflow__content_box_rtl.xml @@ -0,0 +1,13 @@ + + + +
+
+
+ + + + + + + diff --git a/tests/xml/flex/flex_safe_justify_content_center_overflow__border_box_ltr.xml b/tests/xml/flex/flex_safe_justify_content_center_overflow__border_box_ltr.xml new file mode 100644 index 000000000..30a2a03ee --- /dev/null +++ b/tests/xml/flex/flex_safe_justify_content_center_overflow__border_box_ltr.xml @@ -0,0 +1,15 @@ + + + +
+
+
+
+ + + + + + + + diff --git a/tests/xml/flex/flex_safe_justify_content_center_overflow__border_box_rtl.xml b/tests/xml/flex/flex_safe_justify_content_center_overflow__border_box_rtl.xml new file mode 100644 index 000000000..91c0444ee --- /dev/null +++ b/tests/xml/flex/flex_safe_justify_content_center_overflow__border_box_rtl.xml @@ -0,0 +1,15 @@ + + + +
+
+
+
+ + + + + + + + diff --git a/tests/xml/flex/flex_safe_justify_content_center_overflow__content_box_ltr.xml b/tests/xml/flex/flex_safe_justify_content_center_overflow__content_box_ltr.xml new file mode 100644 index 000000000..8f11509bc --- /dev/null +++ b/tests/xml/flex/flex_safe_justify_content_center_overflow__content_box_ltr.xml @@ -0,0 +1,15 @@ + + + +
+
+
+
+ + + + + + + + diff --git a/tests/xml/flex/flex_safe_justify_content_center_overflow__content_box_rtl.xml b/tests/xml/flex/flex_safe_justify_content_center_overflow__content_box_rtl.xml new file mode 100644 index 000000000..a8dea362a --- /dev/null +++ b/tests/xml/flex/flex_safe_justify_content_center_overflow__content_box_rtl.xml @@ -0,0 +1,15 @@ + + + +
+
+
+
+ + + + + + + + diff --git a/tests/xml/flex/flex_safe_justify_content_end_overflow__border_box_ltr.xml b/tests/xml/flex/flex_safe_justify_content_end_overflow__border_box_ltr.xml new file mode 100644 index 000000000..a71966232 --- /dev/null +++ b/tests/xml/flex/flex_safe_justify_content_end_overflow__border_box_ltr.xml @@ -0,0 +1,15 @@ + + + +
+
+
+
+ + + + + + + + diff --git a/tests/xml/flex/flex_safe_justify_content_end_overflow__border_box_rtl.xml b/tests/xml/flex/flex_safe_justify_content_end_overflow__border_box_rtl.xml new file mode 100644 index 000000000..030d874a7 --- /dev/null +++ b/tests/xml/flex/flex_safe_justify_content_end_overflow__border_box_rtl.xml @@ -0,0 +1,15 @@ + + + +
+
+
+
+ + + + + + + + diff --git a/tests/xml/flex/flex_safe_justify_content_end_overflow__content_box_ltr.xml b/tests/xml/flex/flex_safe_justify_content_end_overflow__content_box_ltr.xml new file mode 100644 index 000000000..3b9692937 --- /dev/null +++ b/tests/xml/flex/flex_safe_justify_content_end_overflow__content_box_ltr.xml @@ -0,0 +1,15 @@ + + + +
+
+
+
+ + + + + + + + diff --git a/tests/xml/flex/flex_safe_justify_content_end_overflow__content_box_rtl.xml b/tests/xml/flex/flex_safe_justify_content_end_overflow__content_box_rtl.xml new file mode 100644 index 000000000..df19db2f5 --- /dev/null +++ b/tests/xml/flex/flex_safe_justify_content_end_overflow__content_box_rtl.xml @@ -0,0 +1,15 @@ + + + +
+
+
+
+ + + + + + + + diff --git a/tests/xml/flex/flex_unsafe_align_self_end_overflow__border_box_ltr.xml b/tests/xml/flex/flex_unsafe_align_self_end_overflow__border_box_ltr.xml new file mode 100644 index 000000000..3569f08c6 --- /dev/null +++ b/tests/xml/flex/flex_unsafe_align_self_end_overflow__border_box_ltr.xml @@ -0,0 +1,13 @@ + + + +
+
+
+ + + + + + + diff --git a/tests/xml/flex/flex_unsafe_align_self_end_overflow__border_box_rtl.xml b/tests/xml/flex/flex_unsafe_align_self_end_overflow__border_box_rtl.xml new file mode 100644 index 000000000..096058c38 --- /dev/null +++ b/tests/xml/flex/flex_unsafe_align_self_end_overflow__border_box_rtl.xml @@ -0,0 +1,13 @@ + + + +
+
+
+ + + + + + + diff --git a/tests/xml/flex/flex_unsafe_align_self_end_overflow__content_box_ltr.xml b/tests/xml/flex/flex_unsafe_align_self_end_overflow__content_box_ltr.xml new file mode 100644 index 000000000..b49dcd646 --- /dev/null +++ b/tests/xml/flex/flex_unsafe_align_self_end_overflow__content_box_ltr.xml @@ -0,0 +1,13 @@ + + + +
+
+
+ + + + + + + diff --git a/tests/xml/flex/flex_unsafe_align_self_end_overflow__content_box_rtl.xml b/tests/xml/flex/flex_unsafe_align_self_end_overflow__content_box_rtl.xml new file mode 100644 index 000000000..ff0c791c8 --- /dev/null +++ b/tests/xml/flex/flex_unsafe_align_self_end_overflow__content_box_rtl.xml @@ -0,0 +1,13 @@ + + + +
+
+
+ + + + + + + diff --git a/tests/xml/grid/grid_safe_align_content_center_overflow__border_box_ltr.xml b/tests/xml/grid/grid_safe_align_content_center_overflow__border_box_ltr.xml new file mode 100644 index 000000000..d9d8cfb95 --- /dev/null +++ b/tests/xml/grid/grid_safe_align_content_center_overflow__border_box_ltr.xml @@ -0,0 +1,29 @@ + + + +
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + diff --git a/tests/xml/grid/grid_safe_align_content_center_overflow__border_box_rtl.xml b/tests/xml/grid/grid_safe_align_content_center_overflow__border_box_rtl.xml new file mode 100644 index 000000000..dc10ed5a8 --- /dev/null +++ b/tests/xml/grid/grid_safe_align_content_center_overflow__border_box_rtl.xml @@ -0,0 +1,29 @@ + + + +
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + diff --git a/tests/xml/grid/grid_safe_align_content_center_overflow__content_box_ltr.xml b/tests/xml/grid/grid_safe_align_content_center_overflow__content_box_ltr.xml new file mode 100644 index 000000000..bbb45a5f2 --- /dev/null +++ b/tests/xml/grid/grid_safe_align_content_center_overflow__content_box_ltr.xml @@ -0,0 +1,29 @@ + + + +
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + diff --git a/tests/xml/grid/grid_safe_align_content_center_overflow__content_box_rtl.xml b/tests/xml/grid/grid_safe_align_content_center_overflow__content_box_rtl.xml new file mode 100644 index 000000000..175eb0dd4 --- /dev/null +++ b/tests/xml/grid/grid_safe_align_content_center_overflow__content_box_rtl.xml @@ -0,0 +1,29 @@ + + + +
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + diff --git a/tests/xml/grid/grid_safe_align_content_end_overflow__border_box_ltr.xml b/tests/xml/grid/grid_safe_align_content_end_overflow__border_box_ltr.xml new file mode 100644 index 000000000..e4736ffcb --- /dev/null +++ b/tests/xml/grid/grid_safe_align_content_end_overflow__border_box_ltr.xml @@ -0,0 +1,29 @@ + + + +
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + diff --git a/tests/xml/grid/grid_safe_align_content_end_overflow__border_box_rtl.xml b/tests/xml/grid/grid_safe_align_content_end_overflow__border_box_rtl.xml new file mode 100644 index 000000000..201ebadb9 --- /dev/null +++ b/tests/xml/grid/grid_safe_align_content_end_overflow__border_box_rtl.xml @@ -0,0 +1,29 @@ + + + +
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + diff --git a/tests/xml/grid/grid_safe_align_content_end_overflow__content_box_ltr.xml b/tests/xml/grid/grid_safe_align_content_end_overflow__content_box_ltr.xml new file mode 100644 index 000000000..1cce5c2e1 --- /dev/null +++ b/tests/xml/grid/grid_safe_align_content_end_overflow__content_box_ltr.xml @@ -0,0 +1,29 @@ + + + +
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + diff --git a/tests/xml/grid/grid_safe_align_content_end_overflow__content_box_rtl.xml b/tests/xml/grid/grid_safe_align_content_end_overflow__content_box_rtl.xml new file mode 100644 index 000000000..2528fc729 --- /dev/null +++ b/tests/xml/grid/grid_safe_align_content_end_overflow__content_box_rtl.xml @@ -0,0 +1,29 @@ + + + +
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + diff --git a/tests/xml/grid/grid_safe_align_self_end_no_overflow__border_box_ltr.xml b/tests/xml/grid/grid_safe_align_self_end_no_overflow__border_box_ltr.xml new file mode 100644 index 000000000..c6304d7f5 --- /dev/null +++ b/tests/xml/grid/grid_safe_align_self_end_no_overflow__border_box_ltr.xml @@ -0,0 +1,13 @@ + + + +
+
+
+ + + + + + + diff --git a/tests/xml/grid/grid_safe_align_self_end_no_overflow__border_box_rtl.xml b/tests/xml/grid/grid_safe_align_self_end_no_overflow__border_box_rtl.xml new file mode 100644 index 000000000..bb83b019f --- /dev/null +++ b/tests/xml/grid/grid_safe_align_self_end_no_overflow__border_box_rtl.xml @@ -0,0 +1,13 @@ + + + +
+
+
+ + + + + + + diff --git a/tests/xml/grid/grid_safe_align_self_end_no_overflow__content_box_ltr.xml b/tests/xml/grid/grid_safe_align_self_end_no_overflow__content_box_ltr.xml new file mode 100644 index 000000000..ec320b7e2 --- /dev/null +++ b/tests/xml/grid/grid_safe_align_self_end_no_overflow__content_box_ltr.xml @@ -0,0 +1,13 @@ + + + +
+
+
+ + + + + + + diff --git a/tests/xml/grid/grid_safe_align_self_end_no_overflow__content_box_rtl.xml b/tests/xml/grid/grid_safe_align_self_end_no_overflow__content_box_rtl.xml new file mode 100644 index 000000000..3c7a0254a --- /dev/null +++ b/tests/xml/grid/grid_safe_align_self_end_no_overflow__content_box_rtl.xml @@ -0,0 +1,13 @@ + + + +
+
+
+ + + + + + + diff --git a/tests/xml/grid/grid_safe_align_self_end_overflow__border_box_ltr.xml b/tests/xml/grid/grid_safe_align_self_end_overflow__border_box_ltr.xml new file mode 100644 index 000000000..6d44a60b8 --- /dev/null +++ b/tests/xml/grid/grid_safe_align_self_end_overflow__border_box_ltr.xml @@ -0,0 +1,13 @@ + + + +
+
+
+ + + + + + + diff --git a/tests/xml/grid/grid_safe_align_self_end_overflow__border_box_rtl.xml b/tests/xml/grid/grid_safe_align_self_end_overflow__border_box_rtl.xml new file mode 100644 index 000000000..b584b9ab3 --- /dev/null +++ b/tests/xml/grid/grid_safe_align_self_end_overflow__border_box_rtl.xml @@ -0,0 +1,13 @@ + + + +
+
+
+ + + + + + + diff --git a/tests/xml/grid/grid_safe_align_self_end_overflow__content_box_ltr.xml b/tests/xml/grid/grid_safe_align_self_end_overflow__content_box_ltr.xml new file mode 100644 index 000000000..bc76d59ba --- /dev/null +++ b/tests/xml/grid/grid_safe_align_self_end_overflow__content_box_ltr.xml @@ -0,0 +1,13 @@ + + + +
+
+
+ + + + + + + diff --git a/tests/xml/grid/grid_safe_align_self_end_overflow__content_box_rtl.xml b/tests/xml/grid/grid_safe_align_self_end_overflow__content_box_rtl.xml new file mode 100644 index 000000000..48a30ec0b --- /dev/null +++ b/tests/xml/grid/grid_safe_align_self_end_overflow__content_box_rtl.xml @@ -0,0 +1,13 @@ + + + +
+
+
+ + + + + + + diff --git a/tests/xml/grid/grid_safe_justify_content_center_overflow__border_box_ltr.xml b/tests/xml/grid/grid_safe_justify_content_center_overflow__border_box_ltr.xml new file mode 100644 index 000000000..841e7570a --- /dev/null +++ b/tests/xml/grid/grid_safe_justify_content_center_overflow__border_box_ltr.xml @@ -0,0 +1,29 @@ + + + +
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + diff --git a/tests/xml/grid/grid_safe_justify_content_center_overflow__border_box_rtl.xml b/tests/xml/grid/grid_safe_justify_content_center_overflow__border_box_rtl.xml new file mode 100644 index 000000000..aa0f91be7 --- /dev/null +++ b/tests/xml/grid/grid_safe_justify_content_center_overflow__border_box_rtl.xml @@ -0,0 +1,29 @@ + + + +
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + diff --git a/tests/xml/grid/grid_safe_justify_content_center_overflow__content_box_ltr.xml b/tests/xml/grid/grid_safe_justify_content_center_overflow__content_box_ltr.xml new file mode 100644 index 000000000..3455e3e8b --- /dev/null +++ b/tests/xml/grid/grid_safe_justify_content_center_overflow__content_box_ltr.xml @@ -0,0 +1,29 @@ + + + +
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + diff --git a/tests/xml/grid/grid_safe_justify_content_center_overflow__content_box_rtl.xml b/tests/xml/grid/grid_safe_justify_content_center_overflow__content_box_rtl.xml new file mode 100644 index 000000000..bd40f7920 --- /dev/null +++ b/tests/xml/grid/grid_safe_justify_content_center_overflow__content_box_rtl.xml @@ -0,0 +1,29 @@ + + + +
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + diff --git a/tests/xml/grid/grid_safe_justify_content_end_no_overflow__border_box_ltr.xml b/tests/xml/grid/grid_safe_justify_content_end_no_overflow__border_box_ltr.xml new file mode 100644 index 000000000..85a7c6e30 --- /dev/null +++ b/tests/xml/grid/grid_safe_justify_content_end_no_overflow__border_box_ltr.xml @@ -0,0 +1,29 @@ + + + +
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + diff --git a/tests/xml/grid/grid_safe_justify_content_end_no_overflow__border_box_rtl.xml b/tests/xml/grid/grid_safe_justify_content_end_no_overflow__border_box_rtl.xml new file mode 100644 index 000000000..3267e393a --- /dev/null +++ b/tests/xml/grid/grid_safe_justify_content_end_no_overflow__border_box_rtl.xml @@ -0,0 +1,29 @@ + + + +
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + diff --git a/tests/xml/grid/grid_safe_justify_content_end_no_overflow__content_box_ltr.xml b/tests/xml/grid/grid_safe_justify_content_end_no_overflow__content_box_ltr.xml new file mode 100644 index 000000000..9c1663fc7 --- /dev/null +++ b/tests/xml/grid/grid_safe_justify_content_end_no_overflow__content_box_ltr.xml @@ -0,0 +1,29 @@ + + + +
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + diff --git a/tests/xml/grid/grid_safe_justify_content_end_no_overflow__content_box_rtl.xml b/tests/xml/grid/grid_safe_justify_content_end_no_overflow__content_box_rtl.xml new file mode 100644 index 000000000..dee1ccccc --- /dev/null +++ b/tests/xml/grid/grid_safe_justify_content_end_no_overflow__content_box_rtl.xml @@ -0,0 +1,29 @@ + + + +
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + diff --git a/tests/xml/grid/grid_safe_justify_content_end_overflow__border_box_ltr.xml b/tests/xml/grid/grid_safe_justify_content_end_overflow__border_box_ltr.xml new file mode 100644 index 000000000..764a1fa53 --- /dev/null +++ b/tests/xml/grid/grid_safe_justify_content_end_overflow__border_box_ltr.xml @@ -0,0 +1,29 @@ + + + +
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + diff --git a/tests/xml/grid/grid_safe_justify_content_end_overflow__border_box_rtl.xml b/tests/xml/grid/grid_safe_justify_content_end_overflow__border_box_rtl.xml new file mode 100644 index 000000000..194a8d55d --- /dev/null +++ b/tests/xml/grid/grid_safe_justify_content_end_overflow__border_box_rtl.xml @@ -0,0 +1,29 @@ + + + +
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + diff --git a/tests/xml/grid/grid_safe_justify_content_end_overflow__content_box_ltr.xml b/tests/xml/grid/grid_safe_justify_content_end_overflow__content_box_ltr.xml new file mode 100644 index 000000000..2b7996cb0 --- /dev/null +++ b/tests/xml/grid/grid_safe_justify_content_end_overflow__content_box_ltr.xml @@ -0,0 +1,29 @@ + + + +
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + diff --git a/tests/xml/grid/grid_safe_justify_content_end_overflow__content_box_rtl.xml b/tests/xml/grid/grid_safe_justify_content_end_overflow__content_box_rtl.xml new file mode 100644 index 000000000..2982c1e6d --- /dev/null +++ b/tests/xml/grid/grid_safe_justify_content_end_overflow__content_box_rtl.xml @@ -0,0 +1,29 @@ + + + +
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + diff --git a/tests/xml/grid/grid_safe_justify_self_center_overflow__border_box_ltr.xml b/tests/xml/grid/grid_safe_justify_self_center_overflow__border_box_ltr.xml new file mode 100644 index 000000000..741ddf524 --- /dev/null +++ b/tests/xml/grid/grid_safe_justify_self_center_overflow__border_box_ltr.xml @@ -0,0 +1,13 @@ + + + +
+
+
+ + + + + + + diff --git a/tests/xml/grid/grid_safe_justify_self_center_overflow__border_box_rtl.xml b/tests/xml/grid/grid_safe_justify_self_center_overflow__border_box_rtl.xml new file mode 100644 index 000000000..f5021787b --- /dev/null +++ b/tests/xml/grid/grid_safe_justify_self_center_overflow__border_box_rtl.xml @@ -0,0 +1,13 @@ + + + +
+
+
+ + + + + + + diff --git a/tests/xml/grid/grid_safe_justify_self_center_overflow__content_box_ltr.xml b/tests/xml/grid/grid_safe_justify_self_center_overflow__content_box_ltr.xml new file mode 100644 index 000000000..32cecc176 --- /dev/null +++ b/tests/xml/grid/grid_safe_justify_self_center_overflow__content_box_ltr.xml @@ -0,0 +1,13 @@ + + + +
+
+
+ + + + + + + diff --git a/tests/xml/grid/grid_safe_justify_self_center_overflow__content_box_rtl.xml b/tests/xml/grid/grid_safe_justify_self_center_overflow__content_box_rtl.xml new file mode 100644 index 000000000..3ddc3bf73 --- /dev/null +++ b/tests/xml/grid/grid_safe_justify_self_center_overflow__content_box_rtl.xml @@ -0,0 +1,13 @@ + + + +
+
+
+ + + + + + + diff --git a/tests/xml/grid/grid_safe_justify_self_end_overflow__border_box_ltr.xml b/tests/xml/grid/grid_safe_justify_self_end_overflow__border_box_ltr.xml new file mode 100644 index 000000000..f1dae8910 --- /dev/null +++ b/tests/xml/grid/grid_safe_justify_self_end_overflow__border_box_ltr.xml @@ -0,0 +1,13 @@ + + + +
+
+
+ + + + + + + diff --git a/tests/xml/grid/grid_safe_justify_self_end_overflow__border_box_rtl.xml b/tests/xml/grid/grid_safe_justify_self_end_overflow__border_box_rtl.xml new file mode 100644 index 000000000..e5c5ade2f --- /dev/null +++ b/tests/xml/grid/grid_safe_justify_self_end_overflow__border_box_rtl.xml @@ -0,0 +1,13 @@ + + + +
+
+
+ + + + + + + diff --git a/tests/xml/grid/grid_safe_justify_self_end_overflow__content_box_ltr.xml b/tests/xml/grid/grid_safe_justify_self_end_overflow__content_box_ltr.xml new file mode 100644 index 000000000..79b1c90d3 --- /dev/null +++ b/tests/xml/grid/grid_safe_justify_self_end_overflow__content_box_ltr.xml @@ -0,0 +1,13 @@ + + + +
+
+
+ + + + + + + diff --git a/tests/xml/grid/grid_safe_justify_self_end_overflow__content_box_rtl.xml b/tests/xml/grid/grid_safe_justify_self_end_overflow__content_box_rtl.xml new file mode 100644 index 000000000..cf2f031b5 --- /dev/null +++ b/tests/xml/grid/grid_safe_justify_self_end_overflow__content_box_rtl.xml @@ -0,0 +1,13 @@ + + + +
+
+
+ + + + + + + diff --git a/tests/xml/grid/grid_unsafe_align_self_end_overflow__border_box_ltr.xml b/tests/xml/grid/grid_unsafe_align_self_end_overflow__border_box_ltr.xml new file mode 100644 index 000000000..cc6ad04fa --- /dev/null +++ b/tests/xml/grid/grid_unsafe_align_self_end_overflow__border_box_ltr.xml @@ -0,0 +1,13 @@ + + + +
+
+
+ + + + + + + diff --git a/tests/xml/grid/grid_unsafe_align_self_end_overflow__border_box_rtl.xml b/tests/xml/grid/grid_unsafe_align_self_end_overflow__border_box_rtl.xml new file mode 100644 index 000000000..b2969b5c1 --- /dev/null +++ b/tests/xml/grid/grid_unsafe_align_self_end_overflow__border_box_rtl.xml @@ -0,0 +1,13 @@ + + + +
+
+
+ + + + + + + diff --git a/tests/xml/grid/grid_unsafe_align_self_end_overflow__content_box_ltr.xml b/tests/xml/grid/grid_unsafe_align_self_end_overflow__content_box_ltr.xml new file mode 100644 index 000000000..30f43072c --- /dev/null +++ b/tests/xml/grid/grid_unsafe_align_self_end_overflow__content_box_ltr.xml @@ -0,0 +1,13 @@ + + + +
+
+
+ + + + + + + diff --git a/tests/xml/grid/grid_unsafe_align_self_end_overflow__content_box_rtl.xml b/tests/xml/grid/grid_unsafe_align_self_end_overflow__content_box_rtl.xml new file mode 100644 index 000000000..9636d6534 --- /dev/null +++ b/tests/xml/grid/grid_unsafe_align_self_end_overflow__content_box_rtl.xml @@ -0,0 +1,13 @@ + + + +
+
+
+ + + + + + + diff --git a/tests/xml/grid/grid_unsafe_justify_content_end_overflow__border_box_ltr.xml b/tests/xml/grid/grid_unsafe_justify_content_end_overflow__border_box_ltr.xml new file mode 100644 index 000000000..40a5376c6 --- /dev/null +++ b/tests/xml/grid/grid_unsafe_justify_content_end_overflow__border_box_ltr.xml @@ -0,0 +1,29 @@ + + + +
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + diff --git a/tests/xml/grid/grid_unsafe_justify_content_end_overflow__border_box_rtl.xml b/tests/xml/grid/grid_unsafe_justify_content_end_overflow__border_box_rtl.xml new file mode 100644 index 000000000..89697fdd4 --- /dev/null +++ b/tests/xml/grid/grid_unsafe_justify_content_end_overflow__border_box_rtl.xml @@ -0,0 +1,29 @@ + + + +
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + diff --git a/tests/xml/grid/grid_unsafe_justify_content_end_overflow__content_box_ltr.xml b/tests/xml/grid/grid_unsafe_justify_content_end_overflow__content_box_ltr.xml new file mode 100644 index 000000000..520a239dc --- /dev/null +++ b/tests/xml/grid/grid_unsafe_justify_content_end_overflow__content_box_ltr.xml @@ -0,0 +1,29 @@ + + + +
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + diff --git a/tests/xml/grid/grid_unsafe_justify_content_end_overflow__content_box_rtl.xml b/tests/xml/grid/grid_unsafe_justify_content_end_overflow__content_box_rtl.xml new file mode 100644 index 000000000..252f65d01 --- /dev/null +++ b/tests/xml/grid/grid_unsafe_justify_content_end_overflow__content_box_rtl.xml @@ -0,0 +1,29 @@ + + + +
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + diff --git a/tests/xml/mod.rs b/tests/xml/mod.rs index 7218ac1f7..cfe6081d9 100644 --- a/tests/xml/mod.rs +++ b/tests/xml/mod.rs @@ -10281,6 +10281,106 @@ mod flex { crate::run_xml_test("flex", "flex_row_relative_all_sides__content_box_rtl"); } + #[test] + fn flex_safe_align_content_end_overflow__border_box_ltr() { + crate::run_xml_test("flex", "flex_safe_align_content_end_overflow__border_box_ltr"); + } + + #[test] + fn flex_safe_align_content_end_overflow__content_box_ltr() { + crate::run_xml_test("flex", "flex_safe_align_content_end_overflow__content_box_ltr"); + } + + #[test] + fn flex_safe_align_content_end_overflow__border_box_rtl() { + crate::run_xml_test("flex", "flex_safe_align_content_end_overflow__border_box_rtl"); + } + + #[test] + fn flex_safe_align_content_end_overflow__content_box_rtl() { + crate::run_xml_test("flex", "flex_safe_align_content_end_overflow__content_box_rtl"); + } + + #[test] + fn flex_safe_align_self_center_overflow__border_box_ltr() { + crate::run_xml_test("flex", "flex_safe_align_self_center_overflow__border_box_ltr"); + } + + #[test] + fn flex_safe_align_self_center_overflow__content_box_ltr() { + crate::run_xml_test("flex", "flex_safe_align_self_center_overflow__content_box_ltr"); + } + + #[test] + fn flex_safe_align_self_center_overflow__border_box_rtl() { + crate::run_xml_test("flex", "flex_safe_align_self_center_overflow__border_box_rtl"); + } + + #[test] + fn flex_safe_align_self_center_overflow__content_box_rtl() { + crate::run_xml_test("flex", "flex_safe_align_self_center_overflow__content_box_rtl"); + } + + #[test] + fn flex_safe_align_self_end_overflow__border_box_ltr() { + crate::run_xml_test("flex", "flex_safe_align_self_end_overflow__border_box_ltr"); + } + + #[test] + fn flex_safe_align_self_end_overflow__content_box_ltr() { + crate::run_xml_test("flex", "flex_safe_align_self_end_overflow__content_box_ltr"); + } + + #[test] + fn flex_safe_align_self_end_overflow__border_box_rtl() { + crate::run_xml_test("flex", "flex_safe_align_self_end_overflow__border_box_rtl"); + } + + #[test] + fn flex_safe_align_self_end_overflow__content_box_rtl() { + crate::run_xml_test("flex", "flex_safe_align_self_end_overflow__content_box_rtl"); + } + + #[test] + fn flex_safe_justify_content_center_overflow__border_box_ltr() { + crate::run_xml_test("flex", "flex_safe_justify_content_center_overflow__border_box_ltr"); + } + + #[test] + fn flex_safe_justify_content_center_overflow__content_box_ltr() { + crate::run_xml_test("flex", "flex_safe_justify_content_center_overflow__content_box_ltr"); + } + + #[test] + fn flex_safe_justify_content_center_overflow__border_box_rtl() { + crate::run_xml_test("flex", "flex_safe_justify_content_center_overflow__border_box_rtl"); + } + + #[test] + fn flex_safe_justify_content_center_overflow__content_box_rtl() { + crate::run_xml_test("flex", "flex_safe_justify_content_center_overflow__content_box_rtl"); + } + + #[test] + fn flex_safe_justify_content_end_overflow__border_box_ltr() { + crate::run_xml_test("flex", "flex_safe_justify_content_end_overflow__border_box_ltr"); + } + + #[test] + fn flex_safe_justify_content_end_overflow__content_box_ltr() { + crate::run_xml_test("flex", "flex_safe_justify_content_end_overflow__content_box_ltr"); + } + + #[test] + fn flex_safe_justify_content_end_overflow__border_box_rtl() { + crate::run_xml_test("flex", "flex_safe_justify_content_end_overflow__border_box_rtl"); + } + + #[test] + fn flex_safe_justify_content_end_overflow__content_box_rtl() { + crate::run_xml_test("flex", "flex_safe_justify_content_end_overflow__content_box_rtl"); + } + #[test] fn flex_shrink_by_outer_margin_with_max_size__border_box_ltr() { crate::run_xml_test("flex", "flex_shrink_by_outer_margin_with_max_size__border_box_ltr"); @@ -10361,6 +10461,26 @@ mod flex { crate::run_xml_test("flex", "flex_shrink_to_zero__content_box_rtl"); } + #[test] + fn flex_unsafe_align_self_end_overflow__border_box_ltr() { + crate::run_xml_test("flex", "flex_unsafe_align_self_end_overflow__border_box_ltr"); + } + + #[test] + fn flex_unsafe_align_self_end_overflow__content_box_ltr() { + crate::run_xml_test("flex", "flex_unsafe_align_self_end_overflow__content_box_ltr"); + } + + #[test] + fn flex_unsafe_align_self_end_overflow__border_box_rtl() { + crate::run_xml_test("flex", "flex_unsafe_align_self_end_overflow__border_box_rtl"); + } + + #[test] + fn flex_unsafe_align_self_end_overflow__content_box_rtl() { + crate::run_xml_test("flex", "flex_unsafe_align_self_end_overflow__content_box_rtl"); + } + #[test] fn flex_wrap_align_stretch_fits_one_row__border_box_ltr() { crate::run_xml_test("flex", "flex_wrap_align_stretch_fits_one_row__border_box_ltr"); @@ -21885,6 +22005,222 @@ mod grid { crate::run_xml_test("grid", "grid_repeat_mixed__content_box_rtl"); } + #[cfg(feature = "grid")] + #[test] + fn grid_safe_align_content_center_overflow__border_box_ltr() { + crate::run_xml_test("grid", "grid_safe_align_content_center_overflow__border_box_ltr"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_safe_align_content_center_overflow__content_box_ltr() { + crate::run_xml_test("grid", "grid_safe_align_content_center_overflow__content_box_ltr"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_safe_align_content_center_overflow__border_box_rtl() { + crate::run_xml_test("grid", "grid_safe_align_content_center_overflow__border_box_rtl"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_safe_align_content_center_overflow__content_box_rtl() { + crate::run_xml_test("grid", "grid_safe_align_content_center_overflow__content_box_rtl"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_safe_align_content_end_overflow__border_box_ltr() { + crate::run_xml_test("grid", "grid_safe_align_content_end_overflow__border_box_ltr"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_safe_align_content_end_overflow__content_box_ltr() { + crate::run_xml_test("grid", "grid_safe_align_content_end_overflow__content_box_ltr"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_safe_align_content_end_overflow__border_box_rtl() { + crate::run_xml_test("grid", "grid_safe_align_content_end_overflow__border_box_rtl"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_safe_align_content_end_overflow__content_box_rtl() { + crate::run_xml_test("grid", "grid_safe_align_content_end_overflow__content_box_rtl"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_safe_align_self_end_no_overflow__border_box_ltr() { + crate::run_xml_test("grid", "grid_safe_align_self_end_no_overflow__border_box_ltr"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_safe_align_self_end_no_overflow__content_box_ltr() { + crate::run_xml_test("grid", "grid_safe_align_self_end_no_overflow__content_box_ltr"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_safe_align_self_end_no_overflow__border_box_rtl() { + crate::run_xml_test("grid", "grid_safe_align_self_end_no_overflow__border_box_rtl"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_safe_align_self_end_no_overflow__content_box_rtl() { + crate::run_xml_test("grid", "grid_safe_align_self_end_no_overflow__content_box_rtl"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_safe_align_self_end_overflow__border_box_ltr() { + crate::run_xml_test("grid", "grid_safe_align_self_end_overflow__border_box_ltr"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_safe_align_self_end_overflow__content_box_ltr() { + crate::run_xml_test("grid", "grid_safe_align_self_end_overflow__content_box_ltr"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_safe_align_self_end_overflow__border_box_rtl() { + crate::run_xml_test("grid", "grid_safe_align_self_end_overflow__border_box_rtl"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_safe_align_self_end_overflow__content_box_rtl() { + crate::run_xml_test("grid", "grid_safe_align_self_end_overflow__content_box_rtl"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_safe_justify_content_center_overflow__border_box_ltr() { + crate::run_xml_test("grid", "grid_safe_justify_content_center_overflow__border_box_ltr"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_safe_justify_content_center_overflow__content_box_ltr() { + crate::run_xml_test("grid", "grid_safe_justify_content_center_overflow__content_box_ltr"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_safe_justify_content_center_overflow__border_box_rtl() { + crate::run_xml_test("grid", "grid_safe_justify_content_center_overflow__border_box_rtl"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_safe_justify_content_center_overflow__content_box_rtl() { + crate::run_xml_test("grid", "grid_safe_justify_content_center_overflow__content_box_rtl"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_safe_justify_content_end_no_overflow__border_box_ltr() { + crate::run_xml_test("grid", "grid_safe_justify_content_end_no_overflow__border_box_ltr"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_safe_justify_content_end_no_overflow__content_box_ltr() { + crate::run_xml_test("grid", "grid_safe_justify_content_end_no_overflow__content_box_ltr"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_safe_justify_content_end_no_overflow__border_box_rtl() { + crate::run_xml_test("grid", "grid_safe_justify_content_end_no_overflow__border_box_rtl"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_safe_justify_content_end_no_overflow__content_box_rtl() { + crate::run_xml_test("grid", "grid_safe_justify_content_end_no_overflow__content_box_rtl"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_safe_justify_content_end_overflow__border_box_ltr() { + crate::run_xml_test("grid", "grid_safe_justify_content_end_overflow__border_box_ltr"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_safe_justify_content_end_overflow__content_box_ltr() { + crate::run_xml_test("grid", "grid_safe_justify_content_end_overflow__content_box_ltr"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_safe_justify_content_end_overflow__border_box_rtl() { + crate::run_xml_test("grid", "grid_safe_justify_content_end_overflow__border_box_rtl"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_safe_justify_content_end_overflow__content_box_rtl() { + crate::run_xml_test("grid", "grid_safe_justify_content_end_overflow__content_box_rtl"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_safe_justify_self_center_overflow__border_box_ltr() { + crate::run_xml_test("grid", "grid_safe_justify_self_center_overflow__border_box_ltr"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_safe_justify_self_center_overflow__content_box_ltr() { + crate::run_xml_test("grid", "grid_safe_justify_self_center_overflow__content_box_ltr"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_safe_justify_self_center_overflow__border_box_rtl() { + crate::run_xml_test("grid", "grid_safe_justify_self_center_overflow__border_box_rtl"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_safe_justify_self_center_overflow__content_box_rtl() { + crate::run_xml_test("grid", "grid_safe_justify_self_center_overflow__content_box_rtl"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_safe_justify_self_end_overflow__border_box_ltr() { + crate::run_xml_test("grid", "grid_safe_justify_self_end_overflow__border_box_ltr"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_safe_justify_self_end_overflow__content_box_ltr() { + crate::run_xml_test("grid", "grid_safe_justify_self_end_overflow__content_box_ltr"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_safe_justify_self_end_overflow__border_box_rtl() { + crate::run_xml_test("grid", "grid_safe_justify_self_end_overflow__border_box_rtl"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_safe_justify_self_end_overflow__content_box_rtl() { + crate::run_xml_test("grid", "grid_safe_justify_self_end_overflow__content_box_rtl"); + } + #[cfg(feature = "grid")] #[test] fn grid_scrollbar_rtl__border_box_ltr() { @@ -22484,6 +22820,54 @@ mod grid { fn grid_taffy_issue_624__content_box_rtl() { crate::run_xml_test("grid", "grid_taffy_issue_624__content_box_rtl"); } + + #[cfg(feature = "grid")] + #[test] + fn grid_unsafe_align_self_end_overflow__border_box_ltr() { + crate::run_xml_test("grid", "grid_unsafe_align_self_end_overflow__border_box_ltr"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_unsafe_align_self_end_overflow__content_box_ltr() { + crate::run_xml_test("grid", "grid_unsafe_align_self_end_overflow__content_box_ltr"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_unsafe_align_self_end_overflow__border_box_rtl() { + crate::run_xml_test("grid", "grid_unsafe_align_self_end_overflow__border_box_rtl"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_unsafe_align_self_end_overflow__content_box_rtl() { + crate::run_xml_test("grid", "grid_unsafe_align_self_end_overflow__content_box_rtl"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_unsafe_justify_content_end_overflow__border_box_ltr() { + crate::run_xml_test("grid", "grid_unsafe_justify_content_end_overflow__border_box_ltr"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_unsafe_justify_content_end_overflow__content_box_ltr() { + crate::run_xml_test("grid", "grid_unsafe_justify_content_end_overflow__content_box_ltr"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_unsafe_justify_content_end_overflow__border_box_rtl() { + crate::run_xml_test("grid", "grid_unsafe_justify_content_end_overflow__border_box_rtl"); + } + + #[cfg(feature = "grid")] + #[test] + fn grid_unsafe_justify_content_end_overflow__content_box_rtl() { + crate::run_xml_test("grid", "grid_unsafe_justify_content_end_overflow__content_box_rtl"); + } } mod gridflex {