Implement basic white-space: pre support for layout 2020.

This commit is contained in:
Josh Matthews 2020-05-06 00:51:59 -04:00
parent c7bdb1bcc0
commit d8b4dab4e3
55 changed files with 167 additions and 113 deletions

View file

@ -20,6 +20,7 @@ use rayon_croissant::ParallelIteratorExt;
use servo_arc::Arc;
use std::borrow::Cow;
use std::convert::{TryFrom, TryInto};
use style::computed_values::white_space::T as WhiteSpace;
use style::properties::longhands::list_style_position::computed_value::T as ListStylePosition;
use style::properties::ComputedValues;
use style::selector_parser::PseudoElement;
@ -293,7 +294,8 @@ where
}
fn handle_text(&mut self, info: &NodeAndStyleInfo<Node>, input: Cow<'dom, str>) {
let (leading_whitespace, mut input) = self.handle_leading_whitespace(&input);
let white_space = info.style.get_inherited_text().white_space;
let (leading_whitespace, mut input) = self.handle_leading_whitespace(&input, white_space);
if leading_whitespace || !input.is_empty() {
// This text node should be pushed either to the next ongoing
// inline level box with the parent style of that inline level box
@ -323,20 +325,37 @@ where
if leading_whitespace {
output.push(' ')
}
loop {
if let Some(i) = input.bytes().position(|b| b.is_ascii_whitespace()) {
let (non_whitespace, rest) = input.split_at(i);
output.push_str(non_whitespace);
output.push(' ');
if let Some(i) = rest.bytes().position(|b| !b.is_ascii_whitespace()) {
input = &rest[i..];
match (
white_space.preserve_spaces(),
white_space.preserve_newlines(),
) {
(true, true) => {
output.push_str(input);
},
(true, false) => unreachable!(),
(false, preserve_newlines) => loop {
if let Some(i) = input.bytes().position(|b| {
b.is_ascii_whitespace() && (!preserve_newlines || b != b'\n')
}) {
let (non_whitespace, rest) = input.split_at(i);
output.push_str(non_whitespace);
output.push(' ');
if let Some(i) = rest.bytes().position(|b| {
!b.is_ascii_whitespace() || (preserve_newlines && b == b'\n')
}) {
input = &rest[i..];
} else {
break;
}
} else {
output.push_str(input);
break;
}
} else {
output.push_str(input);
break;
}
},
}
}
@ -359,10 +378,14 @@ where
///
/// * Whether this text run has preserved (non-collapsible) leading whitespace
/// * The contents starting at the first non-whitespace character (or the empty string)
fn handle_leading_whitespace<'text>(&mut self, text: &'text str) -> (bool, &'text str) {
fn handle_leading_whitespace<'text>(
&mut self,
text: &'text str,
white_space: WhiteSpace,
) -> (bool, &'text str) {
// FIXME: this is only an approximation of
// https://drafts.csswg.org/css2/text.html#white-space-model
if !text.starts_with(|c: char| c.is_ascii_whitespace()) {
if !text.starts_with(|c: char| c.is_ascii_whitespace()) || white_space.preserve_spaces() {
return (false, text);
}

View file

@ -756,13 +756,15 @@ impl TextRun {
let mut glyphs = vec![];
let mut advance_width = Length::zero();
let mut last_break_opportunity = None;
let mut force_line_break = false;
loop {
let next = runs.next();
if next
.as_ref()
.map_or(true, |run| run.glyph_store.is_whitespace())
{
if advance_width > ifc.containing_block.inline_size - ifc.inline_position {
if next.as_ref().map_or(true, |run| {
run.glyph_store.is_whitespace() || force_line_break
}) {
if advance_width > ifc.containing_block.inline_size - ifc.inline_position ||
force_line_break
{
if let Some((len, width, iter)) = last_break_opportunity.take() {
glyphs.truncate(len);
advance_width = width;
@ -774,6 +776,14 @@ impl TextRun {
if let Some(run) = next {
if run.glyph_store.is_whitespace() {
last_break_opportunity = Some((glyphs.len(), advance_width, runs.clone()));
if self.text.as_bytes().get(run.range.end().to_usize() - 1) == Some(&b'\n')
{
force_line_break = self
.parent_style
.get_inherited_text()
.white_space
.preserve_newlines();
}
}
glyphs.push(run.glyph_store.clone());
advance_width += Length::from(run.glyph_store.total_advance());
@ -812,7 +822,7 @@ impl TextRun {
glyphs,
text_decoration_line: ifc.current_nesting_level.text_decoration_line,
}));
if runs.as_slice().is_empty() {
if runs.as_slice().is_empty() && !force_line_break {
break;
} else {
// New line

View file

@ -186,7 +186,6 @@ ${helpers.predefined_type(
name="white-space"
values="normal pre nowrap pre-wrap pre-line"
engines="gecko servo-2013 servo-2020",
servo_2020_pref="layout.2020.unimplemented",
extra_gecko_values="break-spaces -moz-pre-space"
gecko_enum_prefix="StyleWhiteSpace"
needs_conversion="True"