mirror of
https://github.com/servo/servo.git
synced 2025-08-03 20:50:07 +01:00
fix: Crashing due to input element hack (#36461)
This PR addresses a crash in text input element due to a hack to prevent text input being trimmed. The updated behavior will only add `zero width space` unicode character to the node if there's no text content instead of adding it constantly. Also by adding the same `zero width space` unicode character to text area when there's no text content within will allow text area element to properly display caret. <img width="197" alt="截圖 2025-04-11 中午12 51 00" src="https://github.com/user-attachments/assets/10bc7314-9aa3-49df-baac-ef26d39a96d8" /> This PR also addresses issues with multiple glyph runs: https://github.com/user-attachments/assets/658db56f-b166-47ec-bc77-c6c13114d669 Testing: This PR is tested using: - `./mach run -d 'data:text/html,<input id="input_element" value="xxxxxxxxxxxxxxxxxxxx">'` - `./mach run -d 'data:text/html,<textarea id="input_element" value="xxxxxxxxxxxxxxxxxxxx">'` without causing crashes, while the results should be covered by WPT tests Fixes: https://github.com/servo/servo/issues/36449 --------- Signed-off-by: DK Liao <dklassic@gmail.com>
This commit is contained in:
parent
972ca77ce1
commit
5df4c760d3
5 changed files with 14 additions and 11 deletions
|
@ -1223,9 +1223,7 @@ fn glyphs(
|
||||||
glyphs
|
glyphs
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This implementation has not been tested against `TextFragment` with mutiple runs.
|
// TODO: The implementation here does not account for multiple glyph runs properly.
|
||||||
// It is possible that the `glyphs` function above will need to be modified to
|
|
||||||
// handle multiple runs correctly.
|
|
||||||
fn glyphs_advance_by_index(
|
fn glyphs_advance_by_index(
|
||||||
glyph_runs: &[Arc<GlyphStore>],
|
glyph_runs: &[Arc<GlyphStore>],
|
||||||
index: fonts_traits::ByteIndex,
|
index: fonts_traits::ByteIndex,
|
||||||
|
@ -1233,11 +1231,13 @@ fn glyphs_advance_by_index(
|
||||||
justification_adjustment: Au,
|
justification_adjustment: Au,
|
||||||
) -> PhysicalPoint<Au> {
|
) -> PhysicalPoint<Au> {
|
||||||
let mut point = baseline_origin;
|
let mut point = baseline_origin;
|
||||||
|
let mut index = index;
|
||||||
for run in glyph_runs {
|
for run in glyph_runs {
|
||||||
let total_advance = run.advance_for_byte_range(
|
let total_advance = run.advance_for_byte_range(
|
||||||
&ServoRange::new(fonts::ByteIndex(0), index),
|
&ServoRange::new(fonts::ByteIndex(0), index.min(run.len())),
|
||||||
justification_adjustment,
|
justification_adjustment,
|
||||||
);
|
);
|
||||||
|
index = index - index.min(run.len());
|
||||||
point.x += total_advance;
|
point.x += total_advance;
|
||||||
}
|
}
|
||||||
point
|
point
|
||||||
|
|
|
@ -212,12 +212,17 @@ fn traverse_children_of<'dom, Node>(
|
||||||
if is_text_input_element || is_textarea_element {
|
if is_text_input_element || is_textarea_element {
|
||||||
let info = NodeAndStyleInfo::new(parent_element, parent_element.style(context));
|
let info = NodeAndStyleInfo::new(parent_element, parent_element.style(context));
|
||||||
|
|
||||||
if is_text_input_element {
|
if parent_element
|
||||||
|
.to_threadsafe()
|
||||||
|
.node_text_content()
|
||||||
|
.is_empty()
|
||||||
|
{
|
||||||
// The addition of zero-width space here forces the text input to have an inline formatting
|
// The addition of zero-width space here forces the text input to have an inline formatting
|
||||||
// context that might otherwise be trimmed if there's no text. This is important to ensure
|
// context that might otherwise be trimmed if there's no text. This is important to ensure
|
||||||
// that the input element is at least as tall as the line gap of the caret:
|
// that the input element is at least as tall as the line gap of the caret:
|
||||||
// <https://drafts.csswg.org/css-ui/#element-with-default-preferred-size>.
|
// <https://drafts.csswg.org/css-ui/#element-with-default-preferred-size>.
|
||||||
//
|
//
|
||||||
|
// This is also used to ensure that the caret will still be rendered when the input is empty.
|
||||||
// TODO: Is there a less hacky way to do this?
|
// TODO: Is there a less hacky way to do this?
|
||||||
handler.handle_text(&info, "\u{200B}".into());
|
handler.handle_text(&info, "\u{200B}".into());
|
||||||
}
|
}
|
||||||
|
|
|
@ -166,7 +166,7 @@ impl TextRunSegment {
|
||||||
self.bidi_level,
|
self.bidi_level,
|
||||||
ServoRange::<ByteIndex>::new(
|
ServoRange::<ByteIndex>::new(
|
||||||
byte_processed + ByteIndex(self.range.start as isize),
|
byte_processed + ByteIndex(self.range.start as isize),
|
||||||
run.range.length(),
|
ByteIndex(self.range.len() as isize) - byte_processed,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
byte_processed = byte_processed + run.range.length();
|
byte_processed = byte_processed + run.range.length();
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
[.target > * 9]
|
[.target > * 9]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[.target > * 1]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[.target > * 11]
|
[.target > * 11]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
[.target > * 7]
|
||||||
|
expected: FAIL
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
[input-type-button-clip.html]
|
|
||||||
expected: FAIL
|
|
Loading…
Add table
Add a link
Reference in a new issue