From da8e6af7e8227d217c28f04f5fe8758f49b4cf00 Mon Sep 17 00:00:00 2001 From: Martin Vlach Date: Tue, 20 Oct 2026 09:20:24 +0200 Subject: [PATCH] Gradient meshes: deleting mesh lines leaves a malformed mesh as it is GradientMesh::remove_point_lines (the Mesh tool's Alt-click, object.mesh.deletePoint) only checked that the clicked point exists, then indexed the points above and below or left and right of it by the mesh's rows and columns. A gradient mesh read from a document is not checked against its rows and columns, so one with fewer points than they need (rows 2, columns 1 and 3 points instead of 6) indexes past the end and panics: the engine guard rolls the command back with an internal error, and the web app, which has no guard, aborts. It now returns false for a mesh that isn't valid, as locate (and with it adding mesh lines), the outline and the mesh lines already do, and the command reports that nothing could be deleted. The test Alt-clicks an interior row's point and an interior column's point of two such meshes and checks nothing is deleted and the mesh is unchanged. Reported-by: BS.ehlas.cz --- crates/doc/src/live.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/crates/doc/src/live.rs b/crates/doc/src/live.rs index fd7886786..5fe81d02b 100644 --- a/crates/doc/src/live.rs +++ b/crates/doc/src/live.rs @@ -1026,7 +1026,8 @@ impl GradientMesh { /// Mesh tool Alt-click: delete the mesh lines through point `index` (interior lines only). pub fn remove_point_lines(&mut self, index: usize) -> bool { - if index >= self.points.len() { + // A mesh from a file may not have the points its rows and columns need: it is indexed below. + if !self.is_valid() || index >= self.points.len() { return false; } let w = self.cols as usize + 1; @@ -1854,4 +1855,18 @@ mod tests { m.points[1].color = Color::rgb(1.0, 0.0, 0.0); assert!(matches!(m.color_at(0, 0, 0.5, 0.5).0, Color::Rgb { .. })); } + + /// Alt-clicking a point of a mesh from a file with fewer points than its rows and columns need + /// deletes nothing (it indexed past the points, a panic). + #[test] + fn mesh_lines_of_a_malformed_mesh_stay() { + let point = |i: u32| MeshPoint { p: Point::new(f64::from(i), 0.0), color: Color::BLACK, opacity: 1.0, handles: [Vec2::ZERO; 4] }; + // An interior row's point and an interior column's point, each with 3 of the 6 points. + for (rows, cols, index) in [(2, 1, 2), (1, 2, 1)] { + let mut m = GradientMesh { rows, cols, points: (0..3).map(point).collect() }; + let before = m.clone(); + assert!(!m.remove_point_lines(index), "{rows}×{cols}, point {index}"); + assert_eq!(m, before); + } + } }