mirror of
https://github.com/servo/servo.git
synced 2025-07-23 07:13:52 +01:00
Stop sending " " to linebreaker for replaced content (#30740)
We previously sent a " " to the linebreaker in order to ensure that the next text had a soft wrap opportunity at the start. Calling `next(" ")` without waiting until the returned index was 1, violated some invariants of linebreaker ultimately causing a panic. Instead of using the linebreaker for this, simply keep a flag in the IFC layout state, which avoids the problem entirely. Fixes #30703.
This commit is contained in:
parent
53b0fa827d
commit
f1c291853e
8 changed files with 57 additions and 13 deletions
|
@ -220,7 +220,7 @@ impl<'a> TextRun {
|
|||
if text.len() == 0 {
|
||||
return (glyphs, true);
|
||||
}
|
||||
*breaker = Some(LineBreakLeafIter::new(&text, 0));
|
||||
*breaker = Some(LineBreakLeafIter::new(text, 0));
|
||||
}
|
||||
|
||||
let breaker = breaker.as_mut().unwrap();
|
||||
|
|
|
@ -21,7 +21,7 @@ use style::values::generics::text::LineHeight;
|
|||
use style::values::specified::text::{TextAlignKeyword, TextDecorationLine};
|
||||
use style::Zero;
|
||||
use webrender_api::FontInstanceKey;
|
||||
use xi_unicode::LineBreakLeafIter;
|
||||
use xi_unicode::{linebreak_property, LineBreakLeafIter};
|
||||
|
||||
use super::float::PlacementAmongFloats;
|
||||
use super::CollapsibleWithParentStartMargin;
|
||||
|
@ -44,6 +44,12 @@ use crate::style_ext::{
|
|||
};
|
||||
use crate::ContainingBlock;
|
||||
|
||||
// These constants are the xi-unicode line breaking classes that are defined in
|
||||
// `table.rs`. Unfortunately, they are only identified by number.
|
||||
const XI_LINE_BREAKING_CLASS_GL: u8 = 12;
|
||||
const XI_LINE_BREAKING_CLASS_WJ: u8 = 30;
|
||||
const XI_LINE_BREAKING_CLASS_ZWJ: u8 = 40;
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub(crate) struct InlineFormattingContext {
|
||||
pub(super) inline_level_boxes: Vec<ArcRefCell<InlineLevelBox>>,
|
||||
|
@ -364,6 +370,11 @@ struct InlineFormattingContextState<'a, 'b> {
|
|||
/// The line breaking state for this inline formatting context.
|
||||
linebreaker: Option<LineBreakLeafIter>,
|
||||
|
||||
/// Whether or not a soft wrap opportunity is queued. Soft wrap opportunities are
|
||||
/// queued after replaced content and they are processed when the next text content
|
||||
/// is encountered.
|
||||
have_deferred_soft_wrap_opportunity: bool,
|
||||
|
||||
/// The currently white-space setting of this line. This is stored on the
|
||||
/// [`InlineFormattingContextState`] because when a soft wrap opportunity is defined
|
||||
/// by the boundary between two characters, the white-space property of their nearest
|
||||
|
@ -1248,6 +1259,7 @@ impl InlineFormattingContext {
|
|||
linebreak_before_new_content: false,
|
||||
white_space: containing_block.style.get_inherited_text().white_space,
|
||||
linebreaker: None,
|
||||
have_deferred_soft_wrap_opportunity: false,
|
||||
root_nesting_level: InlineContainerState {
|
||||
nested_block_size: line_height_from_style(layout_context, &containing_block.style),
|
||||
has_content: false,
|
||||
|
@ -1544,9 +1556,8 @@ impl IndependentFormattingContext {
|
|||
true,
|
||||
);
|
||||
|
||||
if let Some(linebreaker) = ifc.linebreaker.as_mut() {
|
||||
linebreaker.next(" ");
|
||||
}
|
||||
// Defer a soft wrap opportunity for when we next process text content.
|
||||
ifc.have_deferred_soft_wrap_opportunity = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1666,6 +1677,31 @@ impl TextRun {
|
|||
},
|
||||
};
|
||||
|
||||
// We either have a soft wrap opportunity if specified by the breaker or if we are
|
||||
// following replaced content.
|
||||
let have_deferred_soft_wrap_opportunity =
|
||||
mem::replace(&mut ifc.have_deferred_soft_wrap_opportunity, false);
|
||||
let mut break_at_start = break_at_start || have_deferred_soft_wrap_opportunity;
|
||||
|
||||
// From https://www.w3.org/TR/css-text-3/#line-break-details:
|
||||
//
|
||||
// > For Web-compatibility there is a soft wrap opportunity before and after each
|
||||
// > replaced element or other atomic inline, even when adjacent to a character that
|
||||
// > would normally suppress them, including U+00A0 NO-BREAK SPACE. However, with
|
||||
// > the exception of U+00A0 NO-BREAK SPACE, there must be no soft wrap opportunity
|
||||
// > between atomic inlines and adjacent characters belonging to the Unicode GL, WJ,
|
||||
// > or ZWJ line breaking classes.
|
||||
if have_deferred_soft_wrap_opportunity {
|
||||
if let Some(first_character) = self.text.chars().nth(0) {
|
||||
let class = linebreak_property(first_character);
|
||||
break_at_start = break_at_start &&
|
||||
(first_character == '\u{00A0}' ||
|
||||
(class != XI_LINE_BREAKING_CLASS_GL &&
|
||||
class != XI_LINE_BREAKING_CLASS_WJ &&
|
||||
class != XI_LINE_BREAKING_CLASS_ZWJ));
|
||||
}
|
||||
}
|
||||
|
||||
for (run_index, run) in runs.into_iter().enumerate() {
|
||||
ifc.possibly_flush_deferred_forced_line_break();
|
||||
|
||||
|
|
|
@ -382,6 +382,13 @@
|
|||
]
|
||||
]
|
||||
},
|
||||
"soft-wrap-opportunity-after-replaced-content-crash.html": [
|
||||
"2f2707f2d5d6039718e5f904a1cdeb05866959a4",
|
||||
[
|
||||
null,
|
||||
{}
|
||||
]
|
||||
],
|
||||
"video-needs-layout-crash.html": [
|
||||
"b8c52edf8272abc6f7c30372b1d37e9545d08c74",
|
||||
[
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
[line-breaking-atomic-002.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[line-breaking-atomic-009.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[line-breaking-replaced-001.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[line-breaking-replaced-006.html]
|
||||
expected: FAIL
|
|
@ -0,0 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<link rel="help" href="https://github.com/servo/servo/issues/30703">
|
||||
<link rel="author" href="mailto:mrobinson@igalia.com">
|
||||
<body>
|
||||
<br><img>
|
||||
</body>
|
||||
</html>
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue