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]]
|
||||
name = "derive_common"
|
||||
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 = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
|
@ -3454,7 +3454,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets 0.48.5",
|
||||
"windows-targets 0.52.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3625,7 +3625,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "malloc_size_of"
|
||||
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 = [
|
||||
"accountable-refcell",
|
||||
"app_units",
|
||||
|
@ -5221,7 +5221,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "selectors"
|
||||
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 = [
|
||||
"bitflags 2.5.0",
|
||||
"cssparser",
|
||||
|
@ -5509,7 +5509,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "servo_arc"
|
||||
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 = [
|
||||
"nodrop",
|
||||
"serde",
|
||||
|
@ -5519,7 +5519,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "servo_atoms"
|
||||
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 = [
|
||||
"string_cache",
|
||||
"string_cache_codegen",
|
||||
|
@ -5717,7 +5717,7 @@ checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
|
|||
[[package]]
|
||||
name = "size_of_test"
|
||||
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 = [
|
||||
"static_assertions",
|
||||
]
|
||||
|
@ -5858,7 +5858,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
|||
[[package]]
|
||||
name = "static_prefs"
|
||||
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]]
|
||||
name = "strict-num"
|
||||
|
@ -5895,7 +5895,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "style"
|
||||
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 = [
|
||||
"app_units",
|
||||
"arrayvec",
|
||||
|
@ -5953,7 +5953,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "style_config"
|
||||
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 = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
@ -5961,7 +5961,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "style_derive"
|
||||
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 = [
|
||||
"darling",
|
||||
"derive_common",
|
||||
|
@ -5992,7 +5992,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "style_traits"
|
||||
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 = [
|
||||
"app_units",
|
||||
"bitflags 2.5.0",
|
||||
|
@ -6356,7 +6356,7 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
|||
[[package]]
|
||||
name = "to_shmem"
|
||||
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 = [
|
||||
"cssparser",
|
||||
"servo_arc",
|
||||
|
@ -6369,7 +6369,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "to_shmem_derive"
|
||||
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 = [
|
||||
"darling",
|
||||
"derive_common",
|
||||
|
|
|
@ -2,108 +2,7 @@
|
|||
* 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/. */
|
||||
|
||||
use gfx::text::util::{is_cjk, transform_text, CompressionMode};
|
||||
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
use gfx::text::util::is_cjk;
|
||||
|
||||
#[test]
|
||||
fn test_is_cjk() {
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use std::cmp::{Ordering, PartialOrd};
|
||||
use std::sync::Arc;
|
||||
use std::vec::Vec;
|
||||
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};
|
||||
|
||||
pub use crate::text::shaping::Shaper;
|
||||
pub use crate::text::text_run::TextRun;
|
||||
|
||||
pub mod glyph;
|
||||
pub mod shaping;
|
||||
pub mod text_run;
|
||||
pub mod util;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
|
|
|
@ -4,106 +4,6 @@
|
|||
|
||||
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 {
|
||||
((1i32 << before) as f64 * f) as i32
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ publish = false
|
|||
[lib]
|
||||
name = "layout_2013"
|
||||
path = "lib.rs"
|
||||
test = false
|
||||
test = true
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
|
|
|
@ -21,7 +21,6 @@ use euclid::default::{Point2D, Rect, SideOffsets2D as UntypedSideOffsets2D, Size
|
|||
use euclid::{rect, SideOffsets2D};
|
||||
use fnv::FnvHashMap;
|
||||
use gfx::text::glyph::ByteIndex;
|
||||
use gfx::text::TextRun;
|
||||
use ipc_channel::ipc;
|
||||
use log::{debug, warn};
|
||||
use net_traits::image_cache::UsePlaceholder;
|
||||
|
@ -73,6 +72,7 @@ use crate::fragment::{
|
|||
use crate::inline::InlineFragmentNodeFlags;
|
||||
use crate::model::MaybeAuto;
|
||||
use crate::table_cell::CollapsedBordersForCell;
|
||||
use crate::text_run::TextRun;
|
||||
|
||||
static THREAD_TINT_COLORS: [ColorF; 8] = [
|
||||
ColorF {
|
||||
|
|
|
@ -16,7 +16,6 @@ use bitflags::bitflags;
|
|||
use canvas_traits::canvas::{CanvasId, CanvasMsg};
|
||||
use euclid::default::{Point2D, Rect, Size2D, Vector2D};
|
||||
use gfx::text::glyph::ByteIndex;
|
||||
use gfx::text::text_run::{TextRun, TextRunSlice};
|
||||
use html5ever::{local_name, namespace_url, ns};
|
||||
use ipc_channel::ipc::IpcSender;
|
||||
use log::debug;
|
||||
|
@ -72,6 +71,7 @@ use crate::model::{
|
|||
self, style_length, IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto, SizeConstraint,
|
||||
};
|
||||
use crate::text::TextRunScanner;
|
||||
use crate::text_run::{TextRun, TextRunSlice};
|
||||
use crate::wrapper::ThreadSafeLayoutNodeHelpers;
|
||||
use crate::{text, ServoArc};
|
||||
|
||||
|
|
|
@ -1502,7 +1502,9 @@ impl Flow for InlineFlow {
|
|||
.union_nonbreaking_inline(&intrinsic_sizes_for_fragment)
|
||||
},
|
||||
(
|
||||
WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::PreserveBreaks,
|
||||
WhiteSpaceCollapse::Preserve |
|
||||
WhiteSpaceCollapse::PreserveBreaks |
|
||||
WhiteSpaceCollapse::BreakSpaces,
|
||||
TextWrapMode::Nowrap,
|
||||
) => {
|
||||
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,
|
||||
) => {
|
||||
// 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_wrapper;
|
||||
mod text;
|
||||
mod text_run;
|
||||
pub mod traversal;
|
||||
pub mod wrapper;
|
||||
|
||||
|
|
|
@ -12,8 +12,7 @@ use app_units::Au;
|
|||
use gfx::font::{self, FontMetrics, FontRef, RunMetrics, ShapingFlags, ShapingOptions};
|
||||
use gfx::font_cache_thread::FontIdentifier;
|
||||
use gfx::text::glyph::ByteIndex;
|
||||
use gfx::text::text_run::TextRun;
|
||||
use gfx::text::util::{self, CompressionMode};
|
||||
use gfx::text::util::is_bidi_control;
|
||||
use log::{debug, warn};
|
||||
use range::Range;
|
||||
use style::computed_values::text_rendering::T as TextRendering;
|
||||
|
@ -35,6 +34,7 @@ use crate::fragment::{
|
|||
};
|
||||
use crate::inline::{InlineFragmentNodeFlags, InlineFragments};
|
||||
use crate::linked_list::split_off_head;
|
||||
use crate::text_run::TextRun;
|
||||
|
||||
/// Returns the concatenated text of a list of unscanned text fragments.
|
||||
fn text(fragments: &LinkedList<Fragment>) -> String {
|
||||
|
@ -192,7 +192,9 @@ impl TextRunScanner {
|
|||
font_group = font_context.font_group(font_style);
|
||||
compression = match in_fragment.white_space_collapse() {
|
||||
WhiteSpaceCollapse::Collapse => CompressionMode::CompressWhitespaceNewline,
|
||||
WhiteSpaceCollapse::Preserve => CompressionMode::CompressNone,
|
||||
WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces => {
|
||||
CompressionMode::CompressNone
|
||||
},
|
||||
WhiteSpaceCollapse::PreserveBreaks => CompressionMode::CompressWhitespace,
|
||||
};
|
||||
text_transform = inherited_text_style.text_transform;
|
||||
|
@ -712,7 +714,7 @@ impl RunMapping {
|
|||
) {
|
||||
let was_empty = *start_position == end_position;
|
||||
let old_byte_length = run_info.text.len();
|
||||
*last_whitespace = util::transform_text(
|
||||
*last_whitespace = transform_text(
|
||||
&text[(*start_position)..end_position],
|
||||
compression,
|
||||
*last_whitespace,
|
||||
|
@ -828,3 +830,181 @@ fn is_compatible(a: Script, b: Script) -> bool {
|
|||
fn is_specific(script: Script) -> bool {
|
||||
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/. */
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::cmp::{max, Ordering};
|
||||
use std::cmp::max;
|
||||
use std::slice::Iter;
|
||||
use std::sync::Arc;
|
||||
|
||||
use app_units::Au;
|
||||
use gfx::font::{FontMetrics, FontRef, RunMetrics, ShapingFlags, ShapingOptions};
|
||||
use gfx::text::glyph::{ByteIndex, GlyphRun, GlyphStore};
|
||||
use log::debug;
|
||||
use range::Range;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -16,9 +18,6 @@ use unicode_bidi as bidi;
|
|||
use webrender_api::FontInstanceKey;
|
||||
use xi_unicode::LineBreakLeafIter;
|
||||
|
||||
use crate::font::{FontMetrics, FontRef, RunMetrics, ShapingFlags, ShapingOptions};
|
||||
use crate::text::glyph::{ByteIndex, GlyphStore};
|
||||
|
||||
thread_local! {
|
||||
static INDEX_OF_FIRST_GLYPH_RUN_CACHE: Cell<Option<(*const TextRun, ByteIndex, usize)>> =
|
||||
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> {
|
||||
glyphs: &'a [GlyphRun],
|
||||
index: usize,
|
||||
|
@ -67,18 +57,6 @@ pub struct NaturalWordSliceIterator<'a> {
|
|||
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
|
||||
/// store. Line breaking strategies yield these.
|
||||
pub struct TextRunSlice<'a> {
|
|
@ -1361,7 +1361,7 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
|
|||
inline_size: Length,
|
||||
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;
|
||||
} else {
|
||||
self.current_line_segment.trailing_whitespace_size = Length::zero();
|
||||
|
@ -1497,7 +1497,7 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
|
|||
bitflags! {
|
||||
pub struct SegmentContentFlags: u8 {
|
||||
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)
|
||||
}
|
||||
|
||||
fn is_wrappable_whitespace(&self) -> bool {
|
||||
self.contains(Self::WRAPPABLE_WHITESPACE)
|
||||
fn is_wrappable_and_hangable(&self) -> bool {
|
||||
self.contains(Self::WRAPPABLE_AND_HANGABLE_WHITESPACE)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&InheritedText> for SegmentContentFlags {
|
||||
fn from(style_text: &InheritedText) -> Self {
|
||||
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);
|
||||
}
|
||||
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
|
||||
}
|
||||
|
|
|
@ -152,9 +152,10 @@ pub(super) struct TextRunLineItem {
|
|||
|
||||
impl TextRunLineItem {
|
||||
fn trim_whitespace_at_end(&mut self, whitespace_trimmed: &mut Length) -> bool {
|
||||
if self.parent_style.get_inherited_text().white_space_collapse ==
|
||||
WhiteSpaceCollapse::Preserve
|
||||
{
|
||||
if matches!(
|
||||
self.parent_style.get_inherited_text().white_space_collapse,
|
||||
WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -177,9 +178,10 @@ impl TextRunLineItem {
|
|||
}
|
||||
|
||||
fn trim_whitespace_at_start(&mut self, whitespace_trimmed: &mut Length) -> bool {
|
||||
if self.parent_style.get_inherited_text().white_space_collapse ==
|
||||
WhiteSpaceCollapse::Preserve
|
||||
{
|
||||
if matches!(
|
||||
self.parent_style.get_inherited_text().white_space_collapse,
|
||||
WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ use app_units::Au;
|
|||
use gfx::font::{FontRef, ShapingFlags, ShapingOptions};
|
||||
use gfx::font_cache_thread::FontCacheThread;
|
||||
use gfx::font_context::FontContext;
|
||||
use gfx::text::text_run::GlyphRun;
|
||||
use gfx::text::glyph::GlyphRun;
|
||||
use gfx_traits::ByteIndex;
|
||||
use log::warn;
|
||||
use range::Range;
|
||||
|
@ -18,7 +18,10 @@ use servo_arc::Arc;
|
|||
use style::computed_values::text_rendering::T as TextRendering;
|
||||
use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse;
|
||||
use style::computed_values::word_break::T as WordBreak;
|
||||
use style::properties::style_structs::InheritedText;
|
||||
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::TextTransform;
|
||||
use unicode_script::Script;
|
||||
|
@ -256,11 +259,10 @@ impl TextRun {
|
|||
script: segment.script,
|
||||
flags,
|
||||
};
|
||||
(segment.runs, segment.break_at_start) =
|
||||
gfx::text::text_run::TextRun::break_and_shape(
|
||||
(segment.runs, segment.break_at_start) = break_and_shape(
|
||||
font,
|
||||
&self.text
|
||||
[segment.range.begin().0 as usize..segment.range.end().0 as usize],
|
||||
&self.text[segment.range.begin().0 as usize..segment.range.end().0 as usize],
|
||||
&inherited_text_style,
|
||||
&shaping_options,
|
||||
linebreaker,
|
||||
);
|
||||
|
@ -590,8 +592,17 @@ where
|
|||
// > characters are considered collapsible
|
||||
// If whitespace is not considered collapsible, it is preserved entirely, which
|
||||
// means that we can simply return the input string exactly.
|
||||
if self.white_space_collapse == WhiteSpaceCollapse::Preserve {
|
||||
return self.char_iterator.next();
|
||||
if self.white_space_collapse == WhiteSpaceCollapse::Preserve ||
|
||||
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() {
|
||||
|
@ -830,3 +841,129 @@ where
|
|||
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]
|
||||
[Property white-space value 'break-spaces' computes to 'break-spaces']
|
||||
expected: FAIL
|
||||
|
||||
[Property white-space value 'break-spaces']
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,22 +1,4 @@
|
|||
[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]
|
||||
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]
|
||||
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]
|
||||
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]
|
||||
[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]
|
||||
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]
|
||||
[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']
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,13 +1,4 @@
|
|||
[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]
|
||||
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