From d1ff7e339b9ee35b33858779f8d7653197e93d92 Mon Sep 17 00:00:00 2001 From: Ritesh <87757827+rit3sh-x@users.noreply.github.com> Date: Thu, 28 May 2026 00:34:29 +0530 Subject: [PATCH] Implement `safe` and `unsafe` overflow-position alignment keywords (#952) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(style): add Safe* alignment variants and parser Add SafeStart/SafeEnd/SafeFlexStart/SafeFlexEnd/SafeCenter on AlignItems and AlignContent with is_safe()/position() helpers, RTL-aware reversed() extension, and a hand-rolled FromCss parser for `safe X` / `unsafe X` that rejects spec-invalid combinations. apply_alignment_fallback now folds the safe modifier into its is_safe flag, so content alignment in grid tracks and flex justify/align-content honor the Start fallback when overflowing. Self-alignment paths accept the new variants but defer the overflow fallback to a follow-up commit. * feat(compute): wire safe-overflow fallback for self alignment Implement the Start fallback for "safe" overflow-position keywords on align-self and justify-self in both grid and flex layouts. When an item overflows its alignment container and the alignment value is one of the Safe* variants, the offset is anchored to the logical Start edge to avoid data loss; an item that fits its container keeps its requested position. In grid `align_item_within_area`, overflow is detected from the resolved size and non-auto margins versus the grid area size, and the fallback respects the container `direction` so RTL anchors at the right edge. In flex `align_flex_items_along_cross_axis`, the fallback short-circuits when the cross-axis free space is negative, accounting for `cross_axis_should_reverse` and `is_wrap_reverse` so the Start edge matches the underlying writing-mode-relative start. The three previously-stale `// TODO: Implement safe alignment` comments at the grid track, flex justify-content, and flex align-content sites are removed; the modifier-folding inside `apply_alignment_fallback` already routes Safe* keywords through the Start fallback path on overflow. Add ten integration tests covering the new self-alignment behavior, the content-alignment behavior wired by the previous commit, the no-overflow case, the unsafe (default) overflow regression, the RTL fallback edge, and a multi-line flex `align-content` overflow. The flex absolutely-positioned alignment paths still strip Safe* via `.position()` without invoking the Start fallback; this is left for a follow-up commit. * test(safe-alignment): add gentest fixtures for safe and unsafe overflow alignment Add 17 HTML fixtures under test_fixtures/grid/ and test_fixtures/flex/ that exercise the `safe` and `unsafe` overflow-position keywords across container-level (justify-content, align-content) and item-level (justify-self, align-self) alignment in both grid and flex layouts. The fixtures pair overflow and no-overflow geometries so that the Start fallback path and the normal positional path are both verified, and include unsafe-end regressions to prove the default behavior is unchanged for plain end alignment. Run the gentest pipeline against these fixtures to render each in headless Chrome and capture the expected layout into 68 generated XML expectation files (4 variants per fixture: border-box LTR, border-box RTL, content-box LTR, content-box RTL). Register the new entries in tests/xml/mod.rs. The full suite passes — taffy's safe-alignment implementation matches Chrome byte-for-byte on every fixture. * docs: document safe and unsafe overflow alignment keywords Add a section to docs/style-properties.md that lists the new Safe* variants on AlignItems and AlignContent, describes the overflow fallback to logical Start, and clarifies which position keywords are valid pairings under the spec. * refactor(safe-alignment): address review feedback - Drop dead `is_safe` parameter from `apply_alignment_fallback`; infer it from the alignment style itself. - In `align_item_within_area` (grid) and `align_flex_items_along_cross_axis` (flex), mutate `alignment_style` to `Start` on safe + overflow and reuse the existing match, removing a duplicated RTL branch. - Add `Safe*` arms to `benches/yoga_helpers.rs` so the bench crate builds on CI; Yoga has no overflow-position equivalent, so they map to `unimplemented!()` like the other unsupported variants. * refactor(safe-alignment): collapse Safe* variants into struct { keyword, safety } Addresses review feedback from @nicoburns. Replaces the combinatorial Safe* variants on AlignContent / AlignItems with a struct holding a position-keyword enum and an orthogonal AlignmentSafety { Safe, Unsafe } modifier. Every pre-existing CSS spelling (Start, End, FlexStart, FlexEnd, Center, Stretch, SpaceBetween, SpaceEvenly, SpaceAround, Baseline) and every Safe* spelling (SafeStart, SafeEnd, SafeFlexStart, SafeFlexEnd, SafeCenter) is exposed as a PascalCase associated constant, so call sites read identically to the previous enum form. Compute paths take the struct at the API boundary, fold safety into the keyword via the fallback step, and then match on the bare *Keyword enum — exhaustive, with zero unreachable!() arms in src/compute/. - public API: AlignContent / AlignItems are now `pub struct { keyword, safety }` with associated consts for every prior spelling - new public types: AlignContentKeyword, AlignItemsKeyword, AlignmentSafety (added to the prelude) - `position()` accessor renamed to `keyword()` (returns the new *Keyword enum) - compute/common/alignment::apply_alignment_fallback returns AlignContentKeyword; compute_alignment_offset takes one — internal matches lose every `| Self::Safe*` sibling - custom Serialize / Deserialize preserves the single-tag wire format ("Start", "SafeFlexEnd", …) so deserializing data produced by older builds still works - drive-by: fix pre-existing rand::distributions → rand::distr drift and Dimension/LengthPercentage enum-variant → tag-based API drift in benches/src/taffy_03_helpers.rs so `cargo test --workspace --all-features` passes again Size impact (src/style/mod.rs::style_sizes asserts these now): AlignContent / AlignItems : 1 → 2 bytes Option / : 1 → 2 bytes (niche-packed in safety byte) Style : 536 → 544 bytes Style> : 504 → 512 bytes No behaviour change: test_fixtures byte-identical, all 4345 generated + 107 lib + 45 hand-written + 5 doc tests pass with `--all-features` and `--no-default-features`. * style: drop drive-by _f32 literal suffixes Reverts 208 _f32 type-annotation suffixes that were inserted by an IDE auto-fixer responding to a nightly-only `f32: From` lint that's not yet a hard error. None of them are necessary on stable; several landed inside string literals or doc comments where there was no float to annotate at all (e.g. "Taffy 0.3" became "Taffy 0.3_f32"). Per @nicoburns review feedback: keep the diff minimal and let upstream address the lint when it actually fires. No behavior change: test_fixtures byte-identical, all 4345 generated + 107 lib + 45 hand-written + 5 doc tests pass with --all-features and --no-default-features. --- benches/src/taffy_03_helpers.rs | 30 +- benches/src/yoga_helpers.rs | 58 +- docs/style-properties.md | 46 +- src/compute/common/alignment.rs | 84 +- src/compute/flexbox.rs | 89 +- src/compute/grid/alignment.rs | 29 +- src/compute/grid/track_sizing.rs | 47 +- src/prelude.rs | 5 +- src/style/alignment.rs | 769 ++++++++++++++++-- src/style/available_space.rs | 2 +- src/style/dimension.rs | 2 +- src/style/mod.rs | 23 +- src/util/parse.rs | 2 +- .../flex_safe_align_content_end_overflow.html | 18 + .../flex_safe_align_self_center_overflow.html | 17 + .../flex_safe_align_self_end_overflow.html | 17 + ..._safe_justify_content_center_overflow.html | 18 + ...lex_safe_justify_content_end_overflow.html | 18 + .../flex_unsafe_align_self_end_overflow.html | 17 + ...id_safe_align_content_center_overflow.html | 25 + .../grid_safe_align_content_end_overflow.html | 25 + .../grid_safe_align_self_end_no_overflow.html | 17 + .../grid_safe_align_self_end_overflow.html | 17 + ..._safe_justify_content_center_overflow.html | 25 + ..._safe_justify_content_end_no_overflow.html | 25 + ...rid_safe_justify_content_end_overflow.html | 25 + ...rid_safe_justify_self_center_overflow.html | 17 + .../grid_safe_justify_self_end_overflow.html | 17 + .../grid_unsafe_align_self_end_overflow.html | 17 + ...d_unsafe_justify_content_end_overflow.html | 25 + tests/hand_written.rs | 1 + tests/hand_written/safe_alignment.rs | 308 +++++++ ...n_content_end_overflow__border_box_ltr.xml | 15 + ...n_content_end_overflow__border_box_rtl.xml | 15 + ..._content_end_overflow__content_box_ltr.xml | 15 + ..._content_end_overflow__content_box_rtl.xml | 15 + ...n_self_center_overflow__border_box_ltr.xml | 13 + ...n_self_center_overflow__border_box_rtl.xml | 13 + ..._self_center_overflow__content_box_ltr.xml | 13 + ..._self_center_overflow__content_box_rtl.xml | 13 + ...lign_self_end_overflow__border_box_ltr.xml | 13 + ...lign_self_end_overflow__border_box_rtl.xml | 13 + ...ign_self_end_overflow__content_box_ltr.xml | 13 + ...ign_self_end_overflow__content_box_rtl.xml | 13 + ...ontent_center_overflow__border_box_ltr.xml | 15 + ...ontent_center_overflow__border_box_rtl.xml | 15 + ...ntent_center_overflow__content_box_ltr.xml | 15 + ...ntent_center_overflow__content_box_rtl.xml | 15 + ...y_content_end_overflow__border_box_ltr.xml | 15 + ...y_content_end_overflow__border_box_rtl.xml | 15 + ..._content_end_overflow__content_box_ltr.xml | 15 + ..._content_end_overflow__content_box_rtl.xml | 15 + ...lign_self_end_overflow__border_box_ltr.xml | 13 + ...lign_self_end_overflow__border_box_rtl.xml | 13 + ...ign_self_end_overflow__content_box_ltr.xml | 13 + ...ign_self_end_overflow__content_box_rtl.xml | 13 + ...ontent_center_overflow__border_box_ltr.xml | 29 + ...ontent_center_overflow__border_box_rtl.xml | 29 + ...ntent_center_overflow__content_box_ltr.xml | 29 + ...ntent_center_overflow__content_box_rtl.xml | 29 + ...n_content_end_overflow__border_box_ltr.xml | 29 + ...n_content_end_overflow__border_box_rtl.xml | 29 + ..._content_end_overflow__content_box_ltr.xml | 29 + ..._content_end_overflow__content_box_rtl.xml | 29 + ...n_self_end_no_overflow__border_box_ltr.xml | 13 + ...n_self_end_no_overflow__border_box_rtl.xml | 13 + ..._self_end_no_overflow__content_box_ltr.xml | 13 + ..._self_end_no_overflow__content_box_rtl.xml | 13 + ...lign_self_end_overflow__border_box_ltr.xml | 13 + ...lign_self_end_overflow__border_box_rtl.xml | 13 + ...ign_self_end_overflow__content_box_ltr.xml | 13 + ...ign_self_end_overflow__content_box_rtl.xml | 13 + ...ontent_center_overflow__border_box_ltr.xml | 29 + ...ontent_center_overflow__border_box_rtl.xml | 29 + ...ntent_center_overflow__content_box_ltr.xml | 29 + ...ntent_center_overflow__content_box_rtl.xml | 29 + ...ontent_end_no_overflow__border_box_ltr.xml | 29 + ...ontent_end_no_overflow__border_box_rtl.xml | 29 + ...ntent_end_no_overflow__content_box_ltr.xml | 29 + ...ntent_end_no_overflow__content_box_rtl.xml | 29 + ...y_content_end_overflow__border_box_ltr.xml | 29 + ...y_content_end_overflow__border_box_rtl.xml | 29 + ..._content_end_overflow__content_box_ltr.xml | 29 + ..._content_end_overflow__content_box_rtl.xml | 29 + ...y_self_center_overflow__border_box_ltr.xml | 13 + ...y_self_center_overflow__border_box_rtl.xml | 13 + ..._self_center_overflow__content_box_ltr.xml | 13 + ..._self_center_overflow__content_box_rtl.xml | 13 + ...tify_self_end_overflow__border_box_ltr.xml | 13 + ...tify_self_end_overflow__border_box_rtl.xml | 13 + ...ify_self_end_overflow__content_box_ltr.xml | 13 + ...ify_self_end_overflow__content_box_rtl.xml | 13 + ...lign_self_end_overflow__border_box_ltr.xml | 13 + ...lign_self_end_overflow__border_box_rtl.xml | 13 + ...ign_self_end_overflow__content_box_ltr.xml | 13 + ...ign_self_end_overflow__content_box_rtl.xml | 13 + ...y_content_end_overflow__border_box_ltr.xml | 29 + ...y_content_end_overflow__border_box_rtl.xml | 29 + ..._content_end_overflow__content_box_ltr.xml | 29 + ..._content_end_overflow__content_box_rtl.xml | 29 + tests/xml/mod.rs | 384 +++++++++ 101 files changed, 3253 insertions(+), 258 deletions(-) create mode 100644 test_fixtures/flex/flex_safe_align_content_end_overflow.html create mode 100644 test_fixtures/flex/flex_safe_align_self_center_overflow.html create mode 100644 test_fixtures/flex/flex_safe_align_self_end_overflow.html create mode 100644 test_fixtures/flex/flex_safe_justify_content_center_overflow.html create mode 100644 test_fixtures/flex/flex_safe_justify_content_end_overflow.html create mode 100644 test_fixtures/flex/flex_unsafe_align_self_end_overflow.html create mode 100644 test_fixtures/grid/grid_safe_align_content_center_overflow.html create mode 100644 test_fixtures/grid/grid_safe_align_content_end_overflow.html create mode 100644 test_fixtures/grid/grid_safe_align_self_end_no_overflow.html create mode 100644 test_fixtures/grid/grid_safe_align_self_end_overflow.html create mode 100644 test_fixtures/grid/grid_safe_justify_content_center_overflow.html create mode 100644 test_fixtures/grid/grid_safe_justify_content_end_no_overflow.html create mode 100644 test_fixtures/grid/grid_safe_justify_content_end_overflow.html create mode 100644 test_fixtures/grid/grid_safe_justify_self_center_overflow.html create mode 100644 test_fixtures/grid/grid_safe_justify_self_end_overflow.html create mode 100644 test_fixtures/grid/grid_unsafe_align_self_end_overflow.html create mode 100644 test_fixtures/grid/grid_unsafe_justify_content_end_overflow.html create mode 100644 tests/hand_written/safe_alignment.rs create mode 100644 tests/xml/flex/flex_safe_align_content_end_overflow__border_box_ltr.xml create mode 100644 tests/xml/flex/flex_safe_align_content_end_overflow__border_box_rtl.xml create mode 100644 tests/xml/flex/flex_safe_align_content_end_overflow__content_box_ltr.xml create mode 100644 tests/xml/flex/flex_safe_align_content_end_overflow__content_box_rtl.xml create mode 100644 tests/xml/flex/flex_safe_align_self_center_overflow__border_box_ltr.xml create mode 100644 tests/xml/flex/flex_safe_align_self_center_overflow__border_box_rtl.xml create mode 100644 tests/xml/flex/flex_safe_align_self_center_overflow__content_box_ltr.xml create mode 100644 tests/xml/flex/flex_safe_align_self_center_overflow__content_box_rtl.xml create mode 100644 tests/xml/flex/flex_safe_align_self_end_overflow__border_box_ltr.xml create mode 100644 tests/xml/flex/flex_safe_align_self_end_overflow__border_box_rtl.xml create mode 100644 tests/xml/flex/flex_safe_align_self_end_overflow__content_box_ltr.xml create mode 100644 tests/xml/flex/flex_safe_align_self_end_overflow__content_box_rtl.xml create mode 100644 tests/xml/flex/flex_safe_justify_content_center_overflow__border_box_ltr.xml create mode 100644 tests/xml/flex/flex_safe_justify_content_center_overflow__border_box_rtl.xml create mode 100644 tests/xml/flex/flex_safe_justify_content_center_overflow__content_box_ltr.xml create mode 100644 tests/xml/flex/flex_safe_justify_content_center_overflow__content_box_rtl.xml create mode 100644 tests/xml/flex/flex_safe_justify_content_end_overflow__border_box_ltr.xml create mode 100644 tests/xml/flex/flex_safe_justify_content_end_overflow__border_box_rtl.xml create mode 100644 tests/xml/flex/flex_safe_justify_content_end_overflow__content_box_ltr.xml create mode 100644 tests/xml/flex/flex_safe_justify_content_end_overflow__content_box_rtl.xml create mode 100644 tests/xml/flex/flex_unsafe_align_self_end_overflow__border_box_ltr.xml create mode 100644 tests/xml/flex/flex_unsafe_align_self_end_overflow__border_box_rtl.xml create mode 100644 tests/xml/flex/flex_unsafe_align_self_end_overflow__content_box_ltr.xml create mode 100644 tests/xml/flex/flex_unsafe_align_self_end_overflow__content_box_rtl.xml create mode 100644 tests/xml/grid/grid_safe_align_content_center_overflow__border_box_ltr.xml create mode 100644 tests/xml/grid/grid_safe_align_content_center_overflow__border_box_rtl.xml create mode 100644 tests/xml/grid/grid_safe_align_content_center_overflow__content_box_ltr.xml create mode 100644 tests/xml/grid/grid_safe_align_content_center_overflow__content_box_rtl.xml create mode 100644 tests/xml/grid/grid_safe_align_content_end_overflow__border_box_ltr.xml create mode 100644 tests/xml/grid/grid_safe_align_content_end_overflow__border_box_rtl.xml create mode 100644 tests/xml/grid/grid_safe_align_content_end_overflow__content_box_ltr.xml create mode 100644 tests/xml/grid/grid_safe_align_content_end_overflow__content_box_rtl.xml create mode 100644 tests/xml/grid/grid_safe_align_self_end_no_overflow__border_box_ltr.xml create mode 100644 tests/xml/grid/grid_safe_align_self_end_no_overflow__border_box_rtl.xml create mode 100644 tests/xml/grid/grid_safe_align_self_end_no_overflow__content_box_ltr.xml create mode 100644 tests/xml/grid/grid_safe_align_self_end_no_overflow__content_box_rtl.xml create mode 100644 tests/xml/grid/grid_safe_align_self_end_overflow__border_box_ltr.xml create mode 100644 tests/xml/grid/grid_safe_align_self_end_overflow__border_box_rtl.xml create mode 100644 tests/xml/grid/grid_safe_align_self_end_overflow__content_box_ltr.xml create mode 100644 tests/xml/grid/grid_safe_align_self_end_overflow__content_box_rtl.xml create mode 100644 tests/xml/grid/grid_safe_justify_content_center_overflow__border_box_ltr.xml create mode 100644 tests/xml/grid/grid_safe_justify_content_center_overflow__border_box_rtl.xml create mode 100644 tests/xml/grid/grid_safe_justify_content_center_overflow__content_box_ltr.xml create mode 100644 tests/xml/grid/grid_safe_justify_content_center_overflow__content_box_rtl.xml create mode 100644 tests/xml/grid/grid_safe_justify_content_end_no_overflow__border_box_ltr.xml create mode 100644 tests/xml/grid/grid_safe_justify_content_end_no_overflow__border_box_rtl.xml create mode 100644 tests/xml/grid/grid_safe_justify_content_end_no_overflow__content_box_ltr.xml create mode 100644 tests/xml/grid/grid_safe_justify_content_end_no_overflow__content_box_rtl.xml create mode 100644 tests/xml/grid/grid_safe_justify_content_end_overflow__border_box_ltr.xml create mode 100644 tests/xml/grid/grid_safe_justify_content_end_overflow__border_box_rtl.xml create mode 100644 tests/xml/grid/grid_safe_justify_content_end_overflow__content_box_ltr.xml create mode 100644 tests/xml/grid/grid_safe_justify_content_end_overflow__content_box_rtl.xml create mode 100644 tests/xml/grid/grid_safe_justify_self_center_overflow__border_box_ltr.xml create mode 100644 tests/xml/grid/grid_safe_justify_self_center_overflow__border_box_rtl.xml create mode 100644 tests/xml/grid/grid_safe_justify_self_center_overflow__content_box_ltr.xml create mode 100644 tests/xml/grid/grid_safe_justify_self_center_overflow__content_box_rtl.xml create mode 100644 tests/xml/grid/grid_safe_justify_self_end_overflow__border_box_ltr.xml create mode 100644 tests/xml/grid/grid_safe_justify_self_end_overflow__border_box_rtl.xml create mode 100644 tests/xml/grid/grid_safe_justify_self_end_overflow__content_box_ltr.xml create mode 100644 tests/xml/grid/grid_safe_justify_self_end_overflow__content_box_rtl.xml create mode 100644 tests/xml/grid/grid_unsafe_align_self_end_overflow__border_box_ltr.xml create mode 100644 tests/xml/grid/grid_unsafe_align_self_end_overflow__border_box_rtl.xml create mode 100644 tests/xml/grid/grid_unsafe_align_self_end_overflow__content_box_ltr.xml create mode 100644 tests/xml/grid/grid_unsafe_align_self_end_overflow__content_box_rtl.xml create mode 100644 tests/xml/grid/grid_unsafe_justify_content_end_overflow__border_box_ltr.xml create mode 100644 tests/xml/grid/grid_unsafe_justify_content_end_overflow__border_box_rtl.xml create mode 100644 tests/xml/grid/grid_unsafe_justify_content_end_overflow__content_box_ltr.xml create mode 100644 tests/xml/grid/grid_unsafe_justify_content_end_overflow__content_box_rtl.xml 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 {