Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 13 additions & 11 deletions components/layout/construct_modern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,14 @@ impl<'dom> ModernContainerJob<'dom> {
.push_text(flex_text_run.text, &flex_text_run.info);
}

let inline_formatting_context = inline_formatting_context_builder.finish(
builder.context,
true, /* has_first_formatted_line */
false, /* is_single_line_text_box */
builder.info.style.to_bidi_level(),
)?;
let inline_formatting_context = inline_formatting_context_builder
.finish(
builder.context,
true, /* has_first_formatted_line */
false, /* is_single_line_text_box */
builder.info.style.to_bidi_level(),
)
.expect("Did not expect document white space only text runs");

let block_formatting_context = BlockFormattingContext::from_block_container(
BlockContainer::InlineFormattingContext(inline_formatting_context),
Expand Down Expand Up @@ -178,14 +180,14 @@ struct ModernContainerTextRun<'dom> {
}

impl ModernContainerTextRun<'_> {
/// <https://drafts.csswg.org/css-text/#white-space>
/// <https://drafts.csswg.org/css-flexbox/#flex-items>:
/// > However, if the entire text sequences contains only document white space characters (i.e.
/// > characters that can be affected by the white-space property) it is instead not rendered
/// > (just as if its text nodes were display:none).
fn is_only_document_white_space(&self) -> bool {
// FIXME: is this the right definition? See
// https://github.com/w3c/csswg-drafts/issues/5146
// https://github.com/w3c/csswg-drafts/issues/5147
self.text
.bytes()
.all(|byte| matches!(byte, b' ' | b'\n' | b'\t'))
.all(|byte| InlineFormattingContextBuilder::is_document_white_space(byte.into()))

@Loirooriol Loirooriol Apr 22, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have tested Firefox and Chrome, and U+000C doesn't create a flex item:

<!DOCTYPE html>
<div style="display: flex; justify-content: space-between">&#x000c;<span>item</span></div>

But it's not included in is_document_white_space

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't sure how to test this, so it is good to have confirmation. I've updated the change to include the form feed character (U+00C), which also happens to be WhatWG's definition of ASCII white space and the Rust is_ascii_whitespace function.

}
}

Expand Down
24 changes: 21 additions & 3 deletions components/layout/flow/inline/construct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,22 @@ pub(crate) struct InlineFormattingContextBuilder {
}

impl InlineFormattingContextBuilder {
/// <https://drafts.csswg.org/css-text/#white-space>:
/// > Except where specified otherwise, white space processing in CSS affects only the document
/// > white space characters: spaces (U+0020), tabs (U+0009), and segment breaks.
///
/// From <https://github.com/w3c/csswg-drafts/issues/5147#issuecomment-637816669>:
/// > HTML clearly treats CR, LF, and CRLF as segment breaks.
///
/// Other browsers also consider the form feed character (0x0c) to be document white space, it
/// seems.
///
/// Taken all together, this is equivalent to the WhatWG Infra Standard's definition of ASCII
/// white space.
pub(crate) fn is_document_white_space(character: char) -> bool {
character.is_ascii_whitespace()
}

pub(crate) fn new(info: &NodeAndStyleInfo, context: &LayoutContext) -> Self {
Self {
// For the purposes of `text-transform: capitalize` the start of the IFC is a word boundary.
Expand Down Expand Up @@ -370,9 +386,9 @@ impl InlineFormattingContextBuilder {

self.is_empty = self.is_empty &&
match white_space_collapse {
WhiteSpaceCollapse::Collapse => character.is_ascii_whitespace(),
WhiteSpaceCollapse::Collapse => Self::is_document_white_space(character),
WhiteSpaceCollapse::PreserveBreaks => {
character.is_ascii_whitespace() && character != '\n'
Self::is_document_white_space(character) && character != '\n'
},
WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces => false,
};
Expand Down Expand Up @@ -550,7 +566,9 @@ where
// Don't push non-newline whitespace immediately. Instead wait to push it until we
// know that it isn't followed by a newline. See `push_pending_whitespace_if_needed`
// above.
if character.is_ascii_whitespace() && character != '\n' {
if InlineFormattingContextBuilder::is_document_white_space(character) &&
character != '\n'
{
self.inside_white_space = true;
continue;
}
Expand Down
7 changes: 7 additions & 0 deletions tests/wpt/meta/MANIFEST.json
Original file line number Diff line number Diff line change
Expand Up @@ -2921,6 +2921,13 @@
]
]
},
"anonymous-flex-item-document-white-space-crash.html": [
"b670a1029ddf7552eaba97a77444815042e222e7",
[
null,
{}
]
],
"button-column-wrap-crash.html": [
"0741735d4e357af037169937229c96209c457ac7",
[
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<!DOCTYPE html>
<link rel="assert" href="Flex containers that only contain document white space should not cause a crash">
<link rel="help" href="https://github.com/servo/servo/issues/43550">

<div style="display: flex">&#10;&#13;</div>
<div style="display: flex">&#13;</div>
<div style="display: flex">&#12;</div>
Loading