Per https://www.w3.org/TR/css-grid-1/#overlarge-grids, to prevent
arithmetic overflow of the i16/u16 types used for grid track counts
and grid line placements.
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
diff --git a/src/compute/grid/explicit_grid.rs b/src/compute/grid/explicit_grid.rs
index b32f2c960..595f508ff 100644
--- a/src/compute/grid/explicit_grid.rs
+++ b/src/compute/grid/explicit_grid.rs
@@ -7,6 +7,9 @@ use crate::style_helpers::TaffyAuto;
use crate::util::sys::{ceil, floor, Vec};
use crate::util::MaybeMath;
use crate::util::ResolveOrZero;
+use core::cmp::min;
+
+use super::MAX_GRID_TRACKS;
use crate::{GenericGridTemplateComponent, GenericRepetition, GridContainerStyle};
/// The auto-repeat fit strategy to use
@@ -53,16 +56,18 @@ pub(crate) fn compute_explicit_grid_size_in_axis(
}
// Compute that number of track generated by single track definition and repetitions with a fixed repetition count
+ // (clamping to the maximum track limit to prevent arithmetic overflow)
let non_auto_repeating_track_count = template
.clone()
.map(|track_def| match track_def {
- GenericGridTemplateComponent::Single(_) => 1,
+ GenericGridTemplateComponent::Single(_) => 1u32,
GenericGridTemplateComponent::Repeat(repeat) => match repeat.count() {
- RepetitionCount::Count(count) => count * repeat.track_count(),
+ RepetitionCount::Count(count) => count as u32 * repeat.track_count() as u32,
RepetitionCount::AutoFit | RepetitionCount::AutoFill => 0,
},
})
- .sum::<u16>();
+ .sum::<u32>()
+ .min(MAX_GRID_TRACKS as u32) as u16;
let auto_repetition_count: u16 = template.clone().filter(|track_def| track_def.is_auto_repetition()).count() as u16;
let all_track_defs_have_fixed_component = template.clone().all(|track_def| match track_def {
@@ -101,7 +106,7 @@ pub(crate) fn compute_explicit_grid_size_in_axis(
let repetition_track_count = repetition_definition_iter.len() as u16;
// Determine the number of repetitions
- let num_repetitions: u16 = match auto_fit_container_size {
+ let num_repetitions: u32 = match auto_fit_container_size {
None => 1,
Some(inner_container_size) => {
let parent_size = Some(inner_container_size);
@@ -154,7 +159,7 @@ pub(crate) fn compute_explicit_grid_size_in_axis(
// If a single repetition already overflows the container then we return 1 as the repetition count
// (the number of repetitions is floored at 1)
if first_repetition_and_non_repeating_tracks_used_space > inner_container_size {
- 1u16
+ 1u32
} else {
let per_repetition_gap_used_space = (repetition_track_count as f32) * gap_size;
let per_repetition_used_space = per_repetition_track_used_space + per_repetition_gap_used_space;
@@ -168,15 +173,31 @@ pub(crate) fn compute_explicit_grid_size_in_axis(
// - Then we return the minimum number of repetitions required to overflow the size.
//
// In all cases we add the additional repetition that was already accounted for in the special-case computation above
+ // Note: as-casts from f32 to u32 saturate, so a huge or infinite number of repetitions
+ // (e.g. due to zero-sized tracks) resolves to u32::MAX and is clamped below
match auto_fit_strategy {
- AutoRepeatStrategy::MaxRepetitionsThatDoNotOverflow => (floor(num_repetition_that_fit) as u16) + 1,
- AutoRepeatStrategy::MinRepetitionsThatDoOverflow => (ceil(num_repetition_that_fit) as u16) + 1,
+ AutoRepeatStrategy::MaxRepetitionsThatDoNotOverflow => {
+ (floor(num_repetition_that_fit) as u32).saturating_add(1)
+ }
+ AutoRepeatStrategy::MinRepetitionsThatDoOverflow => {
+ (ceil(num_repetition_that_fit) as u32).saturating_add(1)
+ }
}
}
}
};
- let grid_template_track_count = non_auto_repeating_track_count + (repetition_track_count * num_repetitions);
+ // Clamp the number of repetitions (flooring at 1) such that the total number of tracks in the
+ // template does not exceed the maximum track limit
+ // https://www.w3.org/TR/css-grid-1/#overlarge-grids
+ let max_repetitions =
+ (((MAX_GRID_TRACKS - non_auto_repeating_track_count) as u32) / (repetition_track_count as u32)).max(1);
+ let num_repetitions = num_repetitions.clamp(1, max_repetitions) as u16;
+
+ let grid_template_track_count = min(
+ non_auto_repeating_track_count as u32 + (repetition_track_count as u32 * num_repetitions as u32),
+ MAX_GRID_TRACKS as u32,
+ ) as u16;
(num_repetitions, grid_template_track_count)
}
@@ -218,13 +239,14 @@ pub(super) fn initialize_grid_tracks(
.map(|track_template| {
track_template
.map(|track_def| match track_def {
- GenericGridTemplateComponent::Single(_) => 1,
+ GenericGridTemplateComponent::Single(_) => 1u32,
GenericGridTemplateComponent::Repeat(repeat) => match repeat.count() {
- RepetitionCount::Count(count) => count * repeat.track_count(),
+ RepetitionCount::Count(count) => count as u32 * repeat.track_count() as u32,
RepetitionCount::AutoFit | RepetitionCount::AutoFill => 0,
},
})
- .sum::<u16>()
+ .sum::<u32>()
+ .min(MAX_GRID_TRACKS as u32) as u16
})
.unwrap_or(0);
@@ -242,6 +264,10 @@ pub(super) fn initialize_grid_tracks(
let mut current_track_index = (counts.negative_implicit) as usize;
+ // The maximum index up to which explicit tracks may be created. The explicit track count is clamped
+ // to MAX_GRID_TRACKS, so track creation from the template must be truncated to match.
+ let explicit_track_limit = (counts.negative_implicit + counts.explicit) as usize;
+
// Create explicit tracks
// An explicit check against the count (rather than just relying on track_template being empty) is required here
// because a count of zero can result from the track_template being invalid, in which case it should be ignored.
@@ -250,17 +276,21 @@ pub(super) fn initialize_grid_tracks(
track_template.for_each(|track_sizing_function| {
match track_sizing_function {
GenericGridTemplateComponent::Single(sizing_function) => {
- tracks.push(GridTrack::new(
- sizing_function.min_sizing_function(),
- sizing_function.max_sizing_function(),
- ));
- tracks.push(GridTrack::gutter(gap));
- current_track_index += 1;
+ if current_track_index < explicit_track_limit {
+ tracks.push(GridTrack::new(
+ sizing_function.min_sizing_function(),
+ sizing_function.max_sizing_function(),
+ ));
+ tracks.push(GridTrack::gutter(gap));
+ current_track_index += 1;
+ }
}
GenericGridTemplateComponent::Repeat(repeat) => match repeat.count() {
RepetitionCount::Count(count) => {
+ let repeated_track_count = (repeat.track_count() as usize * count as usize)
+ .min(explicit_track_limit - current_track_index);
let track_iter = repeat.tracks();
- let track_iter = track_iter.cycle().take(repeat.track_count() as usize * count as usize);
+ let track_iter = track_iter.cycle().take(repeated_track_count);
track_iter.for_each(|sizing_function| {
tracks.push(GridTrack::new(
sizing_function.min_sizing_function(),
@@ -271,7 +301,9 @@ pub(super) fn initialize_grid_tracks(
});
}
RepetitionCount::AutoFit | RepetitionCount::AutoFill => {
- let auto_repeated_track_count = (counts.explicit - non_auto_repeating_track_count) as usize;
+ let auto_repeated_track_count =
+ (counts.explicit.saturating_sub(non_auto_repeating_track_count) as usize)
+ .min(explicit_track_limit - current_track_index);
let iter = repeat.tracks().cycle();
for track_def in iter.take(auto_repeated_track_count) {
let mut track =
diff --git a/src/compute/grid/implicit_grid.rs b/src/compute/grid/implicit_grid.rs
index 143e7c380..ce3c5196d 100644
--- a/src/compute/grid/implicit_grid.rs
+++ b/src/compute/grid/implicit_grid.rs
@@ -6,7 +6,7 @@ use crate::{CheapCloneStr, Direction, GridItemStyle};
use core::cmp::{max, min};
use super::types::TrackCounts;
-use super::OriginZeroLine;
+use super::{max_oz_line, OriginZeroLine, MIN_OZ_LINE};
/// Estimate the number of rows and columns in the grid
/// This is used as a performance optimisation to pre-size vectors and reduce allocations. It also forms a necessary step
@@ -178,7 +178,13 @@ fn child_min_line_max_line_span<S: CheapCloneStr>(
_ => 1,
};
- (min, max, span)
+ // Clamp the min and max lines into the limited grid so that the estimated implicit track counts
+ // stay within the maximum track limit (https://www.w3.org/TR/css-grid-1/#overlarge-grids).
+ // This matches the clamping of the actual item placements performed during placement.
+ let clamped_min = OriginZeroLine(min.0.max(MIN_OZ_LINE));
+ let clamped_max = OriginZeroLine(max.0.min(max_oz_line(explicit_track_count)));
+
+ (clamped_min, clamped_max, span)
}
#[allow(clippy::bool_assert_comparison)]
diff --git a/src/compute/grid/mod.rs b/src/compute/grid/mod.rs
index 1a631a8ff..a072267b1 100644
--- a/src/compute/grid/mod.rs
+++ b/src/compute/grid/mod.rs
@@ -24,7 +24,7 @@ use types::{CellOccupancyMatrix, GridTrack, NamedLineResolver, TrackCounts};
#[cfg(feature = "detailed_layout_info")]
use types::{GridItem, GridTrackKind};
-pub(crate) use types::{GridCoordinate, GridLine, OriginZeroLine};
+pub(crate) use types::{max_oz_line, GridCoordinate, GridLine, OriginZeroLine, MAX_GRID_TRACKS, MIN_OZ_LINE};
mod alignment;
mod explicit_grid;
@@ -195,8 +195,10 @@ pub fn compute_grid_layout<Tree: LayoutGridContainer>(
// type CustomIdent<'a> = <<Tree as LayoutPartialTree>::CoreContainerStyle<'_> as CoreStyle>::CustomIdent;
let mut name_resolver = NamedLineResolver::new(&style, col_auto_repetition_count, row_auto_repetition_count);
- let explicit_col_count = grid_template_col_count.max(name_resolver.area_column_count());
- let explicit_row_count = grid_template_row_count.max(name_resolver.area_row_count());
+ // Clamp the explicit grid to MAX_GRID_TRACKS tracks in each axis
+ // https://www.w3.org/TR/css-grid-1/#overlarge-grids
+ let explicit_col_count = grid_template_col_count.max(name_resolver.area_column_count()).min(MAX_GRID_TRACKS);
+ let explicit_row_count = grid_template_row_count.max(name_resolver.area_row_count()).min(MAX_GRID_TRACKS);
name_resolver.set_explicit_column_count(explicit_col_count);
name_resolver.set_explicit_row_count(explicit_row_count);
diff --git a/src/compute/grid/placement.rs b/src/compute/grid/placement.rs
index ff24a0368..716aa2118 100644
--- a/src/compute/grid/placement.rs
+++ b/src/compute/grid/placement.rs
@@ -1,7 +1,7 @@
//! Implements placing items in the grid and resolving the implicit grid.
//! <https://www.w3.org/TR/css-grid-1/#placement>
use super::types::{CellOccupancyMatrix, CellOccupancyState, GridItem};
-use super::{NamedLineResolver, OriginZeroLine};
+use super::{max_oz_line, NamedLineResolver, OriginZeroLine, MIN_OZ_LINE};
use crate::geometry::Line;
use crate::geometry::{AbsoluteAxis, InBothAbsAxis};
use crate::style::{AlignItems, GridAutoFlow, OriginZeroGridPlacement};
@@ -454,6 +454,17 @@ fn place_indefinitely_positioned_item(
}
}
+/// Clamp a placement into the limited grid `[-MAX_GRID_TRACKS, explicit_track_count + MAX_GRID_TRACKS]`,
+/// preserving a span of at least 1 track. Items placed outside of the limited grid are clamped into it.
+///
+/// See: <https://www.w3.org/TR/css-grid-1/#overlarge-grids>
+fn clamp_span_to_limited_grid(span: Line<OriginZeroLine>, explicit_track_count: u16) -> Line<OriginZeroLine> {
+ let max_line = max_oz_line(explicit_track_count);
+ let start = span.start.0.clamp(MIN_OZ_LINE, max_line - 1);
+ let end = span.end.0.clamp(start + 1, max_line);
+ Line { start: OriginZeroLine(start), end: OriginZeroLine(end) }
+}
+
/// Record the grid item in both CellOccupancyMatric and the GridItems list
/// once a definite placement has been determined
#[allow(clippy::too_many_arguments)]
@@ -475,6 +486,13 @@ fn record_grid_placement<S: GridItemStyle>(
#[cfg(test)]
println!("{cell_occupancy_matrix:?}");
+ // Clamp placements into the limited grid to prevent arithmetic overflow when growing the
+ // implicit grid (https://www.w3.org/TR/css-grid-1/#overlarge-grids)
+ let primary_explicit_count = cell_occupancy_matrix.track_counts(primary_axis).explicit;
+ let secondary_explicit_count = cell_occupancy_matrix.track_counts(primary_axis.other_axis()).explicit;
+ let primary_span = clamp_span_to_limited_grid(primary_span, primary_explicit_count);
+ let secondary_span = clamp_span_to_limited_grid(secondary_span, secondary_explicit_count);
+
// Mark area of grid as occupied
cell_occupancy_matrix.mark_area_as(primary_axis, primary_span, secondary_span, placement_type);
diff --git a/src/compute/grid/types/coordinates.rs b/src/compute/grid/types/coordinates.rs
index 8543c08e3..0190bd57b 100644
--- a/src/compute/grid/types/coordinates.rs
+++ b/src/compute/grid/types/coordinates.rs
@@ -4,6 +4,25 @@ use crate::geometry::Line;
use core::cmp::{max, Ordering};
use core::ops::{Add, AddAssign, Sub};
+/// The maximum number of explicit tracks, the maximum number of implicit tracks on each side of the
+/// explicit grid, and the maximum span of a single grid item. Grids larger than this limit are clamped.
+///
+/// See: <https://www.w3.org/TR/css-grid-1/#overlarge-grids>
+pub(crate) const MAX_GRID_TRACKS: u16 = 10_000;
+
+/// The lowest valid grid line in OriginZero coordinates (grids are clamped to `MAX_GRID_TRACKS`
+/// implicit tracks before the start of the explicit grid)
+pub(crate) const MIN_OZ_LINE: i16 = -(MAX_GRID_TRACKS as i16);
+
+/// The highest valid grid line in OriginZero coordinates given the explicit track count of the grid
+/// (grids are clamped to `MAX_GRID_TRACKS` implicit tracks after the end of the explicit grid)
+///
+/// The `explicit_track_count` passed must already be clamped to `MAX_GRID_TRACKS`.
+pub(crate) fn max_oz_line(explicit_track_count: u16) -> i16 {
+ debug_assert!(explicit_track_count <= MAX_GRID_TRACKS);
+ (explicit_track_count + MAX_GRID_TRACKS) as i16
+}
+
/// Represents a grid line position in "CSS Grid Line" coordinates
///
/// "CSS Grid Line" coordinates are those used in grid-row/grid-column in the CSS grid spec:
@@ -29,7 +48,9 @@ impl GridLine {
self.0
}
- /// Convert into OriginZero coordinates using the specified explicit track count
+ /// Convert into OriginZero coordinates using the specified explicit track count.
+ ///
+ /// The result is clamped into the limited grid `[-MAX_GRID_TRACKS, explicit_track_count + MAX_GRID_TRACKS]`
pub(crate) fn into_origin_zero_line(self, explicit_track_count: u16) -> OriginZeroLine {
let explicit_line_count = explicit_track_count + 1;
let oz_line = match self.0.cmp(&0) {
@@ -37,7 +58,7 @@ impl GridLine {
Ordering::Less => self.0 + explicit_line_count as i16,
Ordering::Equal => panic!("Grid line of zero is invalid"),
};
- OriginZeroLine(oz_line)
+ OriginZeroLine(oz_line.clamp(MIN_OZ_LINE, max_oz_line(explicit_track_count)))
}
}
diff --git a/src/compute/grid/types/mod.rs b/src/compute/grid/types/mod.rs
index 398aebb94..ac46e7fce 100644
--- a/src/compute/grid/types/mod.rs
+++ b/src/compute/grid/types/mod.rs
@@ -8,7 +8,7 @@ mod named;
// Publish only locally in the grid module
pub(super) use cell_occupancy::{CellOccupancyMatrix, CellOccupancyState};
-pub(crate) use coordinates::{GridCoordinate, GridLine, OriginZeroLine};
+pub(crate) use coordinates::{max_oz_line, GridCoordinate, GridLine, OriginZeroLine, MAX_GRID_TRACKS, MIN_OZ_LINE};
pub(super) use grid_item::GridItem;
pub(super) use grid_track::GridTrack;
pub(super) use grid_track_counts::TrackCounts;
diff --git a/src/style/grid.rs b/src/style/grid.rs
index 034318fdf..95ce9a7d7 100644
--- a/src/style/grid.rs
+++ b/src/style/grid.rs
@@ -3,7 +3,7 @@ use super::{
AlignContent, AlignItems, AlignSelf, CheapCloneStr, CompactLength, CoreStyle, Dimension, JustifyContent,
LengthPercentage, LengthPercentageAuto, Style,
};
-use crate::compute::grid::{GridCoordinate, GridLine, OriginZeroLine};
+use crate::compute::grid::{GridCoordinate, GridLine, OriginZeroLine, MAX_GRID_TRACKS};
use crate::geometry::{AbsoluteAxis, AbstractAxis, Line, MinMax, Size};
use crate::style_helpers::*;
use crate::sys::{DefaultCheapStr, Vec};
@@ -491,7 +491,9 @@ impl<S: CheapCloneStr> GridPlacement<S> {
pub fn into_origin_zero_placement_ignoring_named(&self, explicit_track_count: u16) -> OriginZeroGridPlacement {
match self {
Self::Auto => OriginZeroGridPlacement::Auto,
- Self::Span(span) => OriginZeroGridPlacement::Span(*span),
+ // Spans are clamped to the maximum track limit
+ // https://www.w3.org/TR/css-grid-1/#overlarge-grids
+ Self::Span(span) => OriginZeroGridPlacement::Span(min(*span, MAX_GRID_TRACKS)),
// Grid line zero is an invalid index, so it gets treated as Auto
// See: https://developer.mozilla.org/en-US/docs/Web/CSS/grid-row-start#values
Self::Line(line) => match line.as_i16() {
@@ -523,7 +525,9 @@ impl NonNamedGridPlacement {
) -> OriginZeroGridPlacement {
match self {
Self::Auto => OriginZeroGridPlacement::Auto,
- Self::Span(span) => OriginZeroGridPlacement::Span(*span),
+ // Spans are clamped to the maximum track limit
+ // https://www.w3.org/TR/css-grid-1/#overlarge-grids
+ Self::Span(span) => OriginZeroGridPlacement::Span(min(*span, MAX_GRID_TRACKS)),
// Grid line zero is an invalid index, so it gets treated as Auto
// See: https://developer.mozilla.org/en-US/docs/Web/CSS/grid-row-start#values
Self::Line(line) => match line.as_i16() {
diff --git a/tests/hand_written.rs b/tests/hand_written.rs
index 8f1b996ee..b269789f7 100644
--- a/tests/hand_written.rs
+++ b/tests/hand_written.rs
@@ -4,6 +4,7 @@ mod hand_written {
mod floats;
mod measure;
mod min_max_overrides;
+ mod overlarge_grids;
mod relayout;
mod root_constraints;
mod rounding;
diff --git a/tests/hand_written/overlarge_grids.rs b/tests/hand_written/overlarge_grids.rs
new file mode 100644
index 000000000..1204253e2
--- /dev/null
+++ b/tests/hand_written/overlarge_grids.rs
@@ -0,0 +1,184 @@
+//! Tests for grids which exceed the limits described at
+//! <https://www.w3.org/TR/css-grid-1/#overlarge-grids>
+//!
+//! Test cases are derived from Servo bug reports:
+//! - <https://github.com/servo/servo/issues/45949>
+//! - <https://github.com/servo/servo/issues/45939>
+//! - <https://github.com/servo/servo/issues/45938>
+//! - <https://github.com/servo/servo/issues/46081>
+#![cfg(feature = "grid")]
+
+use taffy::prelude::*;
+use taffy::style::{GridAutoFlow, RepetitionCount};
+
+/// <https://github.com/servo/servo/issues/45949>
+///
+/// `grid-template: 90px / repeat(32768, fit-content(512px)) fit-content(100%)`
+///
+/// An explicit track count greater than `i16::MAX` previously caused a subtract with overflow in
+/// `OriginZeroLine::implied_positive_implicit_tracks`.
+#[test]
+fn explicit_grid_huge_repeat_count() {
+ let mut tree: TaffyTree<()> = TaffyTree::new();
+ let child = tree.new_leaf(Style::default()).unwrap();
+ let node = tree
+ .new_with_children(
+ Style {
+ display: Display::Grid,
+ grid_template_rows: vec![length(90.0)],
+ grid_template_columns: vec![repeat(32768, vec![fit_content(length(512.0))]), fit_content(percent(1.0))],
+ ..Default::default()
+ },
+ &[child],
+ )
+ .unwrap();
+ tree.compute_layout(node, Size::MAX_CONTENT).unwrap();
+}
+
+/// A repeat count and track count whose product overflows `u16`
+#[test]
+fn explicit_grid_repeat_count_track_count_product_overflow() {
+ let mut tree: TaffyTree<()> = TaffyTree::new();
+ let child = tree.new_leaf(Style::default()).unwrap();
+ let node = tree
+ .new_with_children(
+ Style {
+ display: Display::Grid,
+ grid_template_columns: vec![repeat(40000, vec![length(10.0), length(10.0)])],
+ ..Default::default()
+ },
+ &[child],
+ )
+ .unwrap();
+ tree.compute_layout(node, Size::MAX_CONTENT).unwrap();
+}
+
+/// <https://github.com/servo/servo/issues/46081>
+///
+/// `grid-template-columns: repeat(auto-fill, 0em)` with a definite container size.
+///
+/// Zero-sized tracks previously resulted in an infinite number of repetitions, which caused
+/// an add with overflow when computing the repetition count.
+#[test]
+fn auto_fill_zero_sized_tracks() {
+ let mut tree: TaffyTree<()> = TaffyTree::new();
+ let child = tree.new_leaf(Style::default()).unwrap();
+ let node = tree
+ .new_with_children(
+ Style {
+ display: Display::Grid,
+ size: Size { width: length(200.0), height: auto() },
+ grid_template_columns: vec![repeat(RepetitionCount::AutoFill, vec![length(0.0)])],
+ ..Default::default()
+ },
+ &[child],
+ )
+ .unwrap();
+ tree.compute_layout(node, Size::MAX_CONTENT).unwrap();
+}
+
+/// Tiny (but non-zero) sized tracks in a large container can also produce more repetitions than
+/// fit in the 10000 track limit
+#[test]
+fn auto_fill_tiny_tracks_huge_container() {
+ let mut tree: TaffyTree<()> = TaffyTree::new();
+ let child = tree.new_leaf(Style::default()).unwrap();
+ let node = tree
+ .new_with_children(
+ Style {
+ display: Display::Grid,
+ size: Size { width: length(100_000.0), height: auto() },
+ grid_template_columns: vec![repeat(RepetitionCount::AutoFill, vec![length(1.0)])],
+ ..Default::default()
+ },
+ &[child],
+ )
+ .unwrap();
+ tree.compute_layout(node, Size::MAX_CONTENT).unwrap();
+}
+
+/// <https://github.com/servo/servo/issues/45939>
+///
+/// A large negative `grid-row-start` (as produced by Servo wrapping `45983` to `i16`) combined
+/// with `grid-auto-flow: column` and auto-placed items with definite column positions previously
+/// caused a panic in `OriginZeroLine::into_track_vec_index`.
+#[test]
+fn auto_flow_column_with_large_row_start() {
+ let mut tree: TaffyTree<()> = TaffyTree::new();
+ let column_start_child = Style { grid_column: Line { start: line(7), end: auto() }, ..Default::default() };
+ let row_start_child = Style { grid_row: Line { start: line(-19553), end: auto() }, ..Default::default() };
+ let children = [
+ tree.new_leaf(column_start_child.clone()).unwrap(),
+ tree.new_leaf(column_start_child.clone()).unwrap(),
+ tree.new_leaf(column_start_child.clone()).unwrap(),
+ tree.new_leaf(row_start_child).unwrap(),
+ ];
+ let node = tree
+ .new_with_children(
+ Style { display: Display::Grid, grid_auto_flow: GridAutoFlow::Column, ..Default::default() },
+ &children,
+ )
+ .unwrap();
+ tree.compute_layout(node, Size::MAX_CONTENT).unwrap();
+}
+
+/// <https://github.com/servo/servo/issues/45938>
+///
+/// As above, but with explicit grid columns and items with both a large row-start and
+/// a definite column position.
+#[test]
+fn auto_flow_column_with_explicit_columns_and_large_row_start() {
+ let mut tree: TaffyTree<()> = TaffyTree::new();
+ let column_start = Line { start: line(7), end: auto() };
+ let child1 = Style { grid_column: column_start.clone(), ..Default::default() };
+ let child2 = Style {
+ grid_column: column_start.clone(),
+ grid_row: Line { start: line(-19553), end: auto() },
+ ..Default::default()
+ };
+ let children = [
+ tree.new_leaf(child1.clone()).unwrap(),
+ tree.new_leaf(child1.clone()).unwrap(),
+ tree.new_leaf(child1.clone()).unwrap(),
+ tree.new_leaf(child2).unwrap(),
+ ];
+ let node = tree
+ .new_with_children(
+ Style {
+ display: Display::Grid,
+ grid_auto_flow: GridAutoFlow::Column,
+ grid_template_rows: vec![auto()],
+ grid_template_columns: vec![
+ fit_content(length(80.0)),
+ fit_content(length(80.0)),
+ minmax(max_content(), auto()),
+ ],
+ ..Default::default()
+ },
+ &children,
+ )
+ .unwrap();
+ tree.compute_layout(node, Size::MAX_CONTENT).unwrap();
+}
+
+/// Definitely placed items with extreme positive and negative line numbers
+#[test]
+fn definite_placement_with_extreme_line_numbers() {
+ let mut tree: TaffyTree<()> = TaffyTree::new();
+ let children = [
+ tree.new_leaf(Style {
+ grid_column: Line { start: line(32767), end: auto() },
+ grid_row: Line { start: line(-32768), end: auto() },
+ ..Default::default()
+ })
+ .unwrap(),
+ tree.new_leaf(Style {
+ grid_column: Line { start: span(10000), end: line(-32768) },
+ grid_row: Line { start: line(32767), end: span(10000) },
+ ..Default::default()
+ })
+ .unwrap(),
+ ];
+ let node = tree.new_with_children(Style { display: Display::Grid, ..Default::default() }, &children).unwrap();
+ tree.compute_layout(node, Size::MAX_CONTENT).unwrap();
+}