tawasuyu

tawasuyu/taffy

A high performance rust-powered UI layout library

HTML 231Rust 8TOML 3CSS 1317 Definiciones

Clamp CSS grids at 10000 tracks in each dimension

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>

a3f94c2748ff655b3ab42444640d8e69e917aaab · por Nico Burns · (hace 3 meses) · padre b9bfa1d14 · ver el árbol en este commit

Delta semántico · 20

src/compute/grid/explicit_grid.rs

src/compute/grid/implicit_grid.rs

src/compute/grid/mod.rs

src/compute/grid/placement.rs

src/compute/grid/types/coordinates.rs

src/style/grid.rs

tests/hand_written.rs

9 archivos · +299 −31

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();
+}