diff --git a/components/gfx/text/text_run.rs b/components/gfx/text/text_run.rs index 4a877c5eb67..0b36cde4c36 100644 --- a/components/gfx/text/text_run.rs +++ b/components/gfx/text/text_run.rs @@ -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(); diff --git a/components/layout_2020/flow/inline.rs b/components/layout_2020/flow/inline.rs index 70b789b8e4b..9c0a0e25fe1 100644 --- a/components/layout_2020/flow/inline.rs +++ b/components/layout_2020/flow/inline.rs @@ -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>, @@ -364,6 +370,11 @@ struct InlineFormattingContextState<'a, 'b> { /// The line breaking state for this inline formatting context. linebreaker: Option, + /// 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(); diff --git a/tests/wpt/meta/MANIFEST.json b/tests/wpt/meta/MANIFEST.json index 581809ff485..bc0157dc4d3 100644 --- a/tests/wpt/meta/MANIFEST.json +++ b/tests/wpt/meta/MANIFEST.json @@ -382,6 +382,13 @@ ] ] }, + "soft-wrap-opportunity-after-replaced-content-crash.html": [ + "2f2707f2d5d6039718e5f904a1cdeb05866959a4", + [ + null, + {} + ] + ], "video-needs-layout-crash.html": [ "b8c52edf8272abc6f7c30372b1d37e9545d08c74", [ diff --git a/tests/wpt/meta/css/css-text/line-breaking/line-breaking-atomic-002.html.ini b/tests/wpt/meta/css/css-text/line-breaking/line-breaking-atomic-002.html.ini deleted file mode 100644 index c7c55182e95..00000000000 --- a/tests/wpt/meta/css/css-text/line-breaking/line-breaking-atomic-002.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[line-breaking-atomic-002.html] - expected: FAIL diff --git a/tests/wpt/meta/css/css-text/line-breaking/line-breaking-atomic-009.html.ini b/tests/wpt/meta/css/css-text/line-breaking/line-breaking-atomic-009.html.ini deleted file mode 100644 index 05befa37f08..00000000000 --- a/tests/wpt/meta/css/css-text/line-breaking/line-breaking-atomic-009.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[line-breaking-atomic-009.html] - expected: FAIL diff --git a/tests/wpt/meta/css/css-text/line-breaking/line-breaking-replaced-001.html.ini b/tests/wpt/meta/css/css-text/line-breaking/line-breaking-replaced-001.html.ini deleted file mode 100644 index 79d633912dc..00000000000 --- a/tests/wpt/meta/css/css-text/line-breaking/line-breaking-replaced-001.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[line-breaking-replaced-001.html] - expected: FAIL diff --git a/tests/wpt/meta/css/css-text/line-breaking/line-breaking-replaced-006.html.ini b/tests/wpt/meta/css/css-text/line-breaking/line-breaking-replaced-006.html.ini deleted file mode 100644 index bd0b0384885..00000000000 --- a/tests/wpt/meta/css/css-text/line-breaking/line-breaking-replaced-006.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[line-breaking-replaced-006.html] - expected: FAIL diff --git a/tests/wpt/tests/css/CSS2/linebox/soft-wrap-opportunity-after-replaced-content-crash.html b/tests/wpt/tests/css/CSS2/linebox/soft-wrap-opportunity-after-replaced-content-crash.html new file mode 100644 index 00000000000..2f2707f2d5d --- /dev/null +++ b/tests/wpt/tests/css/CSS2/linebox/soft-wrap-opportunity-after-replaced-content-crash.html @@ -0,0 +1,9 @@ + + + + + +
  + + +