mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
layout: Add support for white-space-collapse: break-spaces
(#32388)
This change adds support for `white-space-collapse: break-spaces` and adds initial parsing support for `overflow-wrap` and `word-break`. The later two properties are not fully supported, only in their interaction with `break-spaces`. This is a preliminary change preparing to implement them. In addition, `break_and_shape` is now forked and added to Layout 2020. This function is going to change a lot soon and forking is preparation for this. More code that is only used by Layout 2013 is moved from `gfx` to that crate. Co-authored-by: Rakhi Sharma <atbrakhi@igalia.com>
This commit is contained in:
parent
c0dedf06d6
commit
60b4b6c9f0
96 changed files with 410 additions and 537 deletions
28
Cargo.lock
generated
28
Cargo.lock
generated
|
@ -1265,7 +1265,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "derive_common"
|
name = "derive_common"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
source = "git+https://github.com/servo/stylo?branch=2024-05-15#5e112c7bc6bf6dc78d2b5eeb0251b5ddb3e6585f"
|
source = "git+https://github.com/servo/stylo?branch=2024-05-15#71b0b0ac1d42b221fccee9034da06bfbf481f0d0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling",
|
"darling",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
|
@ -3454,7 +3454,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
|
checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"windows-targets 0.48.5",
|
"windows-targets 0.52.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -3625,7 +3625,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "malloc_size_of"
|
name = "malloc_size_of"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
source = "git+https://github.com/servo/stylo?branch=2024-05-15#5e112c7bc6bf6dc78d2b5eeb0251b5ddb3e6585f"
|
source = "git+https://github.com/servo/stylo?branch=2024-05-15#71b0b0ac1d42b221fccee9034da06bfbf481f0d0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"accountable-refcell",
|
"accountable-refcell",
|
||||||
"app_units",
|
"app_units",
|
||||||
|
@ -5221,7 +5221,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "selectors"
|
name = "selectors"
|
||||||
version = "0.24.0"
|
version = "0.24.0"
|
||||||
source = "git+https://github.com/servo/stylo?branch=2024-05-15#5e112c7bc6bf6dc78d2b5eeb0251b5ddb3e6585f"
|
source = "git+https://github.com/servo/stylo?branch=2024-05-15#71b0b0ac1d42b221fccee9034da06bfbf481f0d0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.5.0",
|
"bitflags 2.5.0",
|
||||||
"cssparser",
|
"cssparser",
|
||||||
|
@ -5509,7 +5509,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "servo_arc"
|
name = "servo_arc"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "git+https://github.com/servo/stylo?branch=2024-05-15#5e112c7bc6bf6dc78d2b5eeb0251b5ddb3e6585f"
|
source = "git+https://github.com/servo/stylo?branch=2024-05-15#71b0b0ac1d42b221fccee9034da06bfbf481f0d0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"nodrop",
|
"nodrop",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -5519,7 +5519,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "servo_atoms"
|
name = "servo_atoms"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
source = "git+https://github.com/servo/stylo?branch=2024-05-15#5e112c7bc6bf6dc78d2b5eeb0251b5ddb3e6585f"
|
source = "git+https://github.com/servo/stylo?branch=2024-05-15#71b0b0ac1d42b221fccee9034da06bfbf481f0d0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"string_cache",
|
"string_cache",
|
||||||
"string_cache_codegen",
|
"string_cache_codegen",
|
||||||
|
@ -5717,7 +5717,7 @@ checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "size_of_test"
|
name = "size_of_test"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
source = "git+https://github.com/servo/stylo?branch=2024-05-15#5e112c7bc6bf6dc78d2b5eeb0251b5ddb3e6585f"
|
source = "git+https://github.com/servo/stylo?branch=2024-05-15#71b0b0ac1d42b221fccee9034da06bfbf481f0d0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"static_assertions",
|
"static_assertions",
|
||||||
]
|
]
|
||||||
|
@ -5858,7 +5858,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "static_prefs"
|
name = "static_prefs"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/servo/stylo?branch=2024-05-15#5e112c7bc6bf6dc78d2b5eeb0251b5ddb3e6585f"
|
source = "git+https://github.com/servo/stylo?branch=2024-05-15#71b0b0ac1d42b221fccee9034da06bfbf481f0d0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strict-num"
|
name = "strict-num"
|
||||||
|
@ -5895,7 +5895,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "style"
|
name = "style"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
source = "git+https://github.com/servo/stylo?branch=2024-05-15#5e112c7bc6bf6dc78d2b5eeb0251b5ddb3e6585f"
|
source = "git+https://github.com/servo/stylo?branch=2024-05-15#71b0b0ac1d42b221fccee9034da06bfbf481f0d0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"app_units",
|
"app_units",
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
|
@ -5953,7 +5953,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "style_config"
|
name = "style_config"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
source = "git+https://github.com/servo/stylo?branch=2024-05-15#5e112c7bc6bf6dc78d2b5eeb0251b5ddb3e6585f"
|
source = "git+https://github.com/servo/stylo?branch=2024-05-15#71b0b0ac1d42b221fccee9034da06bfbf481f0d0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
]
|
]
|
||||||
|
@ -5961,7 +5961,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "style_derive"
|
name = "style_derive"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
source = "git+https://github.com/servo/stylo?branch=2024-05-15#5e112c7bc6bf6dc78d2b5eeb0251b5ddb3e6585f"
|
source = "git+https://github.com/servo/stylo?branch=2024-05-15#71b0b0ac1d42b221fccee9034da06bfbf481f0d0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling",
|
"darling",
|
||||||
"derive_common",
|
"derive_common",
|
||||||
|
@ -5992,7 +5992,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "style_traits"
|
name = "style_traits"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
source = "git+https://github.com/servo/stylo?branch=2024-05-15#5e112c7bc6bf6dc78d2b5eeb0251b5ddb3e6585f"
|
source = "git+https://github.com/servo/stylo?branch=2024-05-15#71b0b0ac1d42b221fccee9034da06bfbf481f0d0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"app_units",
|
"app_units",
|
||||||
"bitflags 2.5.0",
|
"bitflags 2.5.0",
|
||||||
|
@ -6356,7 +6356,7 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "to_shmem"
|
name = "to_shmem"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
source = "git+https://github.com/servo/stylo?branch=2024-05-15#5e112c7bc6bf6dc78d2b5eeb0251b5ddb3e6585f"
|
source = "git+https://github.com/servo/stylo?branch=2024-05-15#71b0b0ac1d42b221fccee9034da06bfbf481f0d0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cssparser",
|
"cssparser",
|
||||||
"servo_arc",
|
"servo_arc",
|
||||||
|
@ -6369,7 +6369,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "to_shmem_derive"
|
name = "to_shmem_derive"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
source = "git+https://github.com/servo/stylo?branch=2024-05-15#5e112c7bc6bf6dc78d2b5eeb0251b5ddb3e6585f"
|
source = "git+https://github.com/servo/stylo?branch=2024-05-15#71b0b0ac1d42b221fccee9034da06bfbf481f0d0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling",
|
"darling",
|
||||||
"derive_common",
|
"derive_common",
|
||||||
|
|
|
@ -2,108 +2,7 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use gfx::text::util::{is_cjk, transform_text, CompressionMode};
|
use gfx::text::util::is_cjk;
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_transform_compress_none() {
|
|
||||||
let test_strs = [
|
|
||||||
" foo bar",
|
|
||||||
"foo bar ",
|
|
||||||
"foo\n bar",
|
|
||||||
"foo \nbar",
|
|
||||||
" foo bar \nbaz",
|
|
||||||
"foo bar baz",
|
|
||||||
"foobarbaz\n\n",
|
|
||||||
];
|
|
||||||
|
|
||||||
let mode = CompressionMode::CompressNone;
|
|
||||||
for &test in test_strs.iter() {
|
|
||||||
let mut trimmed_str = String::new();
|
|
||||||
transform_text(test, mode, true, &mut trimmed_str);
|
|
||||||
assert_eq!(trimmed_str, test)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_transform_discard_newline() {
|
|
||||||
let test_strs = [
|
|
||||||
(" foo bar", " foo bar"),
|
|
||||||
("foo bar ", "foo bar "),
|
|
||||||
("foo\n bar", "foo bar"),
|
|
||||||
("foo \nbar", "foo bar"),
|
|
||||||
(" foo bar \nbaz", " foo bar baz"),
|
|
||||||
("foo bar baz", "foo bar baz"),
|
|
||||||
("foobarbaz\n\n", "foobarbaz"),
|
|
||||||
];
|
|
||||||
|
|
||||||
let mode = CompressionMode::DiscardNewline;
|
|
||||||
for &(test, oracle) in test_strs.iter() {
|
|
||||||
let mut trimmed_str = String::new();
|
|
||||||
transform_text(test, mode, true, &mut trimmed_str);
|
|
||||||
assert_eq!(trimmed_str, oracle)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_transform_compress_whitespace() {
|
|
||||||
let test_strs = [
|
|
||||||
(" foo bar", "foo bar"),
|
|
||||||
("foo bar ", "foo bar "),
|
|
||||||
("foo\n bar", "foo\n bar"),
|
|
||||||
("foo \nbar", "foo \nbar"),
|
|
||||||
(" foo bar \nbaz", "foo bar \nbaz"),
|
|
||||||
("foo bar baz", "foo bar baz"),
|
|
||||||
("foobarbaz\n\n", "foobarbaz\n\n"),
|
|
||||||
];
|
|
||||||
|
|
||||||
let mode = CompressionMode::CompressWhitespace;
|
|
||||||
for &(test, oracle) in test_strs.iter() {
|
|
||||||
let mut trimmed_str = String::new();
|
|
||||||
transform_text(test, mode, true, &mut trimmed_str);
|
|
||||||
assert_eq!(&*trimmed_str, oracle)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_transform_compress_whitespace_newline() {
|
|
||||||
let test_strs = vec![
|
|
||||||
(" foo bar", "foo bar"),
|
|
||||||
("foo bar ", "foo bar "),
|
|
||||||
("foo\n bar", "foo bar"),
|
|
||||||
("foo \nbar", "foo bar"),
|
|
||||||
(" foo bar \nbaz", "foo bar baz"),
|
|
||||||
("foo bar baz", "foo bar baz"),
|
|
||||||
("foobarbaz\n\n", "foobarbaz "),
|
|
||||||
];
|
|
||||||
|
|
||||||
let mode = CompressionMode::CompressWhitespaceNewline;
|
|
||||||
for &(test, oracle) in test_strs.iter() {
|
|
||||||
let mut trimmed_str = String::new();
|
|
||||||
transform_text(test, mode, true, &mut trimmed_str);
|
|
||||||
assert_eq!(&*trimmed_str, oracle)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_transform_compress_whitespace_newline_no_incoming() {
|
|
||||||
let test_strs = [
|
|
||||||
(" foo bar", " foo bar"),
|
|
||||||
("\nfoo bar", " foo bar"),
|
|
||||||
("foo bar ", "foo bar "),
|
|
||||||
("foo\n bar", "foo bar"),
|
|
||||||
("foo \nbar", "foo bar"),
|
|
||||||
(" foo bar \nbaz", " foo bar baz"),
|
|
||||||
("foo bar baz", "foo bar baz"),
|
|
||||||
("foobarbaz\n\n", "foobarbaz "),
|
|
||||||
];
|
|
||||||
|
|
||||||
let mode = CompressionMode::CompressWhitespaceNewline;
|
|
||||||
for &(test, oracle) in test_strs.iter() {
|
|
||||||
let mut trimmed_str = String::new();
|
|
||||||
transform_text(test, mode, false, &mut trimmed_str);
|
|
||||||
assert_eq!(trimmed_str, oracle)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_is_cjk() {
|
fn test_is_cjk() {
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use std::cmp::{Ordering, PartialOrd};
|
use std::cmp::{Ordering, PartialOrd};
|
||||||
|
use std::sync::Arc;
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
use std::{fmt, mem, u16};
|
use std::{fmt, mem, u16};
|
||||||
|
|
||||||
|
@ -773,3 +774,24 @@ impl<'a> Iterator for GlyphIterator<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A single series of glyphs within a text run.
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
pub struct GlyphRun {
|
||||||
|
/// The glyphs.
|
||||||
|
pub glyph_store: Arc<GlyphStore>,
|
||||||
|
/// The byte range of characters in the containing run.
|
||||||
|
pub range: Range<ByteIndex>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GlyphRun {
|
||||||
|
pub fn compare(&self, key: &ByteIndex) -> Ordering {
|
||||||
|
if *key < self.range.begin() {
|
||||||
|
Ordering::Greater
|
||||||
|
} else if *key >= self.range.end() {
|
||||||
|
Ordering::Less
|
||||||
|
} else {
|
||||||
|
Ordering::Equal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,11 +5,9 @@
|
||||||
use unicode_properties::{emoji, UnicodeEmoji};
|
use unicode_properties::{emoji, UnicodeEmoji};
|
||||||
|
|
||||||
pub use crate::text::shaping::Shaper;
|
pub use crate::text::shaping::Shaper;
|
||||||
pub use crate::text::text_run::TextRun;
|
|
||||||
|
|
||||||
pub mod glyph;
|
pub mod glyph;
|
||||||
pub mod shaping;
|
pub mod shaping;
|
||||||
pub mod text_run;
|
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
|
|
@ -4,106 +4,6 @@
|
||||||
|
|
||||||
use ucd::{Codepoint, UnicodeBlock};
|
use ucd::{Codepoint, UnicodeBlock};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
||||||
pub enum CompressionMode {
|
|
||||||
CompressNone,
|
|
||||||
CompressWhitespace,
|
|
||||||
CompressWhitespaceNewline,
|
|
||||||
DiscardNewline,
|
|
||||||
}
|
|
||||||
|
|
||||||
// ported from Gecko's nsTextFrameUtils::TransformText.
|
|
||||||
//
|
|
||||||
// High level TODOs:
|
|
||||||
//
|
|
||||||
// * Issue #113: consider incoming text state (arabic, etc)
|
|
||||||
// and propagate outgoing text state (dual of above)
|
|
||||||
//
|
|
||||||
// * Issue #114: record skipped and kept chars for mapping original to new text
|
|
||||||
//
|
|
||||||
// * Untracked: various edge cases for bidi, CJK, etc.
|
|
||||||
pub fn transform_text(
|
|
||||||
text: &str,
|
|
||||||
mode: CompressionMode,
|
|
||||||
incoming_whitespace: bool,
|
|
||||||
output_text: &mut String,
|
|
||||||
) -> bool {
|
|
||||||
let out_whitespace = match mode {
|
|
||||||
CompressionMode::CompressNone | CompressionMode::DiscardNewline => {
|
|
||||||
for ch in text.chars() {
|
|
||||||
if is_discardable_char(ch, mode) {
|
|
||||||
// TODO: record skipped char
|
|
||||||
} else {
|
|
||||||
// TODO: record kept char
|
|
||||||
if ch == '\t' {
|
|
||||||
// TODO: set "has tab" flag
|
|
||||||
}
|
|
||||||
output_text.push(ch);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
},
|
|
||||||
|
|
||||||
CompressionMode::CompressWhitespace | CompressionMode::CompressWhitespaceNewline => {
|
|
||||||
let mut in_whitespace: bool = incoming_whitespace;
|
|
||||||
for ch in text.chars() {
|
|
||||||
// TODO: discard newlines between CJK chars
|
|
||||||
let mut next_in_whitespace: bool = is_in_whitespace(ch, mode);
|
|
||||||
|
|
||||||
if !next_in_whitespace {
|
|
||||||
if is_always_discardable_char(ch) {
|
|
||||||
// revert whitespace setting, since this char was discarded
|
|
||||||
next_in_whitespace = in_whitespace;
|
|
||||||
// TODO: record skipped char
|
|
||||||
} else {
|
|
||||||
// TODO: record kept char
|
|
||||||
output_text.push(ch);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* next_in_whitespace; possibly add a space char */
|
|
||||||
if in_whitespace {
|
|
||||||
// TODO: record skipped char
|
|
||||||
} else {
|
|
||||||
// TODO: record kept char
|
|
||||||
output_text.push(' ');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// save whitespace context for next char
|
|
||||||
in_whitespace = next_in_whitespace;
|
|
||||||
} /* /for str::each_char */
|
|
||||||
in_whitespace
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return out_whitespace;
|
|
||||||
|
|
||||||
fn is_in_whitespace(ch: char, mode: CompressionMode) -> bool {
|
|
||||||
match (ch, mode) {
|
|
||||||
(' ', _) => true,
|
|
||||||
('\t', _) => true,
|
|
||||||
('\n', CompressionMode::CompressWhitespaceNewline) => true,
|
|
||||||
(_, _) => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_discardable_char(ch: char, mode: CompressionMode) -> bool {
|
|
||||||
if is_always_discardable_char(ch) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
match mode {
|
|
||||||
CompressionMode::DiscardNewline | CompressionMode::CompressWhitespaceNewline => {
|
|
||||||
ch == '\n'
|
|
||||||
},
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_always_discardable_char(ch: char) -> bool {
|
|
||||||
// TODO: check for soft hyphens.
|
|
||||||
is_bidi_control(ch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn float_to_fixed(before: usize, f: f64) -> i32 {
|
pub fn float_to_fixed(before: usize, f: f64) -> i32 {
|
||||||
((1i32 << before) as f64 * f) as i32
|
((1i32 << before) as f64 * f) as i32
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ publish = false
|
||||||
[lib]
|
[lib]
|
||||||
name = "layout_2013"
|
name = "layout_2013"
|
||||||
path = "lib.rs"
|
path = "lib.rs"
|
||||||
test = false
|
test = true
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
|
@ -21,7 +21,6 @@ use euclid::default::{Point2D, Rect, SideOffsets2D as UntypedSideOffsets2D, Size
|
||||||
use euclid::{rect, SideOffsets2D};
|
use euclid::{rect, SideOffsets2D};
|
||||||
use fnv::FnvHashMap;
|
use fnv::FnvHashMap;
|
||||||
use gfx::text::glyph::ByteIndex;
|
use gfx::text::glyph::ByteIndex;
|
||||||
use gfx::text::TextRun;
|
|
||||||
use ipc_channel::ipc;
|
use ipc_channel::ipc;
|
||||||
use log::{debug, warn};
|
use log::{debug, warn};
|
||||||
use net_traits::image_cache::UsePlaceholder;
|
use net_traits::image_cache::UsePlaceholder;
|
||||||
|
@ -73,6 +72,7 @@ use crate::fragment::{
|
||||||
use crate::inline::InlineFragmentNodeFlags;
|
use crate::inline::InlineFragmentNodeFlags;
|
||||||
use crate::model::MaybeAuto;
|
use crate::model::MaybeAuto;
|
||||||
use crate::table_cell::CollapsedBordersForCell;
|
use crate::table_cell::CollapsedBordersForCell;
|
||||||
|
use crate::text_run::TextRun;
|
||||||
|
|
||||||
static THREAD_TINT_COLORS: [ColorF; 8] = [
|
static THREAD_TINT_COLORS: [ColorF; 8] = [
|
||||||
ColorF {
|
ColorF {
|
||||||
|
|
|
@ -16,7 +16,6 @@ use bitflags::bitflags;
|
||||||
use canvas_traits::canvas::{CanvasId, CanvasMsg};
|
use canvas_traits::canvas::{CanvasId, CanvasMsg};
|
||||||
use euclid::default::{Point2D, Rect, Size2D, Vector2D};
|
use euclid::default::{Point2D, Rect, Size2D, Vector2D};
|
||||||
use gfx::text::glyph::ByteIndex;
|
use gfx::text::glyph::ByteIndex;
|
||||||
use gfx::text::text_run::{TextRun, TextRunSlice};
|
|
||||||
use html5ever::{local_name, namespace_url, ns};
|
use html5ever::{local_name, namespace_url, ns};
|
||||||
use ipc_channel::ipc::IpcSender;
|
use ipc_channel::ipc::IpcSender;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
|
@ -72,6 +71,7 @@ use crate::model::{
|
||||||
self, style_length, IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto, SizeConstraint,
|
self, style_length, IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto, SizeConstraint,
|
||||||
};
|
};
|
||||||
use crate::text::TextRunScanner;
|
use crate::text::TextRunScanner;
|
||||||
|
use crate::text_run::{TextRun, TextRunSlice};
|
||||||
use crate::wrapper::ThreadSafeLayoutNodeHelpers;
|
use crate::wrapper::ThreadSafeLayoutNodeHelpers;
|
||||||
use crate::{text, ServoArc};
|
use crate::{text, ServoArc};
|
||||||
|
|
||||||
|
|
|
@ -1502,7 +1502,9 @@ impl Flow for InlineFlow {
|
||||||
.union_nonbreaking_inline(&intrinsic_sizes_for_fragment)
|
.union_nonbreaking_inline(&intrinsic_sizes_for_fragment)
|
||||||
},
|
},
|
||||||
(
|
(
|
||||||
WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::PreserveBreaks,
|
WhiteSpaceCollapse::Preserve |
|
||||||
|
WhiteSpaceCollapse::PreserveBreaks |
|
||||||
|
WhiteSpaceCollapse::BreakSpaces,
|
||||||
TextWrapMode::Nowrap,
|
TextWrapMode::Nowrap,
|
||||||
) => {
|
) => {
|
||||||
intrinsic_sizes_for_nonbroken_run
|
intrinsic_sizes_for_nonbroken_run
|
||||||
|
@ -1520,7 +1522,9 @@ impl Flow for InlineFlow {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(
|
(
|
||||||
WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::PreserveBreaks,
|
WhiteSpaceCollapse::Preserve |
|
||||||
|
WhiteSpaceCollapse::PreserveBreaks |
|
||||||
|
WhiteSpaceCollapse::BreakSpaces,
|
||||||
TextWrapMode::Wrap,
|
TextWrapMode::Wrap,
|
||||||
) => {
|
) => {
|
||||||
// Flush the intrinsic sizes we were gathering up for the nonbroken run, if
|
// Flush the intrinsic sizes we were gathering up for the nonbroken run, if
|
||||||
|
|
|
@ -36,6 +36,7 @@ mod table_row;
|
||||||
mod table_rowgroup;
|
mod table_rowgroup;
|
||||||
mod table_wrapper;
|
mod table_wrapper;
|
||||||
mod text;
|
mod text;
|
||||||
|
mod text_run;
|
||||||
pub mod traversal;
|
pub mod traversal;
|
||||||
pub mod wrapper;
|
pub mod wrapper;
|
||||||
|
|
||||||
|
|
|
@ -12,8 +12,7 @@ use app_units::Au;
|
||||||
use gfx::font::{self, FontMetrics, FontRef, RunMetrics, ShapingFlags, ShapingOptions};
|
use gfx::font::{self, FontMetrics, FontRef, RunMetrics, ShapingFlags, ShapingOptions};
|
||||||
use gfx::font_cache_thread::FontIdentifier;
|
use gfx::font_cache_thread::FontIdentifier;
|
||||||
use gfx::text::glyph::ByteIndex;
|
use gfx::text::glyph::ByteIndex;
|
||||||
use gfx::text::text_run::TextRun;
|
use gfx::text::util::is_bidi_control;
|
||||||
use gfx::text::util::{self, CompressionMode};
|
|
||||||
use log::{debug, warn};
|
use log::{debug, warn};
|
||||||
use range::Range;
|
use range::Range;
|
||||||
use style::computed_values::text_rendering::T as TextRendering;
|
use style::computed_values::text_rendering::T as TextRendering;
|
||||||
|
@ -35,6 +34,7 @@ use crate::fragment::{
|
||||||
};
|
};
|
||||||
use crate::inline::{InlineFragmentNodeFlags, InlineFragments};
|
use crate::inline::{InlineFragmentNodeFlags, InlineFragments};
|
||||||
use crate::linked_list::split_off_head;
|
use crate::linked_list::split_off_head;
|
||||||
|
use crate::text_run::TextRun;
|
||||||
|
|
||||||
/// Returns the concatenated text of a list of unscanned text fragments.
|
/// Returns the concatenated text of a list of unscanned text fragments.
|
||||||
fn text(fragments: &LinkedList<Fragment>) -> String {
|
fn text(fragments: &LinkedList<Fragment>) -> String {
|
||||||
|
@ -192,7 +192,9 @@ impl TextRunScanner {
|
||||||
font_group = font_context.font_group(font_style);
|
font_group = font_context.font_group(font_style);
|
||||||
compression = match in_fragment.white_space_collapse() {
|
compression = match in_fragment.white_space_collapse() {
|
||||||
WhiteSpaceCollapse::Collapse => CompressionMode::CompressWhitespaceNewline,
|
WhiteSpaceCollapse::Collapse => CompressionMode::CompressWhitespaceNewline,
|
||||||
WhiteSpaceCollapse::Preserve => CompressionMode::CompressNone,
|
WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces => {
|
||||||
|
CompressionMode::CompressNone
|
||||||
|
},
|
||||||
WhiteSpaceCollapse::PreserveBreaks => CompressionMode::CompressWhitespace,
|
WhiteSpaceCollapse::PreserveBreaks => CompressionMode::CompressWhitespace,
|
||||||
};
|
};
|
||||||
text_transform = inherited_text_style.text_transform;
|
text_transform = inherited_text_style.text_transform;
|
||||||
|
@ -712,7 +714,7 @@ impl RunMapping {
|
||||||
) {
|
) {
|
||||||
let was_empty = *start_position == end_position;
|
let was_empty = *start_position == end_position;
|
||||||
let old_byte_length = run_info.text.len();
|
let old_byte_length = run_info.text.len();
|
||||||
*last_whitespace = util::transform_text(
|
*last_whitespace = transform_text(
|
||||||
&text[(*start_position)..end_position],
|
&text[(*start_position)..end_position],
|
||||||
compression,
|
compression,
|
||||||
*last_whitespace,
|
*last_whitespace,
|
||||||
|
@ -828,3 +830,181 @@ fn is_compatible(a: Script, b: Script) -> bool {
|
||||||
fn is_specific(script: Script) -> bool {
|
fn is_specific(script: Script) -> bool {
|
||||||
script != Script::Common && script != Script::Inherited
|
script != Script::Common && script != Script::Inherited
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub enum CompressionMode {
|
||||||
|
CompressNone,
|
||||||
|
CompressWhitespace,
|
||||||
|
CompressWhitespaceNewline,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ported from Gecko's nsTextFrameUtils::TransformText.
|
||||||
|
//
|
||||||
|
// High level TODOs:
|
||||||
|
//
|
||||||
|
// * Issue #113: consider incoming text state (arabic, etc)
|
||||||
|
// and propagate outgoing text state (dual of above)
|
||||||
|
//
|
||||||
|
// * Issue #114: record skipped and kept chars for mapping original to new text
|
||||||
|
//
|
||||||
|
// * Untracked: various edge cases for bidi, CJK, etc.
|
||||||
|
pub fn transform_text(
|
||||||
|
text: &str,
|
||||||
|
mode: CompressionMode,
|
||||||
|
incoming_whitespace: bool,
|
||||||
|
output_text: &mut String,
|
||||||
|
) -> bool {
|
||||||
|
let out_whitespace = match mode {
|
||||||
|
CompressionMode::CompressNone => {
|
||||||
|
for ch in text.chars() {
|
||||||
|
if is_discardable_char(ch, mode) {
|
||||||
|
// TODO: record skipped char
|
||||||
|
} else {
|
||||||
|
// TODO: record kept char
|
||||||
|
if ch == '\t' {
|
||||||
|
// TODO: set "has tab" flag
|
||||||
|
}
|
||||||
|
output_text.push(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
},
|
||||||
|
|
||||||
|
CompressionMode::CompressWhitespace | CompressionMode::CompressWhitespaceNewline => {
|
||||||
|
let mut in_whitespace: bool = incoming_whitespace;
|
||||||
|
for ch in text.chars() {
|
||||||
|
// TODO: discard newlines between CJK chars
|
||||||
|
let mut next_in_whitespace: bool = is_in_whitespace(ch, mode);
|
||||||
|
|
||||||
|
if !next_in_whitespace {
|
||||||
|
if is_always_discardable_char(ch) {
|
||||||
|
// revert whitespace setting, since this char was discarded
|
||||||
|
next_in_whitespace = in_whitespace;
|
||||||
|
// TODO: record skipped char
|
||||||
|
} else {
|
||||||
|
// TODO: record kept char
|
||||||
|
output_text.push(ch);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* next_in_whitespace; possibly add a space char */
|
||||||
|
if in_whitespace {
|
||||||
|
// TODO: record skipped char
|
||||||
|
} else {
|
||||||
|
// TODO: record kept char
|
||||||
|
output_text.push(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// save whitespace context for next char
|
||||||
|
in_whitespace = next_in_whitespace;
|
||||||
|
} /* /for str::each_char */
|
||||||
|
in_whitespace
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return out_whitespace;
|
||||||
|
|
||||||
|
fn is_in_whitespace(ch: char, mode: CompressionMode) -> bool {
|
||||||
|
match (ch, mode) {
|
||||||
|
(' ', _) => true,
|
||||||
|
('\t', _) => true,
|
||||||
|
('\n', CompressionMode::CompressWhitespaceNewline) => true,
|
||||||
|
(_, _) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_discardable_char(ch: char, mode: CompressionMode) -> bool {
|
||||||
|
if is_always_discardable_char(ch) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
match mode {
|
||||||
|
CompressionMode::CompressWhitespaceNewline => ch == '\n',
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_always_discardable_char(ch: char) -> bool {
|
||||||
|
// TODO: check for soft hyphens.
|
||||||
|
is_bidi_control(ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_transform_compress_none() {
|
||||||
|
let test_strs = [
|
||||||
|
" foo bar",
|
||||||
|
"foo bar ",
|
||||||
|
"foo\n bar",
|
||||||
|
"foo \nbar",
|
||||||
|
" foo bar \nbaz",
|
||||||
|
"foo bar baz",
|
||||||
|
"foobarbaz\n\n",
|
||||||
|
];
|
||||||
|
|
||||||
|
let mode = CompressionMode::CompressNone;
|
||||||
|
for &test in test_strs.iter() {
|
||||||
|
let mut trimmed_str = String::new();
|
||||||
|
transform_text(test, mode, true, &mut trimmed_str);
|
||||||
|
assert_eq!(trimmed_str, test)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_transform_compress_whitespace() {
|
||||||
|
let test_strs = [
|
||||||
|
(" foo bar", "foo bar"),
|
||||||
|
("foo bar ", "foo bar "),
|
||||||
|
("foo\n bar", "foo\n bar"),
|
||||||
|
("foo \nbar", "foo \nbar"),
|
||||||
|
(" foo bar \nbaz", "foo bar \nbaz"),
|
||||||
|
("foo bar baz", "foo bar baz"),
|
||||||
|
("foobarbaz\n\n", "foobarbaz\n\n"),
|
||||||
|
];
|
||||||
|
|
||||||
|
let mode = CompressionMode::CompressWhitespace;
|
||||||
|
for &(test, oracle) in test_strs.iter() {
|
||||||
|
let mut trimmed_str = String::new();
|
||||||
|
transform_text(test, mode, true, &mut trimmed_str);
|
||||||
|
assert_eq!(&*trimmed_str, oracle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_transform_compress_whitespace_newline() {
|
||||||
|
let test_strs = vec![
|
||||||
|
(" foo bar", "foo bar"),
|
||||||
|
("foo bar ", "foo bar "),
|
||||||
|
("foo\n bar", "foo bar"),
|
||||||
|
("foo \nbar", "foo bar"),
|
||||||
|
(" foo bar \nbaz", "foo bar baz"),
|
||||||
|
("foo bar baz", "foo bar baz"),
|
||||||
|
("foobarbaz\n\n", "foobarbaz "),
|
||||||
|
];
|
||||||
|
|
||||||
|
let mode = CompressionMode::CompressWhitespaceNewline;
|
||||||
|
for &(test, oracle) in test_strs.iter() {
|
||||||
|
let mut trimmed_str = String::new();
|
||||||
|
transform_text(test, mode, true, &mut trimmed_str);
|
||||||
|
assert_eq!(&*trimmed_str, oracle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_transform_compress_whitespace_newline_no_incoming() {
|
||||||
|
let test_strs = [
|
||||||
|
(" foo bar", " foo bar"),
|
||||||
|
("\nfoo bar", " foo bar"),
|
||||||
|
("foo bar ", "foo bar "),
|
||||||
|
("foo\n bar", "foo bar"),
|
||||||
|
("foo \nbar", "foo bar"),
|
||||||
|
(" foo bar \nbaz", " foo bar baz"),
|
||||||
|
("foo bar baz", "foo bar baz"),
|
||||||
|
("foobarbaz\n\n", "foobarbaz "),
|
||||||
|
];
|
||||||
|
|
||||||
|
let mode = CompressionMode::CompressWhitespaceNewline;
|
||||||
|
for &(test, oracle) in test_strs.iter() {
|
||||||
|
let mut trimmed_str = String::new();
|
||||||
|
transform_text(test, mode, false, &mut trimmed_str);
|
||||||
|
assert_eq!(trimmed_str, oracle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3,11 +3,13 @@
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::cmp::{max, Ordering};
|
use std::cmp::max;
|
||||||
use std::slice::Iter;
|
use std::slice::Iter;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
|
use gfx::font::{FontMetrics, FontRef, RunMetrics, ShapingFlags, ShapingOptions};
|
||||||
|
use gfx::text::glyph::{ByteIndex, GlyphRun, GlyphStore};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use range::Range;
|
use range::Range;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -16,9 +18,6 @@ use unicode_bidi as bidi;
|
||||||
use webrender_api::FontInstanceKey;
|
use webrender_api::FontInstanceKey;
|
||||||
use xi_unicode::LineBreakLeafIter;
|
use xi_unicode::LineBreakLeafIter;
|
||||||
|
|
||||||
use crate::font::{FontMetrics, FontRef, RunMetrics, ShapingFlags, ShapingOptions};
|
|
||||||
use crate::text::glyph::{ByteIndex, GlyphStore};
|
|
||||||
|
|
||||||
thread_local! {
|
thread_local! {
|
||||||
static INDEX_OF_FIRST_GLYPH_RUN_CACHE: Cell<Option<(*const TextRun, ByteIndex, usize)>> =
|
static INDEX_OF_FIRST_GLYPH_RUN_CACHE: Cell<Option<(*const TextRun, ByteIndex, usize)>> =
|
||||||
Cell::new(None)
|
Cell::new(None)
|
||||||
|
@ -51,15 +50,6 @@ impl Drop for TextRun {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A single series of glyphs within a text run.
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
|
||||||
pub struct GlyphRun {
|
|
||||||
/// The glyphs.
|
|
||||||
pub glyph_store: Arc<GlyphStore>,
|
|
||||||
/// The byte range of characters in the containing run.
|
|
||||||
pub range: Range<ByteIndex>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct NaturalWordSliceIterator<'a> {
|
pub struct NaturalWordSliceIterator<'a> {
|
||||||
glyphs: &'a [GlyphRun],
|
glyphs: &'a [GlyphRun],
|
||||||
index: usize,
|
index: usize,
|
||||||
|
@ -67,18 +57,6 @@ pub struct NaturalWordSliceIterator<'a> {
|
||||||
reverse: bool,
|
reverse: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GlyphRun {
|
|
||||||
fn compare(&self, key: &ByteIndex) -> Ordering {
|
|
||||||
if *key < self.range.begin() {
|
|
||||||
Ordering::Greater
|
|
||||||
} else if *key >= self.range.end() {
|
|
||||||
Ordering::Less
|
|
||||||
} else {
|
|
||||||
Ordering::Equal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A "slice" of a text run is a series of contiguous glyphs that all belong to the same glyph
|
/// A "slice" of a text run is a series of contiguous glyphs that all belong to the same glyph
|
||||||
/// store. Line breaking strategies yield these.
|
/// store. Line breaking strategies yield these.
|
||||||
pub struct TextRunSlice<'a> {
|
pub struct TextRunSlice<'a> {
|
|
@ -1361,7 +1361,7 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
|
||||||
inline_size: Length,
|
inline_size: Length,
|
||||||
flags: SegmentContentFlags,
|
flags: SegmentContentFlags,
|
||||||
) {
|
) {
|
||||||
if flags.is_collapsible_whitespace() || flags.is_wrappable_whitespace() {
|
if flags.is_collapsible_whitespace() || flags.is_wrappable_and_hangable() {
|
||||||
self.current_line_segment.trailing_whitespace_size = inline_size;
|
self.current_line_segment.trailing_whitespace_size = inline_size;
|
||||||
} else {
|
} else {
|
||||||
self.current_line_segment.trailing_whitespace_size = Length::zero();
|
self.current_line_segment.trailing_whitespace_size = Length::zero();
|
||||||
|
@ -1497,7 +1497,7 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
|
||||||
bitflags! {
|
bitflags! {
|
||||||
pub struct SegmentContentFlags: u8 {
|
pub struct SegmentContentFlags: u8 {
|
||||||
const COLLAPSIBLE_WHITESPACE = 0b00000001;
|
const COLLAPSIBLE_WHITESPACE = 0b00000001;
|
||||||
const WRAPPABLE_WHITESPACE = 0b00000010;
|
const WRAPPABLE_AND_HANGABLE_WHITESPACE = 0b00000010;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1506,19 +1506,30 @@ impl SegmentContentFlags {
|
||||||
self.contains(Self::COLLAPSIBLE_WHITESPACE)
|
self.contains(Self::COLLAPSIBLE_WHITESPACE)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_wrappable_whitespace(&self) -> bool {
|
fn is_wrappable_and_hangable(&self) -> bool {
|
||||||
self.contains(Self::WRAPPABLE_WHITESPACE)
|
self.contains(Self::WRAPPABLE_AND_HANGABLE_WHITESPACE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&InheritedText> for SegmentContentFlags {
|
impl From<&InheritedText> for SegmentContentFlags {
|
||||||
fn from(style_text: &InheritedText) -> Self {
|
fn from(style_text: &InheritedText) -> Self {
|
||||||
let mut flags = Self::empty();
|
let mut flags = Self::empty();
|
||||||
if style_text.white_space_collapse != WhiteSpaceCollapse::Preserve {
|
|
||||||
|
// White-space with `white-space-collapse: break-spaces` or `white-space-collapse: preserve`
|
||||||
|
// never collapses.
|
||||||
|
if !matches!(
|
||||||
|
style_text.white_space_collapse,
|
||||||
|
WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
|
||||||
|
) {
|
||||||
flags.insert(Self::COLLAPSIBLE_WHITESPACE);
|
flags.insert(Self::COLLAPSIBLE_WHITESPACE);
|
||||||
}
|
}
|
||||||
if style_text.text_wrap_mode == TextWrapMode::Wrap {
|
|
||||||
flags.insert(Self::WRAPPABLE_WHITESPACE);
|
// White-space with `white-space-collapse: break-spaces` never hangs and always takes up
|
||||||
|
// space.
|
||||||
|
if style_text.text_wrap_mode == TextWrapMode::Wrap &&
|
||||||
|
style_text.white_space_collapse != WhiteSpaceCollapse::BreakSpaces
|
||||||
|
{
|
||||||
|
flags.insert(Self::WRAPPABLE_AND_HANGABLE_WHITESPACE);
|
||||||
}
|
}
|
||||||
flags
|
flags
|
||||||
}
|
}
|
||||||
|
|
|
@ -152,9 +152,10 @@ pub(super) struct TextRunLineItem {
|
||||||
|
|
||||||
impl TextRunLineItem {
|
impl TextRunLineItem {
|
||||||
fn trim_whitespace_at_end(&mut self, whitespace_trimmed: &mut Length) -> bool {
|
fn trim_whitespace_at_end(&mut self, whitespace_trimmed: &mut Length) -> bool {
|
||||||
if self.parent_style.get_inherited_text().white_space_collapse ==
|
if matches!(
|
||||||
WhiteSpaceCollapse::Preserve
|
self.parent_style.get_inherited_text().white_space_collapse,
|
||||||
{
|
WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
|
||||||
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,9 +178,10 @@ impl TextRunLineItem {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trim_whitespace_at_start(&mut self, whitespace_trimmed: &mut Length) -> bool {
|
fn trim_whitespace_at_start(&mut self, whitespace_trimmed: &mut Length) -> bool {
|
||||||
if self.parent_style.get_inherited_text().white_space_collapse ==
|
if matches!(
|
||||||
WhiteSpaceCollapse::Preserve
|
self.parent_style.get_inherited_text().white_space_collapse,
|
||||||
{
|
WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
|
||||||
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ use app_units::Au;
|
||||||
use gfx::font::{FontRef, ShapingFlags, ShapingOptions};
|
use gfx::font::{FontRef, ShapingFlags, ShapingOptions};
|
||||||
use gfx::font_cache_thread::FontCacheThread;
|
use gfx::font_cache_thread::FontCacheThread;
|
||||||
use gfx::font_context::FontContext;
|
use gfx::font_context::FontContext;
|
||||||
use gfx::text::text_run::GlyphRun;
|
use gfx::text::glyph::GlyphRun;
|
||||||
use gfx_traits::ByteIndex;
|
use gfx_traits::ByteIndex;
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use range::Range;
|
use range::Range;
|
||||||
|
@ -18,7 +18,10 @@ use servo_arc::Arc;
|
||||||
use style::computed_values::text_rendering::T as TextRendering;
|
use style::computed_values::text_rendering::T as TextRendering;
|
||||||
use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse;
|
use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse;
|
||||||
use style::computed_values::word_break::T as WordBreak;
|
use style::computed_values::word_break::T as WordBreak;
|
||||||
|
use style::properties::style_structs::InheritedText;
|
||||||
use style::properties::ComputedValues;
|
use style::properties::ComputedValues;
|
||||||
|
use style::str::char_is_whitespace;
|
||||||
|
use style::values::computed::OverflowWrap;
|
||||||
use style::values::specified::text::TextTransformCase;
|
use style::values::specified::text::TextTransformCase;
|
||||||
use style::values::specified::TextTransform;
|
use style::values::specified::TextTransform;
|
||||||
use unicode_script::Script;
|
use unicode_script::Script;
|
||||||
|
@ -256,11 +259,10 @@ impl TextRun {
|
||||||
script: segment.script,
|
script: segment.script,
|
||||||
flags,
|
flags,
|
||||||
};
|
};
|
||||||
(segment.runs, segment.break_at_start) =
|
(segment.runs, segment.break_at_start) = break_and_shape(
|
||||||
gfx::text::text_run::TextRun::break_and_shape(
|
|
||||||
font,
|
font,
|
||||||
&self.text
|
&self.text[segment.range.begin().0 as usize..segment.range.end().0 as usize],
|
||||||
[segment.range.begin().0 as usize..segment.range.end().0 as usize],
|
&inherited_text_style,
|
||||||
&shaping_options,
|
&shaping_options,
|
||||||
linebreaker,
|
linebreaker,
|
||||||
);
|
);
|
||||||
|
@ -590,8 +592,17 @@ where
|
||||||
// > characters are considered collapsible
|
// > characters are considered collapsible
|
||||||
// If whitespace is not considered collapsible, it is preserved entirely, which
|
// If whitespace is not considered collapsible, it is preserved entirely, which
|
||||||
// means that we can simply return the input string exactly.
|
// means that we can simply return the input string exactly.
|
||||||
if self.white_space_collapse == WhiteSpaceCollapse::Preserve {
|
if self.white_space_collapse == WhiteSpaceCollapse::Preserve ||
|
||||||
return self.char_iterator.next();
|
self.white_space_collapse == WhiteSpaceCollapse::BreakSpaces
|
||||||
|
{
|
||||||
|
// From <https://drafts.csswg.org/css-text-3/#white-space-processing>:
|
||||||
|
// > Carriage returns (U+000D) are treated identically to spaces (U+0020) in all respects.
|
||||||
|
//
|
||||||
|
// In the non-preserved case these are converted to space below.
|
||||||
|
return match self.char_iterator.next() {
|
||||||
|
Some('\r') => Some(' '),
|
||||||
|
next => next,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(character) = self.character_pending_to_return.take() {
|
if let Some(character) = self.character_pending_to_return.take() {
|
||||||
|
@ -830,3 +841,129 @@ where
|
||||||
return Some((character, self.next_character.clone()));
|
return Some((character, self.next_character.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn break_and_shape(
|
||||||
|
font: FontRef,
|
||||||
|
text: &str,
|
||||||
|
text_style: &InheritedText,
|
||||||
|
shaping_options: &ShapingOptions,
|
||||||
|
breaker: &mut Option<LineBreakLeafIter>,
|
||||||
|
) -> (Vec<GlyphRun>, bool) {
|
||||||
|
let mut glyphs = vec![];
|
||||||
|
|
||||||
|
if breaker.is_none() {
|
||||||
|
if text.is_empty() {
|
||||||
|
return (glyphs, true);
|
||||||
|
}
|
||||||
|
*breaker = Some(LineBreakLeafIter::new(text, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
let breaker = breaker.as_mut().unwrap();
|
||||||
|
|
||||||
|
let mut push_range = |range: &std::ops::Range<usize>, options: &ShapingOptions| {
|
||||||
|
glyphs.push(GlyphRun {
|
||||||
|
glyph_store: font.shape_text(&text[range.clone()], options),
|
||||||
|
range: Range::new(
|
||||||
|
ByteIndex(range.start as isize),
|
||||||
|
ByteIndex(range.len() as isize),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
let can_break_anywhere = text_style.word_break == WordBreak::BreakAll ||
|
||||||
|
text_style.overflow_wrap == OverflowWrap::Anywhere ||
|
||||||
|
text_style.overflow_wrap == OverflowWrap::BreakWord;
|
||||||
|
|
||||||
|
let mut break_at_zero = false;
|
||||||
|
let mut last_slice_end = 0;
|
||||||
|
while last_slice_end != text.len() {
|
||||||
|
let (break_index, _is_hard_break) = breaker.next(text);
|
||||||
|
if break_index == 0 {
|
||||||
|
break_at_zero = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extend the slice to the next UAX#14 line break opportunity.
|
||||||
|
let mut slice = last_slice_end..break_index;
|
||||||
|
let word = &text[slice.clone()];
|
||||||
|
|
||||||
|
// Split off any trailing whitespace into a separate glyph run.
|
||||||
|
let mut whitespace = slice.end..slice.end;
|
||||||
|
let mut rev_char_indices = word.char_indices().rev().peekable();
|
||||||
|
let ends_with_newline = rev_char_indices.peek().map_or(false, |&(_, c)| c == '\n');
|
||||||
|
if let Some((first_white_space_index, first_white_space_character)) = rev_char_indices
|
||||||
|
.take_while(|&(_, c)| char_is_whitespace(c))
|
||||||
|
.last()
|
||||||
|
{
|
||||||
|
whitespace.start = slice.start + first_white_space_index;
|
||||||
|
|
||||||
|
// If line breaking for a piece of text that has `white-space-collapse: break-spaces` there
|
||||||
|
// is a line break opportunity *after* every preserved space, but not before. This means
|
||||||
|
// that we should not split off the first whitespace, unless that white-space is a preserved
|
||||||
|
// newline.
|
||||||
|
//
|
||||||
|
// An exception to this is if the style tells us that we can break in the middle of words.
|
||||||
|
if text_style.white_space_collapse == WhiteSpaceCollapse::BreakSpaces &&
|
||||||
|
first_white_space_character != '\n' &&
|
||||||
|
!can_break_anywhere
|
||||||
|
{
|
||||||
|
whitespace.start += first_white_space_character.len_utf8();
|
||||||
|
}
|
||||||
|
|
||||||
|
slice.end = whitespace.start;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there's no whitespace and `word-break` is set to `keep-all`, try increasing the slice.
|
||||||
|
// TODO: This should only happen for CJK text.
|
||||||
|
let can_break_anywhere = text_style.word_break == WordBreak::BreakAll ||
|
||||||
|
text_style.overflow_wrap == OverflowWrap::Anywhere ||
|
||||||
|
text_style.overflow_wrap == OverflowWrap::BreakWord;
|
||||||
|
if whitespace.is_empty() &&
|
||||||
|
break_index != text.len() &&
|
||||||
|
text_style.word_break == WordBreak::KeepAll &&
|
||||||
|
!can_break_anywhere
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only advance the last_slice_end if we are not going to try to expand the slice.
|
||||||
|
last_slice_end = break_index;
|
||||||
|
|
||||||
|
// Push the non-whitespace part of the range.
|
||||||
|
if !slice.is_empty() {
|
||||||
|
push_range(&slice, shaping_options);
|
||||||
|
}
|
||||||
|
|
||||||
|
if whitespace.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut options = *shaping_options;
|
||||||
|
options
|
||||||
|
.flags
|
||||||
|
.insert(ShapingFlags::IS_WHITESPACE_SHAPING_FLAG);
|
||||||
|
|
||||||
|
// If `white-space-collapse: break-spaces` is active, insert a line breaking opportunity
|
||||||
|
// between each white space character in the white space that we trimmed off.
|
||||||
|
if text_style.white_space_collapse == WhiteSpaceCollapse::BreakSpaces {
|
||||||
|
let start_index = whitespace.start;
|
||||||
|
for (index, character) in text[whitespace].char_indices() {
|
||||||
|
let index = start_index + index;
|
||||||
|
push_range(&(index..index + character.len_utf8()), &options);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The breaker breaks after every newline, so either there is none,
|
||||||
|
// or there is exactly one at the very end. In the latter case,
|
||||||
|
// split it into a different run. That's because shaping considers
|
||||||
|
// a newline to have the same advance as a space, but during layout
|
||||||
|
// we want to treat the newline as having no advance.
|
||||||
|
if ends_with_newline && whitespace.len() > 1 {
|
||||||
|
push_range(&(whitespace.start..whitespace.end - 1), &options);
|
||||||
|
push_range(&(whitespace.end - 1..whitespace.end), &options);
|
||||||
|
} else {
|
||||||
|
push_range(&whitespace, &options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(glyphs, break_at_zero)
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
[white-space-collapse-computed.html]
|
|
||||||
[Property white-space-collapse value 'break-spaces']
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[white-space-collapse-valid.html]
|
|
||||||
[e.style['white-space-collapse'\] = "break-spaces" should set the property value]
|
|
||||||
expected: FAIL
|
|
|
@ -1,7 +1,3 @@
|
||||||
[white-space-computed.html]
|
[white-space-computed.html]
|
||||||
[Property white-space value 'break-spaces' computes to 'break-spaces']
|
[Property white-space value 'break-spaces' computes to 'break-spaces']
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Property white-space value 'break-spaces']
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,4 @@
|
||||||
[white-space-shorthand.html]
|
[white-space-shorthand.html]
|
||||||
[e.style['white-space'\] = "break-spaces" should set the property value]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Property white-space value 'break-spaces']
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[e.style['white-space'\] = "break-spaces wrap" should set the property value]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Property white-space value 'break-spaces wrap']
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[e.style['white-space'\] = "wrap break-spaces" should set the property value]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Property white-space value 'wrap break-spaces']
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[e.style['white-space'\] = "balance" should set the property value]
|
[e.style['white-space'\] = "balance" should set the property value]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
[white-space-valid.html]
|
|
||||||
[e.style['white-space'\] = "break-spaces" should set the property value]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
[break-spaces-tab-001.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[break-spaces-tab-002.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[trailing-space-and-text-alignment-004.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[white-space-letter-spacing-001.html]
|
|
||||||
expected: FAIL
|
|
|
@ -35,24 +35,6 @@
|
||||||
[Property text-wrap inherits]
|
[Property text-wrap inherits]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Property overflow-wrap has initial value normal]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Property overflow-wrap inherits]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Property word-break has initial value normal]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Property word-break inherits]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Property word-wrap has initial value normal]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Property word-wrap inherits]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Property text-wrap-style has initial value auto]
|
[Property text-wrap-style has initial value auto]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
[overflow-wrap-anywhere-003.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[overflow-wrap-break-word-003.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[overflow-wrap-break-word-006.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[overflow-wrap-break-word-008.html]
|
|
||||||
expected: FAIL
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
[overflow-wrap-normal-keep-all-001.html]
|
||||||
|
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
||||||
[word-wrap-alias.html]
|
|
||||||
[word-wrap should be defined as an alias of overflow-wrap]
|
|
||||||
expected: FAIL
|
|
|
@ -1,9 +0,0 @@
|
||||||
[overflow-wrap-computed.html]
|
|
||||||
[Property overflow-wrap value 'normal']
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Property overflow-wrap value 'break-word']
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Property overflow-wrap value 'anywhere']
|
|
||||||
expected: FAIL
|
|
|
@ -1,9 +0,0 @@
|
||||||
[overflow-wrap-valid.html]
|
|
||||||
[e.style['overflow-wrap'\] = "normal" should set the property value]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[e.style['overflow-wrap'\] = "break-word" should set the property value]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[e.style['overflow-wrap'\] = "anywhere" should set the property value]
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[white-space-collapse-computed.html]
|
|
||||||
[Property white-space-collapse value 'break-spaces']
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[white-space-collapse-valid.html]
|
|
||||||
[e.style['white-space-collapse'\] = "break-spaces" should set the property value]
|
|
||||||
expected: FAIL
|
|
|
@ -1,3 +0,0 @@
|
||||||
[white-space-computed.html]
|
|
||||||
[Property white-space value 'break-spaces']
|
|
||||||
expected: FAIL
|
|
|
@ -1,22 +1,4 @@
|
||||||
[white-space-shorthand.html]
|
[white-space-shorthand.html]
|
||||||
[e.style['white-space'\] = "break-spaces" should set the property value]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Property white-space value 'break-spaces']
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[e.style['white-space'\] = "break-spaces wrap" should set the property value]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Property white-space value 'break-spaces wrap']
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[e.style['white-space'\] = "wrap break-spaces" should set the property value]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Property white-space value 'wrap break-spaces']
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[e.style['white-space'\] = "balance" should set the property value]
|
[e.style['white-space'\] = "balance" should set the property value]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
[white-space-valid.html]
|
|
||||||
[e.style['white-space'\] = "break-spaces" should set the property value]
|
|
||||||
expected: FAIL
|
|
|
@ -1,13 +1,4 @@
|
||||||
[word-break-computed.html]
|
[word-break-computed.html]
|
||||||
[Property word-break value 'normal']
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Property word-break value 'keep-all']
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Property word-break value 'break-all']
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Property word-break value 'break-word']
|
[Property word-break value 'break-word']
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,4 @@
|
||||||
[word-break-valid.html]
|
[word-break-valid.html]
|
||||||
[e.style['word-break'\] = "normal" should set the property value]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[e.style['word-break'\] = "keep-all" should set the property value]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[e.style['word-break'\] = "break-all" should set the property value]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[e.style['word-break'\] = "break-word" should set the property value]
|
[e.style['word-break'\] = "break-word" should set the property value]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
[word-wrap-computed.html]
|
|
||||||
[Property word-wrap value 'normal']
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Property word-wrap value 'break-word']
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Property word-wrap value 'anywhere']
|
|
||||||
expected: FAIL
|
|
|
@ -1,9 +0,0 @@
|
||||||
[word-wrap-valid.html]
|
|
||||||
[e.style['word-wrap'\] = "normal" should set the property value]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[e.style['word-wrap'\] = "break-word" should set the property value]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[e.style['word-wrap'\] = "anywhere" should set the property value]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[break-spaces-001.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[break-spaces-003.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[break-spaces-004.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[break-spaces-005.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[break-spaces-007.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[break-spaces-010.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[break-spaces-011.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[break-spaces-051.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[break-spaces-052.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[break-spaces-before-first-char-001.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[break-spaces-before-first-char-008.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[break-spaces-before-first-char-009.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[break-spaces-before-first-char-010.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[break-spaces-before-first-char-011.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[break-spaces-before-first-char-012.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[break-spaces-before-first-char-013.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[break-spaces-before-first-char-016.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[break-spaces-before-first-char-017.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[break-spaces-before-first-char-018.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[break-spaces-newline-011.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[break-spaces-newline-012.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[break-spaces-newline-013.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[break-spaces-newline-014.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[break-spaces-newline-015.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[break-spaces-newline-016.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[break-spaces-tab-003.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[break-spaces-tab-004.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[break-spaces-with-overflow-wrap-003.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[break-spaces-with-overflow-wrap-004.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[break-spaces-with-overflow-wrap-005.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[break-spaces-with-overflow-wrap-006.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[break-spaces-with-overflow-wrap-007.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[break-spaces-with-overflow-wrap-008.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[break-spaces-with-overflow-wrap-009.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[break-spaces-with-overflow-wrap-010.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[hanging-whitespace-001.tentative.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[textarea-break-spaces-001.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[white-space-letter-spacing-001.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[ws-break-spaces-applies-to-001.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[ws-break-spaces-applies-to-002.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[ws-break-spaces-applies-to-003.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[ws-break-spaces-applies-to-005.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[ws-break-spaces-applies-to-006.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[ws-break-spaces-applies-to-007.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[ws-break-spaces-applies-to-008.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[ws-break-spaces-applies-to-009.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[ws-break-spaces-applies-to-010.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[ws-break-spaces-applies-to-011.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[ws-break-spaces-applies-to-014.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[word-break-keep-all-001.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[word-break-keep-all-002.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[word-break-keep-all-010.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[word-break-keep-all-006.htm]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[word-break-keep-all-007.htm]
|
|
||||||
expected: FAIL
|
|
Loading…
Add table
Add a link
Reference in a new issue