From eb0fb3e5d18a11dec4c81fc471828dcaccdd5fa0 Mon Sep 17 00:00:00 2001 From: webbeef Date: Fri, 4 Sep 2026 13:02:40 -0700 Subject: [PATCH] servo: reduce borrow lifetime of input fields to prevent crashes Signed-off-by: webbeef --- .../form_controls/htmlinputelement.rs.patch | 53 ++++++++++++++++++- .../htmltextareaelement.rs.patch | 51 +++++++++++++++++- 2 files changed, 101 insertions(+), 3 deletions(-) diff --git a/patches/components/script/dom/html/form_controls/htmlinputelement.rs.patch b/patches/components/script/dom/html/form_controls/htmlinputelement.rs.patch index d2067c2..cb37f24 100644 --- a/patches/components/script/dom/html/form_controls/htmlinputelement.rs.patch +++ b/patches/components/script/dom/html/form_controls/htmlinputelement.rs.patch @@ -11,7 +11,56 @@ use encoding_rs::Encoding; use html5ever::{LocalName, Prefix, local_name}; use js::context::JSContext; -@@ -1019,6 +1021,9 @@ +@@ -987,26 +989,31 @@ + } + + fn maybe_update_shared_selection(&self) { +- let mut text_input = self.textinput.borrow_mut(); +- let selection_range = text_input.selection_start()..text_input.selection_end(); +- let enabled = self.is_textual_or_password() && self.upcast::().focus_state(); ++ // The borrow ends before the widget update below: that path is free to read the ++ // element's value back out, and `Value()` borrows `textinput` again. ++ let selection = { ++ let mut text_input = self.textinput.borrow_mut(); ++ let selection_range = text_input.selection_start()..text_input.selection_end(); ++ let enabled = self.is_textual_or_password() && self.upcast::().focus_state(); + +- let range_remained_equal = selection_range == text_input.previous_selection_range; +- if range_remained_equal && enabled == text_input.selection_for_layout.is_some() { +- return; +- } ++ let range_remained_equal = selection_range == text_input.previous_selection_range; ++ if range_remained_equal && enabled == text_input.selection_for_layout.is_some() { ++ return; ++ } + +- if !range_remained_equal { +- // https://w3c.github.io/selection-api/#selectionchange-event +- // > When an input or textarea element provide a text selection and its selection changes +- // > (in either extent or direction), +- // > the user agent must schedule a selectionchange event on the element. +- self.schedule_a_selection_change_event(); +- } ++ if !range_remained_equal { ++ // https://w3c.github.io/selection-api/#selectionchange-event ++ // > When an input or textarea element provide a text selection and its selection changes ++ // > (in either extent or direction), ++ // > the user agent must schedule a selectionchange event on the element. ++ self.schedule_a_selection_change_event(); ++ } + +- let selection = enabled.then(|| text_input.sorted_selection_character_offsets_range()); +- text_input.previous_selection_range = selection_range; +- text_input.selection_for_layout = selection; ++ let selection = enabled.then(|| text_input.sorted_selection_character_offsets_range()); ++ text_input.previous_selection_range = selection_range; ++ text_input.selection_for_layout = selection; ++ selection ++ }; + + if let Some(text_input_widget) = self.input_type.borrow().as_specific().text_input_widget() + { +@@ -1019,6 +1026,9 @@ } else { // Non-text input type. Would this be even called? } @@ -21,7 +70,7 @@ } fn is_password_field(&self) -> bool { -@@ -1966,10 +1971,25 @@ +@@ -1966,10 +1976,25 @@ .hide_embedder_control(self.upcast()); } else if *event_type == *"focus" { let input_type = &*self.input_type(); diff --git a/patches/components/script/dom/html/form_controls/htmltextareaelement.rs.patch b/patches/components/script/dom/html/form_controls/htmltextareaelement.rs.patch index d777ebc..e70ddd2 100644 --- a/patches/components/script/dom/html/form_controls/htmltextareaelement.rs.patch +++ b/patches/components/script/dom/html/form_controls/htmltextareaelement.rs.patch @@ -1,6 +1,55 @@ --- original +++ modified -@@ -303,6 +303,9 @@ +@@ -272,26 +272,31 @@ + } + + fn maybe_update_shared_selection(&self) { +- let mut text_input = self.textinput.borrow_mut(); +- let selection_range = text_input.selection_start()..text_input.selection_end(); +- let enabled = self.upcast::().focus_state(); ++ // The borrow ends before the widget update below: that path is free to read the ++ // element's value back out, and `Value()` borrows `textinput` again. ++ let selection = { ++ let mut text_input = self.textinput.borrow_mut(); ++ let selection_range = text_input.selection_start()..text_input.selection_end(); ++ let enabled = self.upcast::().focus_state(); + +- let range_remained_equal = selection_range == text_input.previous_selection_range; +- if range_remained_equal && enabled == text_input.selection_for_layout.is_some() { +- return; +- } ++ let range_remained_equal = selection_range == text_input.previous_selection_range; ++ if range_remained_equal && enabled == text_input.selection_for_layout.is_some() { ++ return; ++ } + +- if !range_remained_equal { +- // https://w3c.github.io/selection-api/#selectionchange-event +- // > When an input or textarea element provide a text selection and its selection changes +- // > (in either extent or direction), +- // > the user agent must schedule a selectionchange event on the element. +- self.schedule_a_selection_change_event(); +- } ++ if !range_remained_equal { ++ // https://w3c.github.io/selection-api/#selectionchange-event ++ // > When an input or textarea element provide a text selection and its selection changes ++ // > (in either extent or direction), ++ // > the user agent must schedule a selectionchange event on the element. ++ self.schedule_a_selection_change_event(); ++ } + +- let selection = enabled.then(|| text_input.sorted_selection_character_offsets_range()); +- text_input.previous_selection_range = selection_range; +- text_input.selection_for_layout = selection; ++ let selection = enabled.then(|| text_input.sorted_selection_character_offsets_range()); ++ text_input.previous_selection_range = selection_range; ++ text_input.selection_for_layout = selection; ++ selection ++ }; + + if self + .text_input_widget +@@ -303,6 +308,9 @@ } else { // If there isn’t a text run, layout is pending to create it anyway } -- 2.51.2