aboutsummaryrefslogtreecommitdiffstats
path: root/components/layout_2020/table/construct.rs
diff options
context:
space:
mode:
authorOriol Brufau <obrufau@igalia.com>2025-02-12 21:11:11 +0100
committerGitHub <noreply@github.com>2025-02-12 20:11:11 +0000
commitf593b6d4262b5ba3f957de51a382757e7df37faf (patch)
treeaf852aff423a5ce2329cb471bf65405665733546 /components/layout_2020/table/construct.rs
parentabede5b4b2ab3da25400d4c0b86515e13f615c9c (diff)
downloadservo-f593b6d4262b5ba3f957de51a382757e7df37faf.tar.gz
servo-f593b6d4262b5ba3f957de51a382757e7df37faf.zip
Protect `create_spanned_slot_based_on_cell_above()` against arithmetic underflow (#35437)
`Table::create_spanned_slot_based_on_cell_above()` was performing the subtraction `self.slots.len() - 2`, which could theoretically result in underflow if `self.slots.len()` is 0 or 1. That shouldn't have been possible in practice, but it may be worth addressing, to improve code robustness. So this patch: - Switches to `self.current_y()?.checked_sub(1)?`, which is safe and is easier to understand. - Moves `create_spanned_slot_based_on_cell_above()` to `TableBuilder`, since `current_y()` is there, and the method is only used when building the table anyways. - Ensures that both callers use `expect()` to assert that the method returned a value. Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Diffstat (limited to 'components/layout_2020/table/construct.rs')
-rw-r--r--components/layout_2020/table/construct.rs70
1 files changed, 33 insertions, 37 deletions
diff --git a/components/layout_2020/table/construct.rs b/components/layout_2020/table/construct.rs
index 92fdfdff538..ccf09687085 100644
--- a/components/layout_2020/table/construct.rs
+++ b/components/layout_2020/table/construct.rs
@@ -180,32 +180,6 @@ impl Table {
},
}
}
-
- /// Create a [`TableSlot::Spanned`] for the target cell at the given coordinates. If
- /// no slots cover the target, then this returns [`None`]. Note: This does not handle
- /// slots that cover the target using `colspan`, but instead only considers slots that
- /// cover this slot via `rowspan`. `colspan` should be handled by appending to the
- /// return value of this function.
- fn create_spanned_slot_based_on_cell_above(
- &self,
- target_coords: TableSlotCoordinates,
- ) -> Option<TableSlot> {
- let coords_for_slot_above =
- TableSlotCoordinates::new(target_coords.x, self.slots.len() - 2);
- let slots_covering_slot_above = self.resolve_slot_at(coords_for_slot_above);
-
- let coords_of_slots_that_cover_target: Vec<_> = slots_covering_slot_above
- .into_iter()
- .filter(|slot| slot.covers_cell_at(target_coords))
- .map(|slot| target_coords - slot.coords)
- .collect();
-
- if coords_of_slots_that_cover_target.is_empty() {
- None
- } else {
- Some(TableSlot::Spanned(coords_of_slots_that_cover_target))
- }
- }
}
impl TableSlot {
@@ -514,6 +488,32 @@ impl TableBuilder {
self.create_slots_for_cells_above_with_rowspan(false);
}
+ /// Create a [`TableSlot::Spanned`] for the target cell at the given coordinates. If
+ /// no slots cover the target, then this returns [`None`]. Note: This does not handle
+ /// slots that cover the target using `colspan`, but instead only considers slots that
+ /// cover this slot via `rowspan`. `colspan` should be handled by appending to the
+ /// return value of this function.
+ fn create_spanned_slot_based_on_cell_above(
+ &self,
+ target_coords: TableSlotCoordinates,
+ ) -> Option<TableSlot> {
+ let y_above = self.current_y()?.checked_sub(1)?;
+ let coords_for_slot_above = TableSlotCoordinates::new(target_coords.x, y_above);
+ let slots_covering_slot_above = self.table.resolve_slot_at(coords_for_slot_above);
+
+ let coords_of_slots_that_cover_target: Vec<_> = slots_covering_slot_above
+ .into_iter()
+ .filter(|slot| slot.covers_cell_at(target_coords))
+ .map(|slot| target_coords - slot.coords)
+ .collect();
+
+ if coords_of_slots_that_cover_target.is_empty() {
+ None
+ } else {
+ Some(TableSlot::Spanned(coords_of_slots_that_cover_target))
+ }
+ }
+
/// When not in the process of filling a cell, make sure any incoming rowspans are
/// filled so that the next specified cell comes after them. Should have been called before
/// [`Self::add_cell`]
@@ -536,8 +536,7 @@ impl TableBuilder {
let new_cell = if *span != 0 {
*span -= 1;
- self.table
- .create_spanned_slot_based_on_cell_above(current_coords)
+ self.create_spanned_slot_based_on_cell_above(current_coords)
.expect(
"Nonzero incoming rowspan cannot occur without a cell spanning this slot",
)
@@ -606,16 +605,13 @@ impl TableBuilder {
// This code creates a new slot in the case that there is a table model error.
let coords_of_spanned_cell =
TableSlotCoordinates::new(current_x_plus_colspan_offset, current_coords.y);
- match self
- .table
+ let mut incoming_slot = self
.create_spanned_slot_based_on_cell_above(coords_of_spanned_cell)
- {
- Some(mut incoming_slot) => {
- incoming_slot.push_spanned(new_offset);
- incoming_slot
- },
- None => TableSlot::new_spanned(new_offset),
- }
+ .expect(
+ "Nonzero incoming rowspan cannot occur without a cell spanning this slot",
+ );
+ incoming_slot.push_spanned(new_offset);
+ incoming_slot
};
self.table.push_new_slot_to_last_row(new_slot);
}