Implement safe and unsafe overflow-position alignment keywords (#952)

* 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<AlignContent> / <AlignItems>: 1 → 2 bytes (niche-packed in safety byte)
  Style<String>                      : 536 → 544 bytes
  Style<Arc<str>>                    : 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<f64>` 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.
This commit is contained in:
Ritesh
2026-05-27 20:04:29 +01:00
committed by GitHub
parent d803b72f95
commit d1ff7e339b
101 changed files with 3253 additions and 258 deletions
+18 -12
View File
@@ -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<T, U, F: Fn(T) -> U>(input: taffy::geometry::Point<T>, 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"),
}
}
+27 -31
View File
@@ -143,45 +143,41 @@ fn into_pixels(dim: impl Into<tf::Dimension>) -> f32 {
}
fn items_into_align(align: Option<tf::AlignSelf>) -> 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<tf::AlignContent>) -> 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<tf::JustifyContent>) -> 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!(),
}
}
+40 -6
View File
@@ -24,12 +24,12 @@ N = Supported in spec but not implemented in Taffy
| `margin` | Y | ~Y | `Rect<LengthPercentageAuto>` | 32 | - | How large should the margin be on each side? |
| `gap` | Y | Y | `Size<LengthPercentage>` | 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<AlignItems>` | 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<AlignItems>` | 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<AlignItems>` | 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<AlignItems>` | 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<GridPlacement>` | 8 | - | The vertical (row) placement of a grid item |
| `grid_column` | - | Y | `Line<GridPlacement>` | 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.
+43 -41
View File
@@ -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 <https://www.w3.org/TR/css-align-3/> this implementation follows
/// the resolution of <https://github.com/w3c/csswg-drafts/issues/10154>.
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,
}
}
}
+52 -37
View File
@@ -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 <https://www.w3.org/TR/css-align-3/#overflow-values>. 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)
+22 -7
View File
@@ -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 <https://www.w3.org/TR/css-align-3/#overflow-values>. 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 {
+24 -23
View File
@@ -2,7 +2,7 @@
//! <https://www.w3.org/TR/css-grid-1/#layout-algorithm>
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 {
+3 -2
View File
@@ -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,
+680 -89
View File
@@ -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<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
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<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
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<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
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<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
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<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
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<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
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::<AlignItems>() <= 2, "AlignItems grew to {}", size_of::<AlignItems>());
assert!(size_of::<AlignContent>() <= 2, "AlignContent grew to {}", size_of::<AlignContent>());
assert!(size_of::<Option<AlignItems>>() <= 3);
assert!(size_of::<Option<AlignContent>>() <= 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::<AlignItems>().unwrap(), AlignItems::Start);
assert_eq!("end".parse::<AlignItems>().unwrap(), AlignItems::End);
assert_eq!("flex-start".parse::<AlignItems>().unwrap(), AlignItems::FlexStart);
assert_eq!("flex-end".parse::<AlignItems>().unwrap(), AlignItems::FlexEnd);
assert_eq!("center".parse::<AlignItems>().unwrap(), AlignItems::Center);
assert_eq!("baseline".parse::<AlignItems>().unwrap(), AlignItems::Baseline);
assert_eq!("stretch".parse::<AlignItems>().unwrap(), AlignItems::Stretch);
}
#[cfg(feature = "parse")]
#[test]
fn parse_align_items_safe() {
assert_eq!("safe start".parse::<AlignItems>().unwrap(), AlignItems::SafeStart);
assert_eq!("safe end".parse::<AlignItems>().unwrap(), AlignItems::SafeEnd);
assert_eq!("safe flex-start".parse::<AlignItems>().unwrap(), AlignItems::SafeFlexStart);
assert_eq!("safe flex-end".parse::<AlignItems>().unwrap(), AlignItems::SafeFlexEnd);
assert_eq!("safe center".parse::<AlignItems>().unwrap(), AlignItems::SafeCenter);
}
#[cfg(feature = "parse")]
#[test]
fn parse_align_items_safe_case_insensitive() {
assert_eq!("SAFE Start".parse::<AlignItems>().unwrap(), AlignItems::SafeStart);
assert_eq!("Safe FLEX-end".parse::<AlignItems>().unwrap(), AlignItems::SafeFlexEnd);
}
#[cfg(feature = "parse")]
#[test]
fn parse_align_items_unsafe_drops_modifier() {
assert_eq!("unsafe start".parse::<AlignItems>().unwrap(), AlignItems::Start);
assert_eq!("unsafe end".parse::<AlignItems>().unwrap(), AlignItems::End);
assert_eq!("unsafe center".parse::<AlignItems>().unwrap(), AlignItems::Center);
}
#[cfg(feature = "parse")]
#[test]
fn parse_align_items_rejects_invalid_safe_combos() {
assert!("safe stretch".parse::<AlignItems>().is_err());
assert!("safe baseline".parse::<AlignItems>().is_err());
assert!("safe space-between".parse::<AlignItems>().is_err());
assert!("safe".parse::<AlignItems>().is_err());
assert!("safe garbage".parse::<AlignItems>().is_err());
assert!("unsafe stretch".parse::<AlignItems>().is_err());
assert!("unsafe baseline".parse::<AlignItems>().is_err());
}
#[cfg(feature = "parse")]
#[test]
fn parse_align_content_plain() {
assert_eq!("start".parse::<AlignContent>().unwrap(), AlignContent::Start);
assert_eq!("space-between".parse::<AlignContent>().unwrap(), AlignContent::SpaceBetween);
assert_eq!("space-evenly".parse::<AlignContent>().unwrap(), AlignContent::SpaceEvenly);
assert_eq!("space-around".parse::<AlignContent>().unwrap(), AlignContent::SpaceAround);
assert_eq!("stretch".parse::<AlignContent>().unwrap(), AlignContent::Stretch);
}
#[cfg(feature = "parse")]
#[test]
fn parse_align_content_safe() {
assert_eq!("safe start".parse::<AlignContent>().unwrap(), AlignContent::SafeStart);
assert_eq!("safe end".parse::<AlignContent>().unwrap(), AlignContent::SafeEnd);
assert_eq!("safe flex-start".parse::<AlignContent>().unwrap(), AlignContent::SafeFlexStart);
assert_eq!("safe flex-end".parse::<AlignContent>().unwrap(), AlignContent::SafeFlexEnd);
assert_eq!("safe center".parse::<AlignContent>().unwrap(), AlignContent::SafeCenter);
}
#[cfg(feature = "parse")]
#[test]
fn parse_align_content_unsafe_drops_modifier() {
assert_eq!("unsafe start".parse::<AlignContent>().unwrap(), AlignContent::Start);
assert_eq!("unsafe flex-end".parse::<AlignContent>().unwrap(), AlignContent::FlexEnd);
}
#[cfg(feature = "parse")]
#[test]
fn parse_align_content_rejects_invalid_safe_combos() {
assert!("safe stretch".parse::<AlignContent>().is_err());
assert!("safe space-between".parse::<AlignContent>().is_err());
assert!("safe space-evenly".parse::<AlignContent>().is_err());
assert!("safe space-around".parse::<AlignContent>().is_err());
assert!("safe".parse::<AlignContent>().is_err());
assert!("unsafe stretch".parse::<AlignContent>().is_err());
assert!("unsafe space-between".parse::<AlignContent>().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::<AlignItems>("\"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::<AlignContent>("\"NotAVariant\"").is_err());
}
}
+1 -1
View File
@@ -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
/// <https://www.w3.org/TR/css-sizing-3/#available>
+1 -1
View File
@@ -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
///
+16 -7
View File
@@ -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::<Rect<LengthPercentageAuto>>(32);
assert_type_size::<Rect<Dimension>>(32);
// Alignment
assert_type_size::<AlignContent>(1);
assert_type_size::<AlignItems>(1);
assert_type_size::<Option<AlignItems>>(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::<AlignContentKeyword>(1);
assert_type_size::<AlignItemsKeyword>(1);
assert_type_size::<AlignmentSafety>(1);
assert_type_size::<AlignContent>(2);
assert_type_size::<AlignItems>(2);
assert_type_size::<Option<AlignItems>>(2);
assert_type_size::<Option<AlignContent>>(2);
// Flexbox Container
assert_type_size::<FlexDirection>(1);
@@ -1327,12 +1336,12 @@ mod tests {
assert_type_size::<GridTemplateComponent<String>>(56);
assert_type_size::<GridPlacement<String>>(32);
assert_type_size::<Line<GridPlacement<String>>>(64);
assert_type_size::<Style<String>>(536);
assert_type_size::<Style<String>>(544);
// String-type dependent (Arc<str>)
assert_type_size::<GridTemplateComponent<Arc<str>>>(56);
assert_type_size::<GridPlacement<Arc<str>>>(24);
assert_type_size::<Line<GridPlacement<Arc<str>>>>(48);
assert_type_size::<Style<Arc<str>>>(504);
assert_type_size::<Style<Arc<str>>>(512);
}
}
+1 -1
View File
@@ -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<Self, Self::Err> {
parse_css_str_entirely(input)
$crate::util::parse::parse_css_str_entirely(input)
}
}
};
@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<script src="../../scripts/gentest/test_helper.js"></script>
<link rel="stylesheet" type="text/css" href="../../scripts/gentest/test_base_style.css">
<title>
Test description
</title>
</head>
<body>
<div id="test-root" style="display: flex; flex-wrap: wrap; width: 100px; height: 100px; align-content: safe end;">
<div style="width: 60px; height: 80px; flex-shrink: 0;"></div>
<div style="width: 60px; height: 80px; flex-shrink: 0;"></div>
</div>
</body>
</html>
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<script src="../../scripts/gentest/test_helper.js"></script>
<link rel="stylesheet" type="text/css" href="../../scripts/gentest/test_base_style.css">
<title>
Test description
</title>
</head>
<body>
<div id="test-root" style="display: flex; width: 100px; height: 100px;">
<div style="width: 50px; height: 150px; flex-shrink: 0; align-self: safe center;"></div>
</div>
</body>
</html>
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<script src="../../scripts/gentest/test_helper.js"></script>
<link rel="stylesheet" type="text/css" href="../../scripts/gentest/test_base_style.css">
<title>
Test description
</title>
</head>
<body>
<div id="test-root" style="display: flex; width: 100px; height: 100px;">
<div style="width: 50px; height: 150px; flex-shrink: 0; align-self: safe end;"></div>
</div>
</body>
</html>
@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<script src="../../scripts/gentest/test_helper.js"></script>
<link rel="stylesheet" type="text/css" href="../../scripts/gentest/test_base_style.css">
<title>
Test description
</title>
</head>
<body>
<div id="test-root" style="display: flex; width: 100px; height: 100px; justify-content: safe center;">
<div style="width: 80px; height: 50px; flex-shrink: 0;"></div>
<div style="width: 80px; height: 50px; flex-shrink: 0;"></div>
</div>
</body>
</html>
@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<script src="../../scripts/gentest/test_helper.js"></script>
<link rel="stylesheet" type="text/css" href="../../scripts/gentest/test_base_style.css">
<title>
Test description
</title>
</head>
<body>
<div id="test-root" style="display: flex; width: 100px; height: 100px; justify-content: safe end;">
<div style="width: 80px; height: 50px; flex-shrink: 0;"></div>
<div style="width: 80px; height: 50px; flex-shrink: 0;"></div>
</div>
</body>
</html>
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<script src="../../scripts/gentest/test_helper.js"></script>
<link rel="stylesheet" type="text/css" href="../../scripts/gentest/test_base_style.css">
<title>
Test description
</title>
</head>
<body>
<div id="test-root" style="display: flex; width: 100px; height: 100px;">
<div style="width: 50px; height: 150px; flex-shrink: 0; align-self: unsafe end;"></div>
</div>
</body>
</html>
@@ -0,0 +1,25 @@
<!DOCTYPE html>
<html lang="en">
<head>
<script src="../../scripts/gentest/test_helper.js"></script>
<link rel="stylesheet" type="text/css" href="../../scripts/gentest/test_base_style.css">
<title>
Test description
</title>
</head>
<body>
<div id="test-root" style="height: 100px; width: 100px; display: grid; grid-template-columns: 40px 40px 40px; grid-template-rows: 40px 40px 40px; align-content: safe center;">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
</body>
</html>
@@ -0,0 +1,25 @@
<!DOCTYPE html>
<html lang="en">
<head>
<script src="../../scripts/gentest/test_helper.js"></script>
<link rel="stylesheet" type="text/css" href="../../scripts/gentest/test_base_style.css">
<title>
Test description
</title>
</head>
<body>
<div id="test-root" style="height: 100px; width: 100px; display: grid; grid-template-columns: 40px 40px 40px; grid-template-rows: 40px 40px 40px; align-content: safe end;">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
</body>
</html>
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<script src="../../scripts/gentest/test_helper.js"></script>
<link rel="stylesheet" type="text/css" href="../../scripts/gentest/test_base_style.css">
<title>
Test description
</title>
</head>
<body>
<div id="test-root" style="height: 100px; width: 100px; display: grid; grid-template-columns: 100px; grid-template-rows: 100px;">
<div style="width: 50px; height: 40px; align-self: safe end;"></div>
</div>
</body>
</html>
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<script src="../../scripts/gentest/test_helper.js"></script>
<link rel="stylesheet" type="text/css" href="../../scripts/gentest/test_base_style.css">
<title>
Test description
</title>
</head>
<body>
<div id="test-root" style="height: 100px; width: 100px; display: grid; grid-template-columns: 100px; grid-template-rows: 100px;">
<div style="width: 50px; height: 150px; align-self: safe end;"></div>
</div>
</body>
</html>
@@ -0,0 +1,25 @@
<!DOCTYPE html>
<html lang="en">
<head>
<script src="../../scripts/gentest/test_helper.js"></script>
<link rel="stylesheet" type="text/css" href="../../scripts/gentest/test_base_style.css">
<title>
Test description
</title>
</head>
<body>
<div id="test-root" style="height: 100px; width: 100px; display: grid; grid-template-columns: 40px 40px 40px; grid-template-rows: 40px 40px 40px; justify-content: safe center;">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
</body>
</html>
@@ -0,0 +1,25 @@
<!DOCTYPE html>
<html lang="en">
<head>
<script src="../../scripts/gentest/test_helper.js"></script>
<link rel="stylesheet" type="text/css" href="../../scripts/gentest/test_base_style.css">
<title>
Test description
</title>
</head>
<body>
<div id="test-root" style="height: 200px; width: 200px; display: grid; grid-template-columns: 40px 40px 40px; grid-template-rows: 40px 40px 40px; justify-content: safe end;">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
</body>
</html>
@@ -0,0 +1,25 @@
<!DOCTYPE html>
<html lang="en">
<head>
<script src="../../scripts/gentest/test_helper.js"></script>
<link rel="stylesheet" type="text/css" href="../../scripts/gentest/test_base_style.css">
<title>
Test description
</title>
</head>
<body>
<div id="test-root" style="height: 100px; width: 100px; display: grid; grid-template-columns: 40px 40px 40px; grid-template-rows: 40px 40px 40px; justify-content: safe end;">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
</body>
</html>
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<script src="../../scripts/gentest/test_helper.js"></script>
<link rel="stylesheet" type="text/css" href="../../scripts/gentest/test_base_style.css">
<title>
Test description
</title>
</head>
<body>
<div id="test-root" style="height: 100px; width: 100px; display: grid; grid-template-columns: 100px; grid-template-rows: 100px;">
<div style="width: 150px; height: 50px; justify-self: safe center;"></div>
</div>
</body>
</html>
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<script src="../../scripts/gentest/test_helper.js"></script>
<link rel="stylesheet" type="text/css" href="../../scripts/gentest/test_base_style.css">
<title>
Test description
</title>
</head>
<body>
<div id="test-root" style="height: 100px; width: 100px; display: grid; grid-template-columns: 100px; grid-template-rows: 100px;">
<div style="width: 150px; height: 50px; justify-self: safe end;"></div>
</div>
</body>
</html>
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<script src="../../scripts/gentest/test_helper.js"></script>
<link rel="stylesheet" type="text/css" href="../../scripts/gentest/test_base_style.css">
<title>
Test description
</title>
</head>
<body>
<div id="test-root" style="height: 100px; width: 100px; display: grid; grid-template-columns: 100px; grid-template-rows: 100px;">
<div style="width: 50px; height: 150px; align-self: unsafe end;"></div>
</div>
</body>
</html>
@@ -0,0 +1,25 @@
<!DOCTYPE html>
<html lang="en">
<head>
<script src="../../scripts/gentest/test_helper.js"></script>
<link rel="stylesheet" type="text/css" href="../../scripts/gentest/test_base_style.css">
<title>
Test description
</title>
</head>
<body>
<div id="test-root" style="height: 100px; width: 100px; display: grid; grid-template-columns: 40px 40px 40px; grid-template-rows: 40px 40px 40px; justify-content: unsafe end;">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
</body>
</html>
+1
View File
@@ -6,5 +6,6 @@ mod hand_written {
mod relayout;
mod root_constraints;
mod rounding;
mod safe_alignment;
mod serde;
}
+308
View File
@@ -0,0 +1,308 @@
//! Tests for the `safe` and `unsafe` overflow-position keywords on align-* and justify-*.
//!
//! Spec: <https://www.w3.org/TR/css-align-3/#overflow-values>
//!
//! 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);
}
@@ -0,0 +1,15 @@
<test name="flex_safe_align_content_end_overflow__border_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="flex" direction="ltr" flex-wrap="wrap" align-content="safe end" width="100px" height="100px">
<div direction="ltr" flex-shrink="0" width="60px" height="80px"/>
<div direction="ltr" flex-shrink="0" width="60px" height="80px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="0" y="0" width="60" height="80"/>
<node x="0" y="80" width="60" height="80"/>
</node>
</expectations>
</test>
@@ -0,0 +1,15 @@
<test name="flex_safe_align_content_end_overflow__border_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="flex" direction="rtl" flex-wrap="wrap" align-content="safe end" width="100px" height="100px">
<div direction="rtl" flex-shrink="0" width="60px" height="80px"/>
<div direction="rtl" flex-shrink="0" width="60px" height="80px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="40" y="0" width="60" height="80"/>
<node x="40" y="80" width="60" height="80"/>
</node>
</expectations>
</test>
@@ -0,0 +1,15 @@
<test name="flex_safe_align_content_end_overflow__content_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="flex" box-sizing="content-box" direction="ltr" flex-wrap="wrap" align-content="safe end" width="100px" height="100px">
<div box-sizing="content-box" direction="ltr" flex-shrink="0" width="60px" height="80px"/>
<div box-sizing="content-box" direction="ltr" flex-shrink="0" width="60px" height="80px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="0" y="0" width="60" height="80"/>
<node x="0" y="80" width="60" height="80"/>
</node>
</expectations>
</test>
@@ -0,0 +1,15 @@
<test name="flex_safe_align_content_end_overflow__content_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="flex" box-sizing="content-box" direction="rtl" flex-wrap="wrap" align-content="safe end" width="100px" height="100px">
<div box-sizing="content-box" direction="rtl" flex-shrink="0" width="60px" height="80px"/>
<div box-sizing="content-box" direction="rtl" flex-shrink="0" width="60px" height="80px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="40" y="0" width="60" height="80"/>
<node x="40" y="80" width="60" height="80"/>
</node>
</expectations>
</test>
@@ -0,0 +1,13 @@
<test name="flex_safe_align_self_center_overflow__border_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="flex" direction="ltr" width="100px" height="100px">
<div direction="ltr" align-self="safe center" flex-shrink="0" width="50px" height="150px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="0" y="0" width="50" height="150"/>
</node>
</expectations>
</test>
@@ -0,0 +1,13 @@
<test name="flex_safe_align_self_center_overflow__border_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="flex" direction="rtl" width="100px" height="100px">
<div direction="rtl" align-self="safe center" flex-shrink="0" width="50px" height="150px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="50" y="0" width="50" height="150"/>
</node>
</expectations>
</test>
@@ -0,0 +1,13 @@
<test name="flex_safe_align_self_center_overflow__content_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="flex" box-sizing="content-box" direction="ltr" width="100px" height="100px">
<div box-sizing="content-box" direction="ltr" align-self="safe center" flex-shrink="0" width="50px" height="150px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="0" y="0" width="50" height="150"/>
</node>
</expectations>
</test>
@@ -0,0 +1,13 @@
<test name="flex_safe_align_self_center_overflow__content_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="flex" box-sizing="content-box" direction="rtl" width="100px" height="100px">
<div box-sizing="content-box" direction="rtl" align-self="safe center" flex-shrink="0" width="50px" height="150px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="50" y="0" width="50" height="150"/>
</node>
</expectations>
</test>
@@ -0,0 +1,13 @@
<test name="flex_safe_align_self_end_overflow__border_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="flex" direction="ltr" width="100px" height="100px">
<div direction="ltr" align-self="safe end" flex-shrink="0" width="50px" height="150px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="0" y="0" width="50" height="150"/>
</node>
</expectations>
</test>
@@ -0,0 +1,13 @@
<test name="flex_safe_align_self_end_overflow__border_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="flex" direction="rtl" width="100px" height="100px">
<div direction="rtl" align-self="safe end" flex-shrink="0" width="50px" height="150px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="50" y="0" width="50" height="150"/>
</node>
</expectations>
</test>
@@ -0,0 +1,13 @@
<test name="flex_safe_align_self_end_overflow__content_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="flex" box-sizing="content-box" direction="ltr" width="100px" height="100px">
<div box-sizing="content-box" direction="ltr" align-self="safe end" flex-shrink="0" width="50px" height="150px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="0" y="0" width="50" height="150"/>
</node>
</expectations>
</test>
@@ -0,0 +1,13 @@
<test name="flex_safe_align_self_end_overflow__content_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="flex" box-sizing="content-box" direction="rtl" width="100px" height="100px">
<div box-sizing="content-box" direction="rtl" align-self="safe end" flex-shrink="0" width="50px" height="150px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="50" y="0" width="50" height="150"/>
</node>
</expectations>
</test>
@@ -0,0 +1,15 @@
<test name="flex_safe_justify_content_center_overflow__border_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="flex" direction="ltr" justify-content="safe center" width="100px" height="100px">
<div direction="ltr" flex-shrink="0" width="80px" height="50px"/>
<div direction="ltr" flex-shrink="0" width="80px" height="50px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="0" y="0" width="80" height="50"/>
<node x="80" y="0" width="80" height="50"/>
</node>
</expectations>
</test>
@@ -0,0 +1,15 @@
<test name="flex_safe_justify_content_center_overflow__border_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="flex" direction="rtl" justify-content="safe center" width="100px" height="100px">
<div direction="rtl" flex-shrink="0" width="80px" height="50px"/>
<div direction="rtl" flex-shrink="0" width="80px" height="50px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="20" y="0" width="80" height="50"/>
<node x="-60" y="0" width="80" height="50"/>
</node>
</expectations>
</test>
@@ -0,0 +1,15 @@
<test name="flex_safe_justify_content_center_overflow__content_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="flex" box-sizing="content-box" direction="ltr" justify-content="safe center" width="100px" height="100px">
<div box-sizing="content-box" direction="ltr" flex-shrink="0" width="80px" height="50px"/>
<div box-sizing="content-box" direction="ltr" flex-shrink="0" width="80px" height="50px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="0" y="0" width="80" height="50"/>
<node x="80" y="0" width="80" height="50"/>
</node>
</expectations>
</test>
@@ -0,0 +1,15 @@
<test name="flex_safe_justify_content_center_overflow__content_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="flex" box-sizing="content-box" direction="rtl" justify-content="safe center" width="100px" height="100px">
<div box-sizing="content-box" direction="rtl" flex-shrink="0" width="80px" height="50px"/>
<div box-sizing="content-box" direction="rtl" flex-shrink="0" width="80px" height="50px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="20" y="0" width="80" height="50"/>
<node x="-60" y="0" width="80" height="50"/>
</node>
</expectations>
</test>
@@ -0,0 +1,15 @@
<test name="flex_safe_justify_content_end_overflow__border_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="flex" direction="ltr" justify-content="safe end" width="100px" height="100px">
<div direction="ltr" flex-shrink="0" width="80px" height="50px"/>
<div direction="ltr" flex-shrink="0" width="80px" height="50px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="0" y="0" width="80" height="50"/>
<node x="80" y="0" width="80" height="50"/>
</node>
</expectations>
</test>
@@ -0,0 +1,15 @@
<test name="flex_safe_justify_content_end_overflow__border_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="flex" direction="rtl" justify-content="safe end" width="100px" height="100px">
<div direction="rtl" flex-shrink="0" width="80px" height="50px"/>
<div direction="rtl" flex-shrink="0" width="80px" height="50px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="20" y="0" width="80" height="50"/>
<node x="-60" y="0" width="80" height="50"/>
</node>
</expectations>
</test>
@@ -0,0 +1,15 @@
<test name="flex_safe_justify_content_end_overflow__content_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="flex" box-sizing="content-box" direction="ltr" justify-content="safe end" width="100px" height="100px">
<div box-sizing="content-box" direction="ltr" flex-shrink="0" width="80px" height="50px"/>
<div box-sizing="content-box" direction="ltr" flex-shrink="0" width="80px" height="50px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="0" y="0" width="80" height="50"/>
<node x="80" y="0" width="80" height="50"/>
</node>
</expectations>
</test>
@@ -0,0 +1,15 @@
<test name="flex_safe_justify_content_end_overflow__content_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="flex" box-sizing="content-box" direction="rtl" justify-content="safe end" width="100px" height="100px">
<div box-sizing="content-box" direction="rtl" flex-shrink="0" width="80px" height="50px"/>
<div box-sizing="content-box" direction="rtl" flex-shrink="0" width="80px" height="50px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="20" y="0" width="80" height="50"/>
<node x="-60" y="0" width="80" height="50"/>
</node>
</expectations>
</test>
@@ -0,0 +1,13 @@
<test name="flex_unsafe_align_self_end_overflow__border_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="flex" direction="ltr" width="100px" height="100px">
<div direction="ltr" align-self="unsafe end" flex-shrink="0" width="50px" height="150px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="0" y="-50" width="50" height="150"/>
</node>
</expectations>
</test>
@@ -0,0 +1,13 @@
<test name="flex_unsafe_align_self_end_overflow__border_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="flex" direction="rtl" width="100px" height="100px">
<div direction="rtl" align-self="unsafe end" flex-shrink="0" width="50px" height="150px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="50" y="-50" width="50" height="150"/>
</node>
</expectations>
</test>
@@ -0,0 +1,13 @@
<test name="flex_unsafe_align_self_end_overflow__content_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="flex" box-sizing="content-box" direction="ltr" width="100px" height="100px">
<div box-sizing="content-box" direction="ltr" align-self="unsafe end" flex-shrink="0" width="50px" height="150px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="0" y="-50" width="50" height="150"/>
</node>
</expectations>
</test>
@@ -0,0 +1,13 @@
<test name="flex_unsafe_align_self_end_overflow__content_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="flex" box-sizing="content-box" direction="rtl" width="100px" height="100px">
<div box-sizing="content-box" direction="rtl" align-self="unsafe end" flex-shrink="0" width="50px" height="150px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="50" y="-50" width="50" height="150"/>
</node>
</expectations>
</test>
@@ -0,0 +1,29 @@
<test name="grid_safe_align_content_center_overflow__border_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" direction="ltr" align-content="safe center" width="100px" height="100px" grid-template-rows="40px 40px 40px" grid-template-columns="40px 40px 40px">
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="0" y="0" width="40" height="40"/>
<node x="40" y="0" width="40" height="40"/>
<node x="80" y="0" width="40" height="40"/>
<node x="0" y="40" width="40" height="40"/>
<node x="40" y="40" width="40" height="40"/>
<node x="80" y="40" width="40" height="40"/>
<node x="0" y="80" width="40" height="40"/>
<node x="40" y="80" width="40" height="40"/>
<node x="80" y="80" width="40" height="40"/>
</node>
</expectations>
</test>
@@ -0,0 +1,29 @@
<test name="grid_safe_align_content_center_overflow__border_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" direction="rtl" align-content="safe center" width="100px" height="100px" grid-template-rows="40px 40px 40px" grid-template-columns="40px 40px 40px">
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="60" y="0" width="40" height="40"/>
<node x="20" y="0" width="40" height="40"/>
<node x="-20" y="0" width="40" height="40"/>
<node x="60" y="40" width="40" height="40"/>
<node x="20" y="40" width="40" height="40"/>
<node x="-20" y="40" width="40" height="40"/>
<node x="60" y="80" width="40" height="40"/>
<node x="20" y="80" width="40" height="40"/>
<node x="-20" y="80" width="40" height="40"/>
</node>
</expectations>
</test>
@@ -0,0 +1,29 @@
<test name="grid_safe_align_content_center_overflow__content_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" box-sizing="content-box" direction="ltr" align-content="safe center" width="100px" height="100px" grid-template-rows="40px 40px 40px" grid-template-columns="40px 40px 40px">
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="0" y="0" width="40" height="40"/>
<node x="40" y="0" width="40" height="40"/>
<node x="80" y="0" width="40" height="40"/>
<node x="0" y="40" width="40" height="40"/>
<node x="40" y="40" width="40" height="40"/>
<node x="80" y="40" width="40" height="40"/>
<node x="0" y="80" width="40" height="40"/>
<node x="40" y="80" width="40" height="40"/>
<node x="80" y="80" width="40" height="40"/>
</node>
</expectations>
</test>
@@ -0,0 +1,29 @@
<test name="grid_safe_align_content_center_overflow__content_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" box-sizing="content-box" direction="rtl" align-content="safe center" width="100px" height="100px" grid-template-rows="40px 40px 40px" grid-template-columns="40px 40px 40px">
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="60" y="0" width="40" height="40"/>
<node x="20" y="0" width="40" height="40"/>
<node x="-20" y="0" width="40" height="40"/>
<node x="60" y="40" width="40" height="40"/>
<node x="20" y="40" width="40" height="40"/>
<node x="-20" y="40" width="40" height="40"/>
<node x="60" y="80" width="40" height="40"/>
<node x="20" y="80" width="40" height="40"/>
<node x="-20" y="80" width="40" height="40"/>
</node>
</expectations>
</test>
@@ -0,0 +1,29 @@
<test name="grid_safe_align_content_end_overflow__border_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" direction="ltr" align-content="safe end" width="100px" height="100px" grid-template-rows="40px 40px 40px" grid-template-columns="40px 40px 40px">
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="0" y="0" width="40" height="40"/>
<node x="40" y="0" width="40" height="40"/>
<node x="80" y="0" width="40" height="40"/>
<node x="0" y="40" width="40" height="40"/>
<node x="40" y="40" width="40" height="40"/>
<node x="80" y="40" width="40" height="40"/>
<node x="0" y="80" width="40" height="40"/>
<node x="40" y="80" width="40" height="40"/>
<node x="80" y="80" width="40" height="40"/>
</node>
</expectations>
</test>
@@ -0,0 +1,29 @@
<test name="grid_safe_align_content_end_overflow__border_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" direction="rtl" align-content="safe end" width="100px" height="100px" grid-template-rows="40px 40px 40px" grid-template-columns="40px 40px 40px">
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="60" y="0" width="40" height="40"/>
<node x="20" y="0" width="40" height="40"/>
<node x="-20" y="0" width="40" height="40"/>
<node x="60" y="40" width="40" height="40"/>
<node x="20" y="40" width="40" height="40"/>
<node x="-20" y="40" width="40" height="40"/>
<node x="60" y="80" width="40" height="40"/>
<node x="20" y="80" width="40" height="40"/>
<node x="-20" y="80" width="40" height="40"/>
</node>
</expectations>
</test>
@@ -0,0 +1,29 @@
<test name="grid_safe_align_content_end_overflow__content_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" box-sizing="content-box" direction="ltr" align-content="safe end" width="100px" height="100px" grid-template-rows="40px 40px 40px" grid-template-columns="40px 40px 40px">
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="0" y="0" width="40" height="40"/>
<node x="40" y="0" width="40" height="40"/>
<node x="80" y="0" width="40" height="40"/>
<node x="0" y="40" width="40" height="40"/>
<node x="40" y="40" width="40" height="40"/>
<node x="80" y="40" width="40" height="40"/>
<node x="0" y="80" width="40" height="40"/>
<node x="40" y="80" width="40" height="40"/>
<node x="80" y="80" width="40" height="40"/>
</node>
</expectations>
</test>
@@ -0,0 +1,29 @@
<test name="grid_safe_align_content_end_overflow__content_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" box-sizing="content-box" direction="rtl" align-content="safe end" width="100px" height="100px" grid-template-rows="40px 40px 40px" grid-template-columns="40px 40px 40px">
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="60" y="0" width="40" height="40"/>
<node x="20" y="0" width="40" height="40"/>
<node x="-20" y="0" width="40" height="40"/>
<node x="60" y="40" width="40" height="40"/>
<node x="20" y="40" width="40" height="40"/>
<node x="-20" y="40" width="40" height="40"/>
<node x="60" y="80" width="40" height="40"/>
<node x="20" y="80" width="40" height="40"/>
<node x="-20" y="80" width="40" height="40"/>
</node>
</expectations>
</test>
@@ -0,0 +1,13 @@
<test name="grid_safe_align_self_end_no_overflow__border_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" direction="ltr" width="100px" height="100px" grid-template-rows="100px" grid-template-columns="100px">
<div direction="ltr" align-self="safe end" width="50px" height="40px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="0" y="60" width="50" height="40"/>
</node>
</expectations>
</test>
@@ -0,0 +1,13 @@
<test name="grid_safe_align_self_end_no_overflow__border_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" direction="rtl" width="100px" height="100px" grid-template-rows="100px" grid-template-columns="100px">
<div direction="rtl" align-self="safe end" width="50px" height="40px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="50" y="60" width="50" height="40"/>
</node>
</expectations>
</test>
@@ -0,0 +1,13 @@
<test name="grid_safe_align_self_end_no_overflow__content_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" box-sizing="content-box" direction="ltr" width="100px" height="100px" grid-template-rows="100px" grid-template-columns="100px">
<div box-sizing="content-box" direction="ltr" align-self="safe end" width="50px" height="40px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="0" y="60" width="50" height="40"/>
</node>
</expectations>
</test>
@@ -0,0 +1,13 @@
<test name="grid_safe_align_self_end_no_overflow__content_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" box-sizing="content-box" direction="rtl" width="100px" height="100px" grid-template-rows="100px" grid-template-columns="100px">
<div box-sizing="content-box" direction="rtl" align-self="safe end" width="50px" height="40px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="50" y="60" width="50" height="40"/>
</node>
</expectations>
</test>
@@ -0,0 +1,13 @@
<test name="grid_safe_align_self_end_overflow__border_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" direction="ltr" width="100px" height="100px" grid-template-rows="100px" grid-template-columns="100px">
<div direction="ltr" align-self="safe end" width="50px" height="150px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="0" y="0" width="50" height="150"/>
</node>
</expectations>
</test>
@@ -0,0 +1,13 @@
<test name="grid_safe_align_self_end_overflow__border_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" direction="rtl" width="100px" height="100px" grid-template-rows="100px" grid-template-columns="100px">
<div direction="rtl" align-self="safe end" width="50px" height="150px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="50" y="0" width="50" height="150"/>
</node>
</expectations>
</test>
@@ -0,0 +1,13 @@
<test name="grid_safe_align_self_end_overflow__content_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" box-sizing="content-box" direction="ltr" width="100px" height="100px" grid-template-rows="100px" grid-template-columns="100px">
<div box-sizing="content-box" direction="ltr" align-self="safe end" width="50px" height="150px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="0" y="0" width="50" height="150"/>
</node>
</expectations>
</test>
@@ -0,0 +1,13 @@
<test name="grid_safe_align_self_end_overflow__content_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" box-sizing="content-box" direction="rtl" width="100px" height="100px" grid-template-rows="100px" grid-template-columns="100px">
<div box-sizing="content-box" direction="rtl" align-self="safe end" width="50px" height="150px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="50" y="0" width="50" height="150"/>
</node>
</expectations>
</test>
@@ -0,0 +1,29 @@
<test name="grid_safe_justify_content_center_overflow__border_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" direction="ltr" justify-content="safe center" width="100px" height="100px" grid-template-rows="40px 40px 40px" grid-template-columns="40px 40px 40px">
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="0" y="0" width="40" height="40"/>
<node x="40" y="0" width="40" height="40"/>
<node x="80" y="0" width="40" height="40"/>
<node x="0" y="40" width="40" height="40"/>
<node x="40" y="40" width="40" height="40"/>
<node x="80" y="40" width="40" height="40"/>
<node x="0" y="80" width="40" height="40"/>
<node x="40" y="80" width="40" height="40"/>
<node x="80" y="80" width="40" height="40"/>
</node>
</expectations>
</test>
@@ -0,0 +1,29 @@
<test name="grid_safe_justify_content_center_overflow__border_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" direction="rtl" justify-content="safe center" width="100px" height="100px" grid-template-rows="40px 40px 40px" grid-template-columns="40px 40px 40px">
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="60" y="0" width="40" height="40"/>
<node x="20" y="0" width="40" height="40"/>
<node x="-20" y="0" width="40" height="40"/>
<node x="60" y="40" width="40" height="40"/>
<node x="20" y="40" width="40" height="40"/>
<node x="-20" y="40" width="40" height="40"/>
<node x="60" y="80" width="40" height="40"/>
<node x="20" y="80" width="40" height="40"/>
<node x="-20" y="80" width="40" height="40"/>
</node>
</expectations>
</test>
@@ -0,0 +1,29 @@
<test name="grid_safe_justify_content_center_overflow__content_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" box-sizing="content-box" direction="ltr" justify-content="safe center" width="100px" height="100px" grid-template-rows="40px 40px 40px" grid-template-columns="40px 40px 40px">
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="0" y="0" width="40" height="40"/>
<node x="40" y="0" width="40" height="40"/>
<node x="80" y="0" width="40" height="40"/>
<node x="0" y="40" width="40" height="40"/>
<node x="40" y="40" width="40" height="40"/>
<node x="80" y="40" width="40" height="40"/>
<node x="0" y="80" width="40" height="40"/>
<node x="40" y="80" width="40" height="40"/>
<node x="80" y="80" width="40" height="40"/>
</node>
</expectations>
</test>
@@ -0,0 +1,29 @@
<test name="grid_safe_justify_content_center_overflow__content_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" box-sizing="content-box" direction="rtl" justify-content="safe center" width="100px" height="100px" grid-template-rows="40px 40px 40px" grid-template-columns="40px 40px 40px">
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="60" y="0" width="40" height="40"/>
<node x="20" y="0" width="40" height="40"/>
<node x="-20" y="0" width="40" height="40"/>
<node x="60" y="40" width="40" height="40"/>
<node x="20" y="40" width="40" height="40"/>
<node x="-20" y="40" width="40" height="40"/>
<node x="60" y="80" width="40" height="40"/>
<node x="20" y="80" width="40" height="40"/>
<node x="-20" y="80" width="40" height="40"/>
</node>
</expectations>
</test>
@@ -0,0 +1,29 @@
<test name="grid_safe_justify_content_end_no_overflow__border_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" direction="ltr" justify-content="safe end" width="200px" height="200px" grid-template-rows="40px 40px 40px" grid-template-columns="40px 40px 40px">
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="200" height="200">
<node x="80" y="0" width="40" height="40"/>
<node x="120" y="0" width="40" height="40"/>
<node x="160" y="0" width="40" height="40"/>
<node x="80" y="40" width="40" height="40"/>
<node x="120" y="40" width="40" height="40"/>
<node x="160" y="40" width="40" height="40"/>
<node x="80" y="80" width="40" height="40"/>
<node x="120" y="80" width="40" height="40"/>
<node x="160" y="80" width="40" height="40"/>
</node>
</expectations>
</test>
@@ -0,0 +1,29 @@
<test name="grid_safe_justify_content_end_no_overflow__border_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" direction="rtl" justify-content="safe end" width="200px" height="200px" grid-template-rows="40px 40px 40px" grid-template-columns="40px 40px 40px">
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="200" height="200">
<node x="80" y="0" width="40" height="40"/>
<node x="40" y="0" width="40" height="40"/>
<node x="0" y="0" width="40" height="40"/>
<node x="80" y="40" width="40" height="40"/>
<node x="40" y="40" width="40" height="40"/>
<node x="0" y="40" width="40" height="40"/>
<node x="80" y="80" width="40" height="40"/>
<node x="40" y="80" width="40" height="40"/>
<node x="0" y="80" width="40" height="40"/>
</node>
</expectations>
</test>
@@ -0,0 +1,29 @@
<test name="grid_safe_justify_content_end_no_overflow__content_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" box-sizing="content-box" direction="ltr" justify-content="safe end" width="200px" height="200px" grid-template-rows="40px 40px 40px" grid-template-columns="40px 40px 40px">
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="200" height="200">
<node x="80" y="0" width="40" height="40"/>
<node x="120" y="0" width="40" height="40"/>
<node x="160" y="0" width="40" height="40"/>
<node x="80" y="40" width="40" height="40"/>
<node x="120" y="40" width="40" height="40"/>
<node x="160" y="40" width="40" height="40"/>
<node x="80" y="80" width="40" height="40"/>
<node x="120" y="80" width="40" height="40"/>
<node x="160" y="80" width="40" height="40"/>
</node>
</expectations>
</test>
@@ -0,0 +1,29 @@
<test name="grid_safe_justify_content_end_no_overflow__content_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" box-sizing="content-box" direction="rtl" justify-content="safe end" width="200px" height="200px" grid-template-rows="40px 40px 40px" grid-template-columns="40px 40px 40px">
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="200" height="200">
<node x="80" y="0" width="40" height="40"/>
<node x="40" y="0" width="40" height="40"/>
<node x="0" y="0" width="40" height="40"/>
<node x="80" y="40" width="40" height="40"/>
<node x="40" y="40" width="40" height="40"/>
<node x="0" y="40" width="40" height="40"/>
<node x="80" y="80" width="40" height="40"/>
<node x="40" y="80" width="40" height="40"/>
<node x="0" y="80" width="40" height="40"/>
</node>
</expectations>
</test>
@@ -0,0 +1,29 @@
<test name="grid_safe_justify_content_end_overflow__border_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" direction="ltr" justify-content="safe end" width="100px" height="100px" grid-template-rows="40px 40px 40px" grid-template-columns="40px 40px 40px">
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="0" y="0" width="40" height="40"/>
<node x="40" y="0" width="40" height="40"/>
<node x="80" y="0" width="40" height="40"/>
<node x="0" y="40" width="40" height="40"/>
<node x="40" y="40" width="40" height="40"/>
<node x="80" y="40" width="40" height="40"/>
<node x="0" y="80" width="40" height="40"/>
<node x="40" y="80" width="40" height="40"/>
<node x="80" y="80" width="40" height="40"/>
</node>
</expectations>
</test>
@@ -0,0 +1,29 @@
<test name="grid_safe_justify_content_end_overflow__border_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" direction="rtl" justify-content="safe end" width="100px" height="100px" grid-template-rows="40px 40px 40px" grid-template-columns="40px 40px 40px">
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="60" y="0" width="40" height="40"/>
<node x="20" y="0" width="40" height="40"/>
<node x="-20" y="0" width="40" height="40"/>
<node x="60" y="40" width="40" height="40"/>
<node x="20" y="40" width="40" height="40"/>
<node x="-20" y="40" width="40" height="40"/>
<node x="60" y="80" width="40" height="40"/>
<node x="20" y="80" width="40" height="40"/>
<node x="-20" y="80" width="40" height="40"/>
</node>
</expectations>
</test>
@@ -0,0 +1,29 @@
<test name="grid_safe_justify_content_end_overflow__content_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" box-sizing="content-box" direction="ltr" justify-content="safe end" width="100px" height="100px" grid-template-rows="40px 40px 40px" grid-template-columns="40px 40px 40px">
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="0" y="0" width="40" height="40"/>
<node x="40" y="0" width="40" height="40"/>
<node x="80" y="0" width="40" height="40"/>
<node x="0" y="40" width="40" height="40"/>
<node x="40" y="40" width="40" height="40"/>
<node x="80" y="40" width="40" height="40"/>
<node x="0" y="80" width="40" height="40"/>
<node x="40" y="80" width="40" height="40"/>
<node x="80" y="80" width="40" height="40"/>
</node>
</expectations>
</test>
@@ -0,0 +1,29 @@
<test name="grid_safe_justify_content_end_overflow__content_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" box-sizing="content-box" direction="rtl" justify-content="safe end" width="100px" height="100px" grid-template-rows="40px 40px 40px" grid-template-columns="40px 40px 40px">
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="60" y="0" width="40" height="40"/>
<node x="20" y="0" width="40" height="40"/>
<node x="-20" y="0" width="40" height="40"/>
<node x="60" y="40" width="40" height="40"/>
<node x="20" y="40" width="40" height="40"/>
<node x="-20" y="40" width="40" height="40"/>
<node x="60" y="80" width="40" height="40"/>
<node x="20" y="80" width="40" height="40"/>
<node x="-20" y="80" width="40" height="40"/>
</node>
</expectations>
</test>
@@ -0,0 +1,13 @@
<test name="grid_safe_justify_self_center_overflow__border_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" direction="ltr" width="100px" height="100px" grid-template-rows="100px" grid-template-columns="100px">
<div direction="ltr" justify-self="safe center" width="150px" height="50px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="0" y="0" width="150" height="50"/>
</node>
</expectations>
</test>
@@ -0,0 +1,13 @@
<test name="grid_safe_justify_self_center_overflow__border_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" direction="rtl" width="100px" height="100px" grid-template-rows="100px" grid-template-columns="100px">
<div direction="rtl" justify-self="safe center" width="150px" height="50px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="-50" y="0" width="150" height="50"/>
</node>
</expectations>
</test>
@@ -0,0 +1,13 @@
<test name="grid_safe_justify_self_center_overflow__content_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" box-sizing="content-box" direction="ltr" width="100px" height="100px" grid-template-rows="100px" grid-template-columns="100px">
<div box-sizing="content-box" direction="ltr" justify-self="safe center" width="150px" height="50px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="0" y="0" width="150" height="50"/>
</node>
</expectations>
</test>
@@ -0,0 +1,13 @@
<test name="grid_safe_justify_self_center_overflow__content_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" box-sizing="content-box" direction="rtl" width="100px" height="100px" grid-template-rows="100px" grid-template-columns="100px">
<div box-sizing="content-box" direction="rtl" justify-self="safe center" width="150px" height="50px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="-50" y="0" width="150" height="50"/>
</node>
</expectations>
</test>
@@ -0,0 +1,13 @@
<test name="grid_safe_justify_self_end_overflow__border_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" direction="ltr" width="100px" height="100px" grid-template-rows="100px" grid-template-columns="100px">
<div direction="ltr" justify-self="safe end" width="150px" height="50px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="0" y="0" width="150" height="50"/>
</node>
</expectations>
</test>
@@ -0,0 +1,13 @@
<test name="grid_safe_justify_self_end_overflow__border_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" direction="rtl" width="100px" height="100px" grid-template-rows="100px" grid-template-columns="100px">
<div direction="rtl" justify-self="safe end" width="150px" height="50px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="-50" y="0" width="150" height="50"/>
</node>
</expectations>
</test>
@@ -0,0 +1,13 @@
<test name="grid_safe_justify_self_end_overflow__content_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" box-sizing="content-box" direction="ltr" width="100px" height="100px" grid-template-rows="100px" grid-template-columns="100px">
<div box-sizing="content-box" direction="ltr" justify-self="safe end" width="150px" height="50px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="0" y="0" width="150" height="50"/>
</node>
</expectations>
</test>
@@ -0,0 +1,13 @@
<test name="grid_safe_justify_self_end_overflow__content_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" box-sizing="content-box" direction="rtl" width="100px" height="100px" grid-template-rows="100px" grid-template-columns="100px">
<div box-sizing="content-box" direction="rtl" justify-self="safe end" width="150px" height="50px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="-50" y="0" width="150" height="50"/>
</node>
</expectations>
</test>
@@ -0,0 +1,13 @@
<test name="grid_unsafe_align_self_end_overflow__border_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" direction="ltr" width="100px" height="100px" grid-template-rows="100px" grid-template-columns="100px">
<div direction="ltr" align-self="unsafe end" width="50px" height="150px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="0" y="-50" width="50" height="150"/>
</node>
</expectations>
</test>
@@ -0,0 +1,13 @@
<test name="grid_unsafe_align_self_end_overflow__border_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" direction="rtl" width="100px" height="100px" grid-template-rows="100px" grid-template-columns="100px">
<div direction="rtl" align-self="unsafe end" width="50px" height="150px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="50" y="-50" width="50" height="150"/>
</node>
</expectations>
</test>
@@ -0,0 +1,13 @@
<test name="grid_unsafe_align_self_end_overflow__content_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" box-sizing="content-box" direction="ltr" width="100px" height="100px" grid-template-rows="100px" grid-template-columns="100px">
<div box-sizing="content-box" direction="ltr" align-self="unsafe end" width="50px" height="150px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="0" y="-50" width="50" height="150"/>
</node>
</expectations>
</test>
@@ -0,0 +1,13 @@
<test name="grid_unsafe_align_self_end_overflow__content_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" box-sizing="content-box" direction="rtl" width="100px" height="100px" grid-template-rows="100px" grid-template-columns="100px">
<div box-sizing="content-box" direction="rtl" align-self="unsafe end" width="50px" height="150px"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="50" y="-50" width="50" height="150"/>
</node>
</expectations>
</test>
@@ -0,0 +1,29 @@
<test name="grid_unsafe_justify_content_end_overflow__border_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" direction="ltr" justify-content="unsafe end" width="100px" height="100px" grid-template-rows="40px 40px 40px" grid-template-columns="40px 40px 40px">
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
<div direction="ltr"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="-20" y="0" width="40" height="40"/>
<node x="20" y="0" width="40" height="40"/>
<node x="60" y="0" width="40" height="40"/>
<node x="-20" y="40" width="40" height="40"/>
<node x="20" y="40" width="40" height="40"/>
<node x="60" y="40" width="40" height="40"/>
<node x="-20" y="80" width="40" height="40"/>
<node x="20" y="80" width="40" height="40"/>
<node x="60" y="80" width="40" height="40"/>
</node>
</expectations>
</test>
@@ -0,0 +1,29 @@
<test name="grid_unsafe_justify_content_end_overflow__border_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" direction="rtl" justify-content="unsafe end" width="100px" height="100px" grid-template-rows="40px 40px 40px" grid-template-columns="40px 40px 40px">
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
<div direction="rtl"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="80" y="0" width="40" height="40"/>
<node x="40" y="0" width="40" height="40"/>
<node x="0" y="0" width="40" height="40"/>
<node x="80" y="40" width="40" height="40"/>
<node x="40" y="40" width="40" height="40"/>
<node x="0" y="40" width="40" height="40"/>
<node x="80" y="80" width="40" height="40"/>
<node x="40" y="80" width="40" height="40"/>
<node x="0" y="80" width="40" height="40"/>
</node>
</expectations>
</test>
@@ -0,0 +1,29 @@
<test name="grid_unsafe_justify_content_end_overflow__content_box_ltr" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" box-sizing="content-box" direction="ltr" justify-content="unsafe end" width="100px" height="100px" grid-template-rows="40px 40px 40px" grid-template-columns="40px 40px 40px">
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
<div box-sizing="content-box" direction="ltr"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="-20" y="0" width="40" height="40"/>
<node x="20" y="0" width="40" height="40"/>
<node x="60" y="0" width="40" height="40"/>
<node x="-20" y="40" width="40" height="40"/>
<node x="20" y="40" width="40" height="40"/>
<node x="60" y="40" width="40" height="40"/>
<node x="-20" y="80" width="40" height="40"/>
<node x="20" y="80" width="40" height="40"/>
<node x="60" y="80" width="40" height="40"/>
</node>
</expectations>
</test>
@@ -0,0 +1,29 @@
<test name="grid_unsafe_justify_content_end_overflow__content_box_rtl" use-rounding="true">
<viewport width="max-content" height="max-content"/>
<input>
<div display="grid" box-sizing="content-box" direction="rtl" justify-content="unsafe end" width="100px" height="100px" grid-template-rows="40px 40px 40px" grid-template-columns="40px 40px 40px">
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
<div box-sizing="content-box" direction="rtl"/>
</div>
</input>
<expectations>
<node x="0" y="0" width="100" height="100">
<node x="80" y="0" width="40" height="40"/>
<node x="40" y="0" width="40" height="40"/>
<node x="0" y="0" width="40" height="40"/>
<node x="80" y="40" width="40" height="40"/>
<node x="40" y="40" width="40" height="40"/>
<node x="0" y="40" width="40" height="40"/>
<node x="80" y="80" width="40" height="40"/>
<node x="40" y="80" width="40" height="40"/>
<node x="0" y="80" width="40" height="40"/>
</node>
</expectations>
</test>

Some files were not shown because too many files have changed in this diff Show More