mirror of
https://github.com/servo/servo.git
synced 2025-08-02 04:00:32 +01:00
layout: Add initial support for bidirectional text (BiDi) (#33148)
This adds supports for right-to-left text assigning bidi levels to all line items when necessary. This includes support for the `dir` attribute as well as corresponding CSS properties like `unicode-bidi`. It only implements right-to-left rendering for inline layout at the moment and doesn't include support for `dir=auto`. Because of missing features, this causes quite a few tests to start failing, as references become incorrect due to right-to-left rendering being active in some cases, but not others (before it didn't exist at all). Analysis of most of the new failures: ``` - /css/css-flexbox/gap-001-rtl.html /css/css-flexbox/gap-004-rtl.html - Require implementing BiDi in Flexbox, because the start and end inline margins are opposite the order of items. - /css/CSS2/bidi-text/direction-applies-to-*.xht /css/CSS2/bidi-text/direction-applies-to-002.xht /css/CSS2/bidi-text/direction-applies-to-003.xht /css/CSS2/bidi-text/direction-applies-to-004.xht - Broken due to a bug in tables, not allocating the right amount of width for a column. - /css/css-lists/inline-list.html - This fails because we wrongly insert a soft wrap opportunity between the start of an inline box and its first content. - /css/css-text/bidi/bidi-lines-001.html /css/css-text/bidi/bidi-lines-002.html /css/CSS2/text/bidi-flag-emoji.html - We do not fully support unicode-bidi: plaintext - /css/css-text/text-align/text-align-end-010.html /css/css-text/text-align/text-align-justify-006.html /css/css-text/text-align/text-align-start-010.html /html/dom/elements/global-attributes/* - We do not support dir=auto yet. - /css/css-text/white-space/tab-bidi-001.html - Servo doesn't support tab stops - /css/CSS2/positioning/abspos-block-level-001.html /css/css-text/word-break/word-break-normal-ar-000.html - Do not yet support RTL layout in block - /css/css-text/white-space/pre-wrap-018.html - Even in RTL contexts, spaces at the end of the line must hang and not be reordered - /css/css-text/white-space/trailing-space-and-text-alignment-rtl-002.html - We are letting spaces hang with white-space: pre, but they shouldn't hang. ``` Signed-off-by: Martin Robinson <mrobinson@igalia.com> Co-authored-by: Rakhi Sharma <atbrakhi@igalia.com>
This commit is contained in:
parent
65bd5a3b99
commit
56280c6242
189 changed files with 547 additions and 762 deletions
29
Cargo.lock
generated
29
Cargo.lock
generated
|
@ -1287,7 +1287,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-07-16#947990669824c192736f63f982e38b7e62150688"
|
source = "git+https://github.com/servo/stylo?branch=2024-07-16#c3a84ea8c97b0af542fbb47eee8f6f182b357128"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling",
|
"darling",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
|
@ -1476,7 +1476,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dom"
|
name = "dom"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
source = "git+https://github.com/servo/stylo?branch=2024-07-16#947990669824c192736f63f982e38b7e62150688"
|
source = "git+https://github.com/servo/stylo?branch=2024-07-16#c3a84ea8c97b0af542fbb47eee8f6f182b357128"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.6.0",
|
||||||
]
|
]
|
||||||
|
@ -3737,6 +3737,7 @@ dependencies = [
|
||||||
"servo_url",
|
"servo_url",
|
||||||
"style",
|
"style",
|
||||||
"style_traits",
|
"style_traits",
|
||||||
|
"unicode-bidi",
|
||||||
"unicode-script",
|
"unicode-script",
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
"url",
|
"url",
|
||||||
|
@ -4082,7 +4083,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-07-16#947990669824c192736f63f982e38b7e62150688"
|
source = "git+https://github.com/servo/stylo?branch=2024-07-16#c3a84ea8c97b0af542fbb47eee8f6f182b357128"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"accountable-refcell",
|
"accountable-refcell",
|
||||||
"app_units",
|
"app_units",
|
||||||
|
@ -5761,7 +5762,7 @@ checksum = "0495e4577c672de8254beb68d01a9b62d0e8a13c099edecdbedccce3223cd29f"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "selectors"
|
name = "selectors"
|
||||||
version = "0.24.0"
|
version = "0.24.0"
|
||||||
source = "git+https://github.com/servo/stylo?branch=2024-07-16#947990669824c192736f63f982e38b7e62150688"
|
source = "git+https://github.com/servo/stylo?branch=2024-07-16#c3a84ea8c97b0af542fbb47eee8f6f182b357128"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.6.0",
|
||||||
"cssparser",
|
"cssparser",
|
||||||
|
@ -6075,7 +6076,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-07-16#947990669824c192736f63f982e38b7e62150688"
|
source = "git+https://github.com/servo/stylo?branch=2024-07-16#c3a84ea8c97b0af542fbb47eee8f6f182b357128"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"stable_deref_trait",
|
"stable_deref_trait",
|
||||||
|
@ -6084,7 +6085,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-07-16#947990669824c192736f63f982e38b7e62150688"
|
source = "git+https://github.com/servo/stylo?branch=2024-07-16#c3a84ea8c97b0af542fbb47eee8f6f182b357128"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"string_cache",
|
"string_cache",
|
||||||
"string_cache_codegen",
|
"string_cache_codegen",
|
||||||
|
@ -6282,7 +6283,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-07-16#947990669824c192736f63f982e38b7e62150688"
|
source = "git+https://github.com/servo/stylo?branch=2024-07-16#c3a84ea8c97b0af542fbb47eee8f6f182b357128"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"static_assertions",
|
"static_assertions",
|
||||||
]
|
]
|
||||||
|
@ -6423,7 +6424,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-07-16#947990669824c192736f63f982e38b7e62150688"
|
source = "git+https://github.com/servo/stylo?branch=2024-07-16#c3a84ea8c97b0af542fbb47eee8f6f182b357128"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strck"
|
name = "strck"
|
||||||
|
@ -6476,7 +6477,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-07-16#947990669824c192736f63f982e38b7e62150688"
|
source = "git+https://github.com/servo/stylo?branch=2024-07-16#c3a84ea8c97b0af542fbb47eee8f6f182b357128"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"app_units",
|
"app_units",
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
|
@ -6535,7 +6536,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-07-16#947990669824c192736f63f982e38b7e62150688"
|
source = "git+https://github.com/servo/stylo?branch=2024-07-16#c3a84ea8c97b0af542fbb47eee8f6f182b357128"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
]
|
]
|
||||||
|
@ -6543,7 +6544,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-07-16#947990669824c192736f63f982e38b7e62150688"
|
source = "git+https://github.com/servo/stylo?branch=2024-07-16#c3a84ea8c97b0af542fbb47eee8f6f182b357128"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling",
|
"darling",
|
||||||
"derive_common",
|
"derive_common",
|
||||||
|
@ -6574,7 +6575,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-07-16#947990669824c192736f63f982e38b7e62150688"
|
source = "git+https://github.com/servo/stylo?branch=2024-07-16#c3a84ea8c97b0af542fbb47eee8f6f182b357128"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"app_units",
|
"app_units",
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.6.0",
|
||||||
|
@ -6923,7 +6924,7 @@ dependencies = [
|
||||||
[[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-07-16#947990669824c192736f63f982e38b7e62150688"
|
source = "git+https://github.com/servo/stylo?branch=2024-07-16#c3a84ea8c97b0af542fbb47eee8f6f182b357128"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cssparser",
|
"cssparser",
|
||||||
"servo_arc",
|
"servo_arc",
|
||||||
|
@ -6936,7 +6937,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-07-16#947990669824c192736f63f982e38b7e62150688"
|
source = "git+https://github.com/servo/stylo?branch=2024-07-16#c3a84ea8c97b0af542fbb47eee8f6f182b357128"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling",
|
"darling",
|
||||||
"derive_common",
|
"derive_common",
|
||||||
|
|
|
@ -16,14 +16,15 @@ doctest = false
|
||||||
app_units = { workspace = true }
|
app_units = { workspace = true }
|
||||||
atomic_refcell = { workspace = true }
|
atomic_refcell = { workspace = true }
|
||||||
base = { workspace = true }
|
base = { workspace = true }
|
||||||
canvas_traits = { workspace = true }
|
|
||||||
bitflags = { workspace = true }
|
bitflags = { workspace = true }
|
||||||
|
canvas_traits = { workspace = true }
|
||||||
|
data-url = { workspace = true }
|
||||||
embedder_traits = { workspace = true }
|
embedder_traits = { workspace = true }
|
||||||
euclid = { workspace = true }
|
euclid = { workspace = true }
|
||||||
fnv = { workspace = true }
|
fnv = { workspace = true }
|
||||||
fxhash = { workspace = true }
|
|
||||||
fonts = { path = "../fonts" }
|
fonts = { path = "../fonts" }
|
||||||
fonts_traits = { workspace = true }
|
fonts_traits = { workspace = true }
|
||||||
|
fxhash = { workspace = true }
|
||||||
html5ever = { workspace = true }
|
html5ever = { workspace = true }
|
||||||
icu_segmenter = { workspace = true }
|
icu_segmenter = { workspace = true }
|
||||||
ipc-channel = { workspace = true }
|
ipc-channel = { workspace = true }
|
||||||
|
@ -44,10 +45,10 @@ servo_geometry = { path = "../geometry" }
|
||||||
servo_url = { path = "../url" }
|
servo_url = { path = "../url" }
|
||||||
style = { workspace = true }
|
style = { workspace = true }
|
||||||
style_traits = { workspace = true }
|
style_traits = { workspace = true }
|
||||||
|
unicode-bidi = { workspace = true }
|
||||||
unicode-script = { workspace = true }
|
unicode-script = { workspace = true }
|
||||||
unicode-segmentation = { workspace = true }
|
unicode-segmentation = { workspace = true }
|
||||||
url = { workspace = true }
|
url = { workspace = true }
|
||||||
data-url = { workspace = true }
|
|
||||||
webrender_api = { workspace = true }
|
webrender_api = { workspace = true }
|
||||||
webrender_traits = { workspace = true }
|
webrender_traits = { workspace = true }
|
||||||
xi-unicode = { workspace = true }
|
xi-unicode = { workspace = true }
|
||||||
|
|
|
@ -161,6 +161,7 @@ where
|
||||||
self.text_decoration_line,
|
self.text_decoration_line,
|
||||||
true, /* has_first_formatted_line */
|
true, /* has_first_formatted_line */
|
||||||
false, /* is_single_line_text_box */
|
false, /* is_single_line_text_box */
|
||||||
|
self.info.style.writing_mode.to_bidi_level(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let block_formatting_context = BlockFormattingContext::from_block_container(
|
let block_formatting_context = BlockFormattingContext::from_block_container(
|
||||||
|
|
|
@ -15,7 +15,7 @@ pub(super) struct FlexRelativeVec2<T> {
|
||||||
pub cross: T,
|
pub cross: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub(super) struct FlexRelativeSides<T> {
|
pub(super) struct FlexRelativeSides<T> {
|
||||||
pub cross_start: T,
|
pub cross_start: T,
|
||||||
pub main_start: T,
|
pub main_start: T,
|
||||||
|
|
|
@ -217,6 +217,7 @@ where
|
||||||
self.text_decoration_line,
|
self.text_decoration_line,
|
||||||
!self.have_already_seen_first_line_for_text_indent,
|
!self.have_already_seen_first_line_for_text_indent,
|
||||||
self.info.is_single_line_text_input(),
|
self.info.is_single_line_text_input(),
|
||||||
|
self.info.style.writing_mode.to_bidi_level(),
|
||||||
) {
|
) {
|
||||||
// There are two options here. This block was composed of both one or more inline formatting contexts
|
// There are two options here. This block was composed of both one or more inline formatting contexts
|
||||||
// and child blocks OR this block was a single inline formatting context. In the latter case, we
|
// and child blocks OR this block was a single inline formatting context. In the latter case, we
|
||||||
|
@ -489,6 +490,7 @@ where
|
||||||
self.context,
|
self.context,
|
||||||
self.text_decoration_line,
|
self.text_decoration_line,
|
||||||
!self.have_already_seen_first_line_for_text_indent,
|
!self.have_already_seen_first_line_for_text_indent,
|
||||||
|
self.info.style.writing_mode.to_bidi_level(),
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
self.push_block_level_job_for_inline_formatting_context(inline_formatting_context);
|
self.push_block_level_job_for_inline_formatting_context(inline_formatting_context);
|
||||||
|
@ -602,6 +604,7 @@ where
|
||||||
self.text_decoration_line,
|
self.text_decoration_line,
|
||||||
!self.have_already_seen_first_line_for_text_indent,
|
!self.have_already_seen_first_line_for_text_indent,
|
||||||
self.info.is_single_line_text_input(),
|
self.info.is_single_line_text_input(),
|
||||||
|
self.info.style.writing_mode.to_bidi_level(),
|
||||||
) {
|
) {
|
||||||
self.push_block_level_job_for_inline_formatting_context(inline_formatting_context);
|
self.push_block_level_job_for_inline_formatting_context(inline_formatting_context);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ use std::char::{ToLowercase, ToUppercase};
|
||||||
use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse;
|
use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse;
|
||||||
use style::values::computed::TextDecorationLine;
|
use style::values::computed::TextDecorationLine;
|
||||||
use style::values::specified::text::TextTransformCase;
|
use style::values::specified::text::TextTransformCase;
|
||||||
|
use unicode_bidi::Level;
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
use super::text_run::TextRun;
|
use super::text_run::TextRun;
|
||||||
|
@ -19,6 +20,7 @@ use crate::dom_traversal::NodeAndStyleInfo;
|
||||||
use crate::flow::float::FloatBox;
|
use crate::flow::float::FloatBox;
|
||||||
use crate::formatting_contexts::IndependentFormattingContext;
|
use crate::formatting_contexts::IndependentFormattingContext;
|
||||||
use crate::positioned::AbsolutelyPositionedBox;
|
use crate::positioned::AbsolutelyPositionedBox;
|
||||||
|
use crate::style_ext::ComputedValuesExt;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub(crate) struct InlineFormattingContextBuilder {
|
pub(crate) struct InlineFormattingContextBuilder {
|
||||||
|
@ -82,6 +84,11 @@ impl InlineFormattingContextBuilder {
|
||||||
!self.inline_box_stack.is_empty()
|
!self.inline_box_stack.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn push_control_character_string(&mut self, string_to_push: &str) {
|
||||||
|
self.text_segments.push(string_to_push.to_owned());
|
||||||
|
self.current_text_offset += string_to_push.len();
|
||||||
|
}
|
||||||
|
|
||||||
/// Return true if this [`InlineFormattingContextBuilder`] is empty for the purposes of ignoring
|
/// Return true if this [`InlineFormattingContextBuilder`] is empty for the purposes of ignoring
|
||||||
/// during box tree construction. An IFC is empty if it only contains TextRuns with
|
/// during box tree construction. An IFC is empty if it only contains TextRuns with
|
||||||
/// completely collapsible whitespace. When that happens it can be ignored completely.
|
/// completely collapsible whitespace. When that happens it can be ignored completely.
|
||||||
|
@ -101,7 +108,7 @@ impl InlineFormattingContextBuilder {
|
||||||
// Text content is handled by `self.has_uncollapsible_text` content above in order
|
// Text content is handled by `self.has_uncollapsible_text` content above in order
|
||||||
// to avoid having to iterate through the character once again.
|
// to avoid having to iterate through the character once again.
|
||||||
InlineItem::TextRun(_) => true,
|
InlineItem::TextRun(_) => true,
|
||||||
InlineItem::OutOfFlowAbsolutelyPositionedBox(_) => false,
|
InlineItem::OutOfFlowAbsolutelyPositionedBox(..) => false,
|
||||||
InlineItem::OutOfFlowFloatBox(_) => false,
|
InlineItem::OutOfFlowFloatBox(_) => false,
|
||||||
InlineItem::Atomic(..) => false,
|
InlineItem::Atomic(..) => false,
|
||||||
}
|
}
|
||||||
|
@ -119,14 +126,13 @@ impl InlineFormattingContextBuilder {
|
||||||
let inline_level_box = ArcRefCell::new(InlineItem::Atomic(
|
let inline_level_box = ArcRefCell::new(InlineItem::Atomic(
|
||||||
independent_formatting_context,
|
independent_formatting_context,
|
||||||
self.current_text_offset,
|
self.current_text_offset,
|
||||||
|
Level::ltr(), /* This will be assigned later if necessary. */
|
||||||
));
|
));
|
||||||
self.inline_items.push(inline_level_box.clone());
|
self.inline_items.push(inline_level_box.clone());
|
||||||
|
|
||||||
// Push an object replacement character for this atomic, which will ensure that the line breaker
|
// Push an object replacement character for this atomic, which will ensure that the line breaker
|
||||||
// inserts a line breaking opportunity here.
|
// inserts a line breaking opportunity here.
|
||||||
let string_to_push = "\u{fffc}";
|
self.push_control_character_string("\u{fffc}");
|
||||||
self.text_segments.push(string_to_push.to_owned());
|
|
||||||
self.current_text_offset += string_to_push.len();
|
|
||||||
|
|
||||||
self.last_inline_box_ended_with_collapsible_white_space = false;
|
self.last_inline_box_ended_with_collapsible_white_space = false;
|
||||||
self.on_word_boundary = true;
|
self.on_word_boundary = true;
|
||||||
|
@ -141,7 +147,9 @@ impl InlineFormattingContextBuilder {
|
||||||
let absolutely_positioned_box = ArcRefCell::new(absolutely_positioned_box);
|
let absolutely_positioned_box = ArcRefCell::new(absolutely_positioned_box);
|
||||||
let inline_level_box = ArcRefCell::new(InlineItem::OutOfFlowAbsolutelyPositionedBox(
|
let inline_level_box = ArcRefCell::new(InlineItem::OutOfFlowAbsolutelyPositionedBox(
|
||||||
absolutely_positioned_box,
|
absolutely_positioned_box,
|
||||||
|
self.current_text_offset,
|
||||||
));
|
));
|
||||||
|
|
||||||
self.inline_items.push(inline_level_box.clone());
|
self.inline_items.push(inline_level_box.clone());
|
||||||
inline_level_box
|
inline_level_box
|
||||||
}
|
}
|
||||||
|
@ -154,6 +162,8 @@ impl InlineFormattingContextBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn start_inline_box(&mut self, inline_box: InlineBox) {
|
pub(crate) fn start_inline_box(&mut self, inline_box: InlineBox) {
|
||||||
|
self.push_control_character_string(inline_box.style.bidi_control_chars().0);
|
||||||
|
|
||||||
let identifier = self.inline_boxes.start_inline_box(inline_box);
|
let identifier = self.inline_boxes.start_inline_box(inline_box);
|
||||||
self.inline_items
|
self.inline_items
|
||||||
.push(ArcRefCell::new(InlineItem::StartInlineBox(identifier)));
|
.push(ArcRefCell::new(InlineItem::StartInlineBox(identifier)));
|
||||||
|
@ -164,6 +174,9 @@ impl InlineFormattingContextBuilder {
|
||||||
let identifier = self.end_inline_box_internal();
|
let identifier = self.end_inline_box_internal();
|
||||||
let inline_level_box = self.inline_boxes.get(&identifier);
|
let inline_level_box = self.inline_boxes.get(&identifier);
|
||||||
inline_level_box.borrow_mut().is_last_fragment = true;
|
inline_level_box.borrow_mut().is_last_fragment = true;
|
||||||
|
|
||||||
|
self.push_control_character_string(inline_level_box.borrow().style.bidi_control_chars().1);
|
||||||
|
|
||||||
inline_level_box
|
inline_level_box
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,6 +274,7 @@ impl InlineFormattingContextBuilder {
|
||||||
layout_context: &LayoutContext,
|
layout_context: &LayoutContext,
|
||||||
text_decoration_line: TextDecorationLine,
|
text_decoration_line: TextDecorationLine,
|
||||||
has_first_formatted_line: bool,
|
has_first_formatted_line: bool,
|
||||||
|
default_bidi_level: Level,
|
||||||
) -> Option<InlineFormattingContext> {
|
) -> Option<InlineFormattingContext> {
|
||||||
if self.is_empty() {
|
if self.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
|
@ -293,6 +307,7 @@ impl InlineFormattingContextBuilder {
|
||||||
text_decoration_line,
|
text_decoration_line,
|
||||||
has_first_formatted_line,
|
has_first_formatted_line,
|
||||||
/* is_single_line_text_input = */ false,
|
/* is_single_line_text_input = */ false,
|
||||||
|
default_bidi_level,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,6 +318,7 @@ impl InlineFormattingContextBuilder {
|
||||||
text_decoration_line: TextDecorationLine,
|
text_decoration_line: TextDecorationLine,
|
||||||
has_first_formatted_line: bool,
|
has_first_formatted_line: bool,
|
||||||
is_single_line_text_input: bool,
|
is_single_line_text_input: bool,
|
||||||
|
default_bidi_level: Level,
|
||||||
) -> Option<InlineFormattingContext> {
|
) -> Option<InlineFormattingContext> {
|
||||||
if self.is_empty() {
|
if self.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
|
@ -317,6 +333,7 @@ impl InlineFormattingContextBuilder {
|
||||||
text_decoration_line,
|
text_decoration_line,
|
||||||
has_first_formatted_line,
|
has_first_formatted_line,
|
||||||
is_single_line_text_input,
|
is_single_line_text_input,
|
||||||
|
default_bidi_level,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
* 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::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::vec::IntoIter;
|
|
||||||
|
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
|
@ -18,6 +17,7 @@ use style::values::specified::box_::DisplayOutside;
|
||||||
use style::values::specified::text::TextDecorationLine;
|
use style::values::specified::text::TextDecorationLine;
|
||||||
use style::values::Either;
|
use style::values::Either;
|
||||||
use style::Zero;
|
use style::Zero;
|
||||||
|
use unicode_bidi::{BidiInfo, Level};
|
||||||
use webrender_api::FontInstanceKey;
|
use webrender_api::FontInstanceKey;
|
||||||
|
|
||||||
use super::inline_box::{
|
use super::inline_box::{
|
||||||
|
@ -170,7 +170,7 @@ pub(super) struct LineItemLayout<'a> {
|
||||||
impl<'a> LineItemLayout<'a> {
|
impl<'a> LineItemLayout<'a> {
|
||||||
pub(super) fn layout_line_items(
|
pub(super) fn layout_line_items(
|
||||||
state: &mut InlineFormattingContextState,
|
state: &mut InlineFormattingContextState,
|
||||||
iterator: &mut IntoIter<LineItem>,
|
line_items: Vec<LineItem>,
|
||||||
start_position: LogicalVec2<Au>,
|
start_position: LogicalVec2<Au>,
|
||||||
effective_block_advance: &LineBlockSizes,
|
effective_block_advance: &LineBlockSizes,
|
||||||
justification_adjustment: Au,
|
justification_adjustment: Au,
|
||||||
|
@ -191,7 +191,7 @@ impl<'a> LineItemLayout<'a> {
|
||||||
},
|
},
|
||||||
justification_adjustment,
|
justification_adjustment,
|
||||||
}
|
}
|
||||||
.layout(iterator)
|
.layout(line_items, state.has_right_to_left_content)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start and end inline boxes in tree order, so that it reflects the given inline box.
|
/// Start and end inline boxes in tree order, so that it reflects the given inline box.
|
||||||
|
@ -217,8 +217,40 @@ impl<'a> LineItemLayout<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn layout(&mut self, iterator: &mut IntoIter<LineItem>) -> Vec<Fragment> {
|
pub(super) fn layout(
|
||||||
for item in iterator.by_ref() {
|
&mut self,
|
||||||
|
mut line_items: Vec<LineItem>,
|
||||||
|
has_right_to_left_content: bool,
|
||||||
|
) -> Vec<Fragment> {
|
||||||
|
let mut last_level: Level = Level::ltr();
|
||||||
|
let levels: Vec<_> = line_items
|
||||||
|
.iter()
|
||||||
|
.map(|item| {
|
||||||
|
let level = match item {
|
||||||
|
LineItem::TextRun(_, text_run) => text_run.bidi_level,
|
||||||
|
// TODO: This level needs either to be last_level, or if there were
|
||||||
|
// unicode characters inserted for the inline box, we need to get the
|
||||||
|
// level from them.
|
||||||
|
LineItem::StartInlineBoxPaddingBorderMargin(_) => last_level,
|
||||||
|
LineItem::EndInlineBoxPaddingBorderMargin(_) => last_level,
|
||||||
|
LineItem::Atomic(_, atomic) => atomic.bidi_level,
|
||||||
|
LineItem::AbsolutelyPositioned(..) => last_level,
|
||||||
|
LineItem::Float(..) => {
|
||||||
|
// At this point the float is already positioned, so it doesn't really matter what
|
||||||
|
// position it's fragment has in the order of line items.
|
||||||
|
last_level
|
||||||
|
},
|
||||||
|
};
|
||||||
|
last_level = level;
|
||||||
|
level
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if has_right_to_left_content {
|
||||||
|
sort_by_indices_in_place(&mut line_items, BidiInfo::reorder_visual(&levels));
|
||||||
|
}
|
||||||
|
|
||||||
|
for item in line_items.into_iter().by_ref() {
|
||||||
// When preparing to lay out a new line item, start and end inline boxes, so that the current
|
// When preparing to lay out a new line item, start and end inline boxes, so that the current
|
||||||
// inline box state reflects the item's parent. Items in the line are not necessarily in tree
|
// inline box state reflects the item's parent. Items in the line are not necessarily in tree
|
||||||
// order due to BiDi and other reordering so the inline box of the item could potentially be
|
// order due to BiDi and other reordering so the inline box of the item could potentially be
|
||||||
|
@ -304,10 +336,10 @@ impl<'a> LineItemLayout<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn end_inline_box(&mut self) {
|
fn end_inline_box(&mut self) {
|
||||||
let outer_state = self.state_stack.pop().expect("Ended unknown inline box 11");
|
let outer_state = self.state_stack.pop().expect("Ended unknown inline box");
|
||||||
let mut inner_state = std::mem::replace(&mut self.state, outer_state);
|
let mut inner_state = std::mem::replace(&mut self.state, outer_state);
|
||||||
|
|
||||||
let identifier = inner_state.identifier.expect("Ended unknown inline box 22");
|
let identifier = inner_state.identifier.expect("Ended unknown inline box");
|
||||||
let inline_box_state = &*self.inline_box_states[identifier.index_in_inline_boxes as usize];
|
let inline_box_state = &*self.inline_box_states[identifier.index_in_inline_boxes as usize];
|
||||||
let inline_box = self.inline_boxes.get(&identifier);
|
let inline_box = self.inline_boxes.get(&identifier);
|
||||||
let inline_box = &*(inline_box.borrow());
|
let inline_box = &*(inline_box.borrow());
|
||||||
|
@ -315,23 +347,24 @@ impl<'a> LineItemLayout<'a> {
|
||||||
let mut padding = inline_box_state.pbm.padding;
|
let mut padding = inline_box_state.pbm.padding;
|
||||||
let mut border = inline_box_state.pbm.border;
|
let mut border = inline_box_state.pbm.border;
|
||||||
let mut margin = inline_box_state.pbm.margin.auto_is(Au::zero);
|
let mut margin = inline_box_state.pbm.margin.auto_is(Au::zero);
|
||||||
if !inner_state
|
|
||||||
|
let had_start = inner_state
|
||||||
.flags
|
.flags
|
||||||
.contains(LineLayoutInlineContainerFlags::HAD_START_PBM)
|
.contains(LineLayoutInlineContainerFlags::HAD_START_PBM);
|
||||||
{
|
let had_end = inner_state
|
||||||
|
.flags
|
||||||
|
.contains(LineLayoutInlineContainerFlags::HAD_END_PBM);
|
||||||
|
|
||||||
|
if !had_start {
|
||||||
padding.inline_start = Au::zero();
|
padding.inline_start = Au::zero();
|
||||||
border.inline_start = Au::zero();
|
border.inline_start = Au::zero();
|
||||||
margin.inline_start = Au::zero();
|
margin.inline_start = Au::zero();
|
||||||
}
|
}
|
||||||
if !inner_state
|
if !had_end {
|
||||||
.flags
|
|
||||||
.contains(LineLayoutInlineContainerFlags::HAD_END_PBM)
|
|
||||||
{
|
|
||||||
padding.inline_end = Au::zero();
|
padding.inline_end = Au::zero();
|
||||||
border.inline_end = Au::zero();
|
border.inline_end = Au::zero();
|
||||||
margin.inline_end = Au::zero();
|
margin.inline_end = Au::zero();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the inline box didn't have any content at all and it isn't the first fragment for
|
// If the inline box didn't have any content at all and it isn't the first fragment for
|
||||||
// an element (needed for layout queries currently) and it didn't have any padding, border,
|
// an element (needed for layout queries currently) and it didn't have any padding, border,
|
||||||
// or margin do not make a fragment for it.
|
// or margin do not make a fragment for it.
|
||||||
|
@ -339,12 +372,7 @@ impl<'a> LineItemLayout<'a> {
|
||||||
// Note: This is an optimization, but also has side effects. Any fragments on a line will
|
// Note: This is an optimization, but also has side effects. Any fragments on a line will
|
||||||
// force the baseline to advance in the parent IFC.
|
// force the baseline to advance in the parent IFC.
|
||||||
let pbm_sums = padding + border + margin;
|
let pbm_sums = padding + border + margin;
|
||||||
if inner_state.fragments.is_empty() &&
|
if inner_state.fragments.is_empty() && !had_start && pbm_sums.inline_sum().is_zero() {
|
||||||
!inner_state
|
|
||||||
.flags
|
|
||||||
.contains(LineLayoutInlineContainerFlags::HAD_START_PBM) &&
|
|
||||||
pbm_sums.inline_sum().is_zero()
|
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -645,6 +673,8 @@ pub(super) struct TextRunLineItem {
|
||||||
pub font_metrics: FontMetrics,
|
pub font_metrics: FontMetrics,
|
||||||
pub font_key: FontInstanceKey,
|
pub font_key: FontInstanceKey,
|
||||||
pub text_decoration_line: TextDecorationLine,
|
pub text_decoration_line: TextDecorationLine,
|
||||||
|
/// The BiDi level of this [`TextRunLineItem`] to enable reordering.
|
||||||
|
pub bidi_level: Level,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextRunLineItem {
|
impl TextRunLineItem {
|
||||||
|
@ -697,6 +727,10 @@ impl TextRunLineItem {
|
||||||
// Only keep going if we only encountered whitespace.
|
// Only keep going if we only encountered whitespace.
|
||||||
self.text.is_empty()
|
self.text.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn can_merge(&self, font_key: FontInstanceKey, bidi_level: Level) -> bool {
|
||||||
|
self.font_key == font_key && self.bidi_level == bidi_level
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) struct AtomicLineItem {
|
pub(super) struct AtomicLineItem {
|
||||||
|
@ -711,6 +745,9 @@ pub(super) struct AtomicLineItem {
|
||||||
|
|
||||||
/// The offset of the baseline inside this item.
|
/// The offset of the baseline inside this item.
|
||||||
pub baseline_offset_in_item: Au,
|
pub baseline_offset_in_item: Au,
|
||||||
|
|
||||||
|
/// The BiDi level of this [`AtomicLineItem`] to enable reordering.
|
||||||
|
pub bidi_level: Level,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AtomicLineItem {
|
impl AtomicLineItem {
|
||||||
|
@ -753,3 +790,24 @@ fn line_height(parent_style: &ComputedValues, font_metrics: &FontMetrics) -> Len
|
||||||
LineHeight::Length(length) => length.0,
|
LineHeight::Length(length) => length.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sort a mutable slice by the the given indices array in place, reording the slice so that final
|
||||||
|
/// value of `slice[x]` is `slice[indices[x]]`.
|
||||||
|
fn sort_by_indices_in_place<T>(data: &mut [T], mut indices: Vec<usize>) {
|
||||||
|
for idx in 0..data.len() {
|
||||||
|
if indices[idx] == idx {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut current_idx = idx;
|
||||||
|
loop {
|
||||||
|
let target_idx = indices[current_idx];
|
||||||
|
indices[current_idx] = current_idx;
|
||||||
|
if indices[target_idx] == target_idx {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
data.swap(current_idx, target_idx);
|
||||||
|
current_idx = target_idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -108,6 +108,7 @@ use text_run::{
|
||||||
add_or_get_font, get_font_for_first_font_for_style, TextRun, XI_LINE_BREAKING_CLASS_GL,
|
add_or_get_font, get_font_for_first_font_for_style, TextRun, XI_LINE_BREAKING_CLASS_GL,
|
||||||
XI_LINE_BREAKING_CLASS_WJ, XI_LINE_BREAKING_CLASS_ZWJ,
|
XI_LINE_BREAKING_CLASS_WJ, XI_LINE_BREAKING_CLASS_ZWJ,
|
||||||
};
|
};
|
||||||
|
use unicode_bidi::{BidiInfo, Level};
|
||||||
use webrender_api::FontInstanceKey;
|
use webrender_api::FontInstanceKey;
|
||||||
use xi_unicode::linebreak_property;
|
use xi_unicode::linebreak_property;
|
||||||
|
|
||||||
|
@ -161,8 +162,12 @@ pub(crate) struct InlineFormattingContext {
|
||||||
/// Whether or not this [`InlineFormattingContext`] contains floats.
|
/// Whether or not this [`InlineFormattingContext`] contains floats.
|
||||||
pub(super) contains_floats: bool,
|
pub(super) contains_floats: bool,
|
||||||
|
|
||||||
/// Whether or not this is an inline formatting context for a single line text input.
|
/// Whether or not this is an [`InlineFormattingContext`] for a single line text input.
|
||||||
pub(super) is_single_line_text_input: bool,
|
pub(super) is_single_line_text_input: bool,
|
||||||
|
|
||||||
|
/// Whether or not this is an [`InlineFormattingContext`] has right-to-left content, which
|
||||||
|
/// will require reordering during layout.
|
||||||
|
pub(super) has_right_to_left_content: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A collection of data used to cache [`FontMetrics`] in the [`InlineFormattingContext`]
|
/// A collection of data used to cache [`FontMetrics`] in the [`InlineFormattingContext`]
|
||||||
|
@ -178,11 +183,15 @@ pub(crate) enum InlineItem {
|
||||||
StartInlineBox(InlineBoxIdentifier),
|
StartInlineBox(InlineBoxIdentifier),
|
||||||
EndInlineBox,
|
EndInlineBox,
|
||||||
TextRun(TextRun),
|
TextRun(TextRun),
|
||||||
OutOfFlowAbsolutelyPositionedBox(ArcRefCell<AbsolutelyPositionedBox>),
|
OutOfFlowAbsolutelyPositionedBox(
|
||||||
|
ArcRefCell<AbsolutelyPositionedBox>,
|
||||||
|
usize, /* offset_in_text */
|
||||||
|
),
|
||||||
OutOfFlowFloatBox(FloatBox),
|
OutOfFlowFloatBox(FloatBox),
|
||||||
Atomic(
|
Atomic(
|
||||||
IndependentFormattingContext,
|
IndependentFormattingContext,
|
||||||
usize, /* offset_in_text */
|
usize, /* offset_in_text */
|
||||||
|
Level, /* bidi_level */
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -639,6 +648,9 @@ pub(super) struct InlineFormattingContextState<'a, 'b> {
|
||||||
/// are laying out. This is used to propagate baselines to the ancestors of
|
/// are laying out. This is used to propagate baselines to the ancestors of
|
||||||
/// `display: inline-block` elements and table content.
|
/// `display: inline-block` elements and table content.
|
||||||
baselines: Baselines,
|
baselines: Baselines,
|
||||||
|
|
||||||
|
/// Whether or not the [`InlineFormattingContext`] being laid out has right-to-left content.
|
||||||
|
has_right_to_left_content: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
|
impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
|
||||||
|
@ -853,7 +865,7 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
|
||||||
let start_positioning_context_length = self.positioning_context.len();
|
let start_positioning_context_length = self.positioning_context.len();
|
||||||
let fragments = LineItemLayout::layout_line_items(
|
let fragments = LineItemLayout::layout_line_items(
|
||||||
self,
|
self,
|
||||||
&mut line_to_layout.line_items.into_iter(),
|
line_to_layout.line_items,
|
||||||
start_position,
|
start_position,
|
||||||
&effective_block_advance,
|
&effective_block_advance,
|
||||||
justification_adjustment,
|
justification_adjustment,
|
||||||
|
@ -907,9 +919,9 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
|
||||||
last_line_or_forced_line_break: bool,
|
last_line_or_forced_line_break: bool,
|
||||||
) -> (Au, Au) {
|
) -> (Au, Au) {
|
||||||
enum TextAlign {
|
enum TextAlign {
|
||||||
Start,
|
Left,
|
||||||
Center,
|
Center,
|
||||||
End,
|
Right,
|
||||||
}
|
}
|
||||||
let style = self.containing_block.style;
|
let style = self.containing_block.style;
|
||||||
let mut text_align_keyword = style.clone_text_align();
|
let mut text_align_keyword = style.clone_text_align();
|
||||||
|
@ -930,24 +942,24 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let text_align = match text_align_keyword {
|
let text_align = match text_align_keyword {
|
||||||
TextAlignKeyword::Start => TextAlign::Start,
|
TextAlignKeyword::Start => {
|
||||||
|
if style.writing_mode.line_left_is_inline_start() {
|
||||||
|
TextAlign::Left
|
||||||
|
} else {
|
||||||
|
TextAlign::Right
|
||||||
|
}
|
||||||
|
},
|
||||||
TextAlignKeyword::Center | TextAlignKeyword::MozCenter => TextAlign::Center,
|
TextAlignKeyword::Center | TextAlignKeyword::MozCenter => TextAlign::Center,
|
||||||
TextAlignKeyword::End => TextAlign::End,
|
TextAlignKeyword::End => {
|
||||||
TextAlignKeyword::Left | TextAlignKeyword::MozLeft => {
|
if style.writing_mode.line_left_is_inline_start() {
|
||||||
if style.effective_writing_mode().line_left_is_inline_start() {
|
TextAlign::Right
|
||||||
TextAlign::Start
|
|
||||||
} else {
|
} else {
|
||||||
TextAlign::End
|
TextAlign::Left
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
TextAlignKeyword::Right | TextAlignKeyword::MozRight => {
|
TextAlignKeyword::Left | TextAlignKeyword::MozLeft => TextAlign::Left,
|
||||||
if style.effective_writing_mode().line_left_is_inline_start() {
|
TextAlignKeyword::Right | TextAlignKeyword::MozRight => TextAlign::Right,
|
||||||
TextAlign::End
|
TextAlignKeyword::Justify => TextAlign::Left,
|
||||||
} else {
|
|
||||||
TextAlign::Start
|
|
||||||
}
|
|
||||||
},
|
|
||||||
TextAlignKeyword::Justify => TextAlign::Start,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let (line_start, available_space) = match self.current_line.placement_among_floats.get() {
|
let (line_start, available_space) = match self.current_line.placement_among_floats.get() {
|
||||||
|
@ -968,8 +980,8 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
|
||||||
let line_length = self.current_line.inline_position - whitespace_trimmed - text_indent;
|
let line_length = self.current_line.inline_position - whitespace_trimmed - text_indent;
|
||||||
let adjusted_line_start = line_start +
|
let adjusted_line_start = line_start +
|
||||||
match text_align {
|
match text_align {
|
||||||
TextAlign::Start => text_indent,
|
TextAlign::Left => text_indent,
|
||||||
TextAlign::End => (available_space - line_length).max(text_indent),
|
TextAlign::Right => (available_space - line_length).max(text_indent),
|
||||||
TextAlign::Center => (available_space - line_length + text_indent)
|
TextAlign::Center => (available_space - line_length + text_indent)
|
||||||
.scale_by(0.5)
|
.scale_by(0.5)
|
||||||
.max(text_indent),
|
.max(text_indent),
|
||||||
|
@ -1241,6 +1253,7 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
|
||||||
glyph_store: std::sync::Arc<GlyphStore>,
|
glyph_store: std::sync::Arc<GlyphStore>,
|
||||||
text_run: &TextRun,
|
text_run: &TextRun,
|
||||||
font_index: usize,
|
font_index: usize,
|
||||||
|
bidi_level: Level,
|
||||||
) {
|
) {
|
||||||
let inline_advance = glyph_store.total_advance();
|
let inline_advance = glyph_store.total_advance();
|
||||||
let flags = if glyph_store.is_whitespace() {
|
let flags = if glyph_store.is_whitespace() {
|
||||||
|
@ -1288,8 +1301,8 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
|
||||||
let current_inline_box_identifier = self.current_inline_box_identifier();
|
let current_inline_box_identifier = self.current_inline_box_identifier();
|
||||||
match self.current_line_segment.line_items.last_mut() {
|
match self.current_line_segment.line_items.last_mut() {
|
||||||
Some(LineItem::TextRun(inline_box_identifier, line_item))
|
Some(LineItem::TextRun(inline_box_identifier, line_item))
|
||||||
if ifc_font_info.key == line_item.font_key &&
|
if *inline_box_identifier == current_inline_box_identifier &&
|
||||||
*inline_box_identifier == current_inline_box_identifier =>
|
line_item.can_merge(ifc_font_info.key, bidi_level) =>
|
||||||
{
|
{
|
||||||
line_item.text.push(glyph_store);
|
line_item.text.push(glyph_store);
|
||||||
return;
|
return;
|
||||||
|
@ -1306,6 +1319,7 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
|
||||||
font_metrics,
|
font_metrics,
|
||||||
font_key: ifc_font_info.key,
|
font_key: ifc_font_info.key,
|
||||||
text_decoration_line: self.current_inline_container_state().text_decoration_line,
|
text_decoration_line: self.current_inline_container_state().text_decoration_line,
|
||||||
|
bidi_level,
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -1436,26 +1450,9 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
|
||||||
assert!(!will_break);
|
assert!(!will_break);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to merge all TextRuns in the line.
|
|
||||||
let to_skip = match (
|
|
||||||
self.current_line.line_items.last_mut(),
|
|
||||||
segment_items.first_mut(),
|
|
||||||
) {
|
|
||||||
(
|
|
||||||
Some(LineItem::TextRun(last_inline_box_identifier, last_line_item)),
|
|
||||||
Some(LineItem::TextRun(first_inline_box_identifier, first_segment_item)),
|
|
||||||
) if last_line_item.font_key == first_segment_item.font_key &&
|
|
||||||
last_inline_box_identifier == first_inline_box_identifier =>
|
|
||||||
{
|
|
||||||
last_line_item.text.append(&mut first_segment_item.text);
|
|
||||||
1
|
|
||||||
},
|
|
||||||
_ => 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.current_line
|
self.current_line
|
||||||
.line_items
|
.line_items
|
||||||
.extend(segment_items.into_iter().skip(to_skip));
|
.extend(segment_items.into_iter());
|
||||||
self.current_line.has_content |= self.current_line_segment.has_content;
|
self.current_line.has_content |= self.current_line_segment.has_content;
|
||||||
|
|
||||||
self.current_line_segment.reset();
|
self.current_line_segment.reset();
|
||||||
|
@ -1510,11 +1507,15 @@ impl InlineFormattingContext {
|
||||||
text_decoration_line: TextDecorationLine,
|
text_decoration_line: TextDecorationLine,
|
||||||
has_first_formatted_line: bool,
|
has_first_formatted_line: bool,
|
||||||
is_single_line_text_input: bool,
|
is_single_line_text_input: bool,
|
||||||
|
starting_bidi_level: Level,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
// This is to prevent a double borrow.
|
// This is to prevent a double borrow.
|
||||||
let text_content: String = builder.text_segments.into_iter().collect();
|
let text_content: String = builder.text_segments.into_iter().collect();
|
||||||
let mut font_metrics = Vec::new();
|
let mut font_metrics = Vec::new();
|
||||||
|
|
||||||
|
let bidi_info = BidiInfo::new(&text_content, Some(starting_bidi_level));
|
||||||
|
let has_right_to_left_content = bidi_info.has_rtl();
|
||||||
|
|
||||||
let mut new_linebreaker = LineBreaker::new(text_content.as_str());
|
let mut new_linebreaker = LineBreaker::new(text_content.as_str());
|
||||||
for item in builder.inline_items.iter() {
|
for item in builder.inline_items.iter() {
|
||||||
match &mut *item.borrow_mut() {
|
match &mut *item.borrow_mut() {
|
||||||
|
@ -1524,6 +1525,7 @@ impl InlineFormattingContext {
|
||||||
&layout_context.font_context,
|
&layout_context.font_context,
|
||||||
&mut new_linebreaker,
|
&mut new_linebreaker,
|
||||||
&mut font_metrics,
|
&mut font_metrics,
|
||||||
|
&bidi_info,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
InlineItem::StartInlineBox(identifier) => {
|
InlineItem::StartInlineBox(identifier) => {
|
||||||
|
@ -1537,7 +1539,12 @@ impl InlineFormattingContext {
|
||||||
Some(add_or_get_font(&font, &mut font_metrics));
|
Some(add_or_get_font(&font, &mut font_metrics));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {},
|
InlineItem::Atomic(_, index_in_text, bidi_level) => {
|
||||||
|
*bidi_level = bidi_info.levels[*index_in_text];
|
||||||
|
},
|
||||||
|
InlineItem::OutOfFlowAbsolutelyPositionedBox(..) |
|
||||||
|
InlineItem::OutOfFlowFloatBox(_) |
|
||||||
|
InlineItem::EndInlineBox => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1550,6 +1557,7 @@ impl InlineFormattingContext {
|
||||||
has_first_formatted_line,
|
has_first_formatted_line,
|
||||||
contains_floats: builder.contains_floats,
|
contains_floats: builder.contains_floats,
|
||||||
is_single_line_text_input,
|
is_single_line_text_input,
|
||||||
|
has_right_to_left_content,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1630,6 +1638,7 @@ impl InlineFormattingContext {
|
||||||
white_space_collapse: style_text.white_space_collapse,
|
white_space_collapse: style_text.white_space_collapse,
|
||||||
text_wrap_mode: style_text.text_wrap_mode,
|
text_wrap_mode: style_text.text_wrap_mode,
|
||||||
baselines: Baselines::default(),
|
baselines: Baselines::default(),
|
||||||
|
has_right_to_left_content: self.has_right_to_left_content,
|
||||||
};
|
};
|
||||||
|
|
||||||
// FIXME(pcwalton): This assumes that margins never collapse through inline formatting
|
// FIXME(pcwalton): This assumes that margins never collapse through inline formatting
|
||||||
|
@ -1654,15 +1663,16 @@ impl InlineFormattingContext {
|
||||||
},
|
},
|
||||||
InlineItem::EndInlineBox => ifc.finish_inline_box(),
|
InlineItem::EndInlineBox => ifc.finish_inline_box(),
|
||||||
InlineItem::TextRun(run) => run.layout_into_line_items(&mut ifc),
|
InlineItem::TextRun(run) => run.layout_into_line_items(&mut ifc),
|
||||||
InlineItem::Atomic(atomic_formatting_context, offset_in_text) => {
|
InlineItem::Atomic(atomic_formatting_context, offset_in_text, bidi_level) => {
|
||||||
atomic_formatting_context.layout_into_line_items(
|
atomic_formatting_context.layout_into_line_items(
|
||||||
layout_context,
|
layout_context,
|
||||||
self,
|
self,
|
||||||
&mut ifc,
|
&mut ifc,
|
||||||
*offset_in_text,
|
*offset_in_text,
|
||||||
|
*bidi_level,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box) => {
|
InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, _) => {
|
||||||
ifc.push_line_item_to_unbreakable_segment(LineItem::AbsolutelyPositioned(
|
ifc.push_line_item_to_unbreakable_segment(LineItem::AbsolutelyPositioned(
|
||||||
ifc.current_inline_box_identifier(),
|
ifc.current_inline_box_identifier(),
|
||||||
AbsolutelyPositionedLineItem {
|
AbsolutelyPositionedLineItem {
|
||||||
|
@ -1907,6 +1917,7 @@ impl IndependentFormattingContext {
|
||||||
inline_formatting_context: &InlineFormattingContext,
|
inline_formatting_context: &InlineFormattingContext,
|
||||||
inline_formatting_context_state: &mut InlineFormattingContextState,
|
inline_formatting_context_state: &mut InlineFormattingContextState,
|
||||||
offset_in_text: usize,
|
offset_in_text: usize,
|
||||||
|
bidi_level: Level,
|
||||||
) {
|
) {
|
||||||
let style = self.style();
|
let style = self.style();
|
||||||
let container_writing_mode = inline_formatting_context_state
|
let container_writing_mode = inline_formatting_context_state
|
||||||
|
@ -1986,9 +1997,13 @@ impl IndependentFormattingContext {
|
||||||
inline_formatting_context_state
|
inline_formatting_context_state
|
||||||
.containing_block
|
.containing_block
|
||||||
.style
|
.style
|
||||||
.effective_writing_mode(),
|
.writing_mode
|
||||||
containing_block_for_children.effective_writing_mode(),
|
.is_horizontal(),
|
||||||
"Mixed writing modes are not supported yet"
|
containing_block_for_children
|
||||||
|
.style
|
||||||
|
.writing_mode
|
||||||
|
.is_horizontal(),
|
||||||
|
"Mixed horizontal and vertical writing modes are not supported yet"
|
||||||
);
|
);
|
||||||
|
|
||||||
// This always collects for the nearest positioned ancestor even if the parent positioning
|
// This always collects for the nearest positioned ancestor even if the parent positioning
|
||||||
|
@ -2081,6 +2096,7 @@ impl IndependentFormattingContext {
|
||||||
positioning_context: child_positioning_context,
|
positioning_context: child_positioning_context,
|
||||||
baseline_offset_in_parent,
|
baseline_offset_in_parent,
|
||||||
baseline_offset_in_item: baseline_offset,
|
baseline_offset_in_item: baseline_offset,
|
||||||
|
bidi_level,
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -2378,7 +2394,7 @@ impl<'a> ContentSizesComputation<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
InlineItem::Atomic(atomic, offset_in_text) => {
|
InlineItem::Atomic(atomic, offset_in_text, _level) => {
|
||||||
// TODO: need to handle TextWrapMode::Nowrap.
|
// TODO: need to handle TextWrapMode::Nowrap.
|
||||||
if !inline_formatting_context
|
if !inline_formatting_context
|
||||||
.previous_character_prevents_soft_wrap_opportunity(*offset_in_text)
|
.previous_character_prevents_soft_wrap_opportunity(*offset_in_text)
|
||||||
|
|
|
@ -6,6 +6,7 @@ use std::mem;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
|
use base::text::is_bidi_control;
|
||||||
use fonts::{
|
use fonts::{
|
||||||
FontCacheThread, FontContext, FontRef, GlyphRun, ShapingFlags, ShapingOptions,
|
FontCacheThread, FontContext, FontRef, GlyphRun, ShapingFlags, ShapingOptions,
|
||||||
LAST_RESORT_GLYPH_ADVANCE,
|
LAST_RESORT_GLYPH_ADVANCE,
|
||||||
|
@ -21,6 +22,7 @@ use style::computed_values::word_break::T as WordBreak;
|
||||||
use style::properties::ComputedValues;
|
use style::properties::ComputedValues;
|
||||||
use style::str::char_is_whitespace;
|
use style::str::char_is_whitespace;
|
||||||
use style::values::computed::OverflowWrap;
|
use style::values::computed::OverflowWrap;
|
||||||
|
use unicode_bidi::{BidiInfo, Level};
|
||||||
use unicode_script::Script;
|
use unicode_script::Script;
|
||||||
use xi_unicode::linebreak_property;
|
use xi_unicode::linebreak_property;
|
||||||
|
|
||||||
|
@ -73,6 +75,9 @@ pub(crate) struct TextRunSegment {
|
||||||
#[serde(skip_serializing)]
|
#[serde(skip_serializing)]
|
||||||
pub script: Script,
|
pub script: Script,
|
||||||
|
|
||||||
|
/// The bidi Level of this segment.
|
||||||
|
pub bidi_level: Level,
|
||||||
|
|
||||||
/// The range of bytes in the parent [`super::InlineFormattingContext`]'s text content.
|
/// The range of bytes in the parent [`super::InlineFormattingContext`]'s text content.
|
||||||
pub range: Range<usize>,
|
pub range: Range<usize>,
|
||||||
|
|
||||||
|
@ -85,10 +90,11 @@ pub(crate) struct TextRunSegment {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextRunSegment {
|
impl TextRunSegment {
|
||||||
fn new(font_index: usize, script: Script, start_offset: usize) -> Self {
|
fn new(font_index: usize, script: Script, bidi_level: Level, start_offset: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
script,
|
|
||||||
font_index,
|
font_index,
|
||||||
|
script,
|
||||||
|
bidi_level,
|
||||||
range: start_offset..start_offset,
|
range: start_offset..start_offset,
|
||||||
runs: Vec::new(),
|
runs: Vec::new(),
|
||||||
break_at_start: false,
|
break_at_start: false,
|
||||||
|
@ -102,12 +108,17 @@ impl TextRunSegment {
|
||||||
&mut self,
|
&mut self,
|
||||||
new_font: &FontRef,
|
new_font: &FontRef,
|
||||||
script: Script,
|
script: Script,
|
||||||
|
bidi_level: Level,
|
||||||
fonts: &[FontKeyAndMetrics],
|
fonts: &[FontKeyAndMetrics],
|
||||||
) -> bool {
|
) -> 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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if bidi_level != self.bidi_level {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
let current_font_key_and_metrics = &fonts[self.font_index];
|
let current_font_key_and_metrics = &fonts[self.font_index];
|
||||||
if new_font.font_key != current_font_key_and_metrics.key ||
|
if new_font.font_key != current_font_key_and_metrics.key ||
|
||||||
new_font.descriptor.pt_size != current_font_key_and_metrics.pt_size
|
new_font.descriptor.pt_size != current_font_key_and_metrics.pt_size
|
||||||
|
@ -151,6 +162,7 @@ impl TextRunSegment {
|
||||||
run.glyph_store.clone(),
|
run.glyph_store.clone(),
|
||||||
text_run,
|
text_run,
|
||||||
self.font_index,
|
self.font_index,
|
||||||
|
self.bidi_level,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -198,7 +210,7 @@ impl TextRunSegment {
|
||||||
text_style.overflow_wrap == OverflowWrap::Anywhere ||
|
text_style.overflow_wrap == OverflowWrap::Anywhere ||
|
||||||
text_style.overflow_wrap == OverflowWrap::BreakWord;
|
text_style.overflow_wrap == OverflowWrap::BreakWord;
|
||||||
|
|
||||||
let mut last_slice_end = self.range.start;
|
let mut last_slice = self.range.start..self.range.start;
|
||||||
for break_index in linebreak_iter {
|
for break_index in linebreak_iter {
|
||||||
if *break_index == self.range.start {
|
if *break_index == self.range.start {
|
||||||
self.break_at_start = true;
|
self.break_at_start = true;
|
||||||
|
@ -206,12 +218,13 @@ impl TextRunSegment {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extend the slice to the next UAX#14 line break opportunity.
|
// Extend the slice to the next UAX#14 line break opportunity.
|
||||||
let mut slice = last_slice_end..*break_index;
|
let mut slice = last_slice.end..*break_index;
|
||||||
let word = &formatting_context_text[slice.clone()];
|
let word = &formatting_context_text[slice.clone()];
|
||||||
|
|
||||||
// Split off any trailing whitespace into a separate glyph run.
|
// Split off any trailing whitespace into a separate glyph run.
|
||||||
let mut whitespace = slice.end..slice.end;
|
let mut whitespace = slice.end..slice.end;
|
||||||
let mut rev_char_indices = word.char_indices().rev().peekable();
|
let mut rev_char_indices = word.char_indices().rev().peekable();
|
||||||
|
|
||||||
let ends_with_newline = rev_char_indices
|
let ends_with_newline = rev_char_indices
|
||||||
.peek()
|
.peek()
|
||||||
.map_or(false, |&(_, character)| character == '\n');
|
.map_or(false, |&(_, character)| character == '\n');
|
||||||
|
@ -250,8 +263,8 @@ impl TextRunSegment {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only advance the last_slice_end if we are not going to try to expand the slice.
|
// Only advance the last slice if we are not going to try to expand the slice.
|
||||||
last_slice_end = *break_index;
|
last_slice = slice.start..*break_index;
|
||||||
|
|
||||||
// Push the non-whitespace part of the range.
|
// Push the non-whitespace part of the range.
|
||||||
if !slice.is_empty() {
|
if !slice.is_empty() {
|
||||||
|
@ -328,6 +341,7 @@ impl TextRun {
|
||||||
font_context: &FontContext<FontCacheThread>,
|
font_context: &FontContext<FontCacheThread>,
|
||||||
linebreaker: &mut LineBreaker,
|
linebreaker: &mut LineBreaker,
|
||||||
font_cache: &mut Vec<FontKeyAndMetrics>,
|
font_cache: &mut Vec<FontKeyAndMetrics>,
|
||||||
|
bidi_info: &BidiInfo,
|
||||||
) {
|
) {
|
||||||
let inherited_text_style = self.parent_style.get_inherited_text().clone();
|
let inherited_text_style = self.parent_style.get_inherited_text().clone();
|
||||||
let letter_spacing = if inherited_text_style.letter_spacing.0.px() != 0. {
|
let letter_spacing = if inherited_text_style.letter_spacing.0.px() != 0. {
|
||||||
|
@ -349,7 +363,7 @@ impl TextRun {
|
||||||
let style_word_spacing: Option<Au> = specified_word_spacing.to_length().map(|l| l.into());
|
let style_word_spacing: Option<Au> = specified_word_spacing.to_length().map(|l| l.into());
|
||||||
|
|
||||||
let segments = self
|
let segments = self
|
||||||
.segment_text_by_font(formatting_context_text, font_context, font_cache)
|
.segment_text_by_font(formatting_context_text, font_context, font_cache, bidi_info)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(mut segment, font)| {
|
.map(|(mut segment, font)| {
|
||||||
let word_spacing = style_word_spacing.unwrap_or_else(|| {
|
let word_spacing = style_word_spacing.unwrap_or_else(|| {
|
||||||
|
@ -360,6 +374,10 @@ impl TextRun {
|
||||||
specified_word_spacing.to_used_value(Au::from_f64_px(space_width))
|
specified_word_spacing.to_used_value(Au::from_f64_px(space_width))
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let mut flags = flags.clone();
|
||||||
|
if segment.bidi_level.is_rtl() {
|
||||||
|
flags.insert(ShapingFlags::RTL_FLAG);
|
||||||
|
}
|
||||||
let shaping_options = ShapingOptions {
|
let shaping_options = ShapingOptions {
|
||||||
letter_spacing,
|
letter_spacing,
|
||||||
word_spacing,
|
word_spacing,
|
||||||
|
@ -390,6 +408,7 @@ impl TextRun {
|
||||||
formatting_context_text: &str,
|
formatting_context_text: &str,
|
||||||
font_context: &FontContext<FontCacheThread>,
|
font_context: &FontContext<FontCacheThread>,
|
||||||
font_cache: &mut Vec<FontKeyAndMetrics>,
|
font_cache: &mut Vec<FontKeyAndMetrics>,
|
||||||
|
bidi_info: &BidiInfo,
|
||||||
) -> Vec<(TextRunSegment, FontRef)> {
|
) -> Vec<(TextRunSegment, FontRef)> {
|
||||||
let font_group = font_context.font_group(self.parent_style.clone_font());
|
let font_group = font_context.font_group(self.parent_style.clone_font());
|
||||||
let mut current: Option<(TextRunSegment, FontRef)> = None;
|
let mut current: Option<(TextRunSegment, FontRef)> = None;
|
||||||
|
@ -416,8 +435,12 @@ impl TextRun {
|
||||||
|
|
||||||
// If the existing segment is compatible with the character, keep going.
|
// If the existing segment is compatible with the character, keep going.
|
||||||
let script = Script::from(character);
|
let script = Script::from(character);
|
||||||
|
let bidi_level = bidi_info.levels[current_byte_index];
|
||||||
if let Some(current) = current.as_mut() {
|
if let Some(current) = current.as_mut() {
|
||||||
if current.0.update_if_compatible(&font, script, font_cache) {
|
if current
|
||||||
|
.0
|
||||||
|
.update_if_compatible(&font, script, bidi_level, font_cache)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -433,7 +456,7 @@ impl TextRun {
|
||||||
None => self.text_range.start,
|
None => self.text_range.start,
|
||||||
};
|
};
|
||||||
let new = (
|
let new = (
|
||||||
TextRunSegment::new(font_index, script, start_byte_index),
|
TextRunSegment::new(font_index, script, bidi_level, start_byte_index),
|
||||||
font,
|
font,
|
||||||
);
|
);
|
||||||
if let Some(mut finished) = current.replace(new) {
|
if let Some(mut finished) = current.replace(new) {
|
||||||
|
@ -449,7 +472,12 @@ impl TextRun {
|
||||||
current = font_group.write().first(font_context).map(|font| {
|
current = font_group.write().first(font_context).map(|font| {
|
||||||
let font_index = add_or_get_font(&font, font_cache);
|
let font_index = add_or_get_font(&font, font_cache);
|
||||||
(
|
(
|
||||||
TextRunSegment::new(font_index, Script::Common, self.text_range.start),
|
TextRunSegment::new(
|
||||||
|
font_index,
|
||||||
|
Script::Common,
|
||||||
|
Level::ltr(),
|
||||||
|
self.text_range.start,
|
||||||
|
),
|
||||||
font,
|
font,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -496,6 +524,10 @@ fn char_does_not_change_font(character: char) -> bool {
|
||||||
if character == '\u{00A0}' {
|
if character == '\u{00A0}' {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if is_bidi_control(character) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
let class = linebreak_property(character);
|
let class = linebreak_property(character);
|
||||||
class == XI_LINE_BREAKING_CLASS_CM ||
|
class == XI_LINE_BREAKING_CLASS_CM ||
|
||||||
class == XI_LINE_BREAKING_CLASS_GL ||
|
class == XI_LINE_BREAKING_CLASS_GL ||
|
||||||
|
|
|
@ -123,7 +123,7 @@ impl BoxTree {
|
||||||
#[allow(clippy::enum_variant_names)]
|
#[allow(clippy::enum_variant_names)]
|
||||||
enum UpdatePoint {
|
enum UpdatePoint {
|
||||||
AbsolutelyPositionedBlockLevelBox(ArcRefCell<BlockLevelBox>),
|
AbsolutelyPositionedBlockLevelBox(ArcRefCell<BlockLevelBox>),
|
||||||
AbsolutelyPositionedInlineLevelBox(ArcRefCell<InlineItem>),
|
AbsolutelyPositionedInlineLevelBox(ArcRefCell<InlineItem>, usize),
|
||||||
AbsolutelyPositionedFlexLevelBox(ArcRefCell<FlexLevelBox>),
|
AbsolutelyPositionedFlexLevelBox(ArcRefCell<FlexLevelBox>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,11 +183,12 @@ impl BoxTree {
|
||||||
},
|
},
|
||||||
LayoutBox::InlineBox(_) => return None,
|
LayoutBox::InlineBox(_) => return None,
|
||||||
LayoutBox::InlineLevel(inline_level_box) => match &*inline_level_box.borrow() {
|
LayoutBox::InlineLevel(inline_level_box) => match &*inline_level_box.borrow() {
|
||||||
InlineItem::OutOfFlowAbsolutelyPositionedBox(_)
|
InlineItem::OutOfFlowAbsolutelyPositionedBox(_, text_offset_index)
|
||||||
if box_style.position.is_absolutely_positioned() =>
|
if box_style.position.is_absolutely_positioned() =>
|
||||||
{
|
{
|
||||||
UpdatePoint::AbsolutelyPositionedInlineLevelBox(
|
UpdatePoint::AbsolutelyPositionedInlineLevelBox(
|
||||||
inline_level_box.clone(),
|
inline_level_box.clone(),
|
||||||
|
*text_offset_index,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
_ => return None,
|
_ => return None,
|
||||||
|
@ -219,10 +220,14 @@ impl BoxTree {
|
||||||
out_of_flow_absolutely_positioned_box,
|
out_of_flow_absolutely_positioned_box,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
UpdatePoint::AbsolutelyPositionedInlineLevelBox(inline_level_box) => {
|
UpdatePoint::AbsolutelyPositionedInlineLevelBox(
|
||||||
|
inline_level_box,
|
||||||
|
text_offset_index,
|
||||||
|
) => {
|
||||||
*inline_level_box.borrow_mut() =
|
*inline_level_box.borrow_mut() =
|
||||||
InlineItem::OutOfFlowAbsolutelyPositionedBox(
|
InlineItem::OutOfFlowAbsolutelyPositionedBox(
|
||||||
out_of_flow_absolutely_positioned_box,
|
out_of_flow_absolutely_positioned_box,
|
||||||
|
text_offset_index,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
UpdatePoint::AbsolutelyPositionedFlexLevelBox(flex_level_box) => {
|
UpdatePoint::AbsolutelyPositionedFlexLevelBox(flex_level_box) => {
|
||||||
|
|
|
@ -8,9 +8,7 @@ use std::ops::{Add, AddAssign, Neg, Sub, SubAssign};
|
||||||
|
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use style::logical_geometry::{
|
use style::logical_geometry::{BlockFlowDirection, InlineBaseDirection, WritingMode};
|
||||||
BlockFlowDirection, InlineBaseDirection, PhysicalCorner, WritingMode,
|
|
||||||
};
|
|
||||||
use style::values::computed::{CSSPixelLength, Length, LengthPercentage};
|
use style::values::computed::{CSSPixelLength, Length, LengthPercentage};
|
||||||
use style::values::generics::length::GenericLengthPercentageOrAuto as AutoOr;
|
use style::values::generics::length::GenericLengthPercentageOrAuto as AutoOr;
|
||||||
use style::Zero;
|
use style::Zero;
|
||||||
|
@ -489,15 +487,10 @@ impl<T> LogicalRect<T> {
|
||||||
|
|
||||||
pub fn to_physical(&self, mode: WritingMode) -> PhysicalRect<T>
|
pub fn to_physical(&self, mode: WritingMode) -> PhysicalRect<T>
|
||||||
where
|
where
|
||||||
T: Clone,
|
T: Copy,
|
||||||
{
|
{
|
||||||
// Top-left corner
|
|
||||||
let (tl_x, tl_y) = match mode.start_start_physical_corner() {
|
|
||||||
PhysicalCorner::TopLeft => (&self.start_corner.inline, &self.start_corner.block),
|
|
||||||
_ => unimplemented!(),
|
|
||||||
};
|
|
||||||
PhysicalRect::new(
|
PhysicalRect::new(
|
||||||
PhysicalPoint::new(tl_x.clone(), tl_y.clone()),
|
self.start_corner.to_physical_point(mode),
|
||||||
self.size.to_physical_size(mode),
|
self.size.to_physical_size(mode),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,12 @@
|
||||||
* 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 app_units::Au;
|
use app_units::Au;
|
||||||
|
use style::computed_values::direction::T as Direction;
|
||||||
use style::computed_values::mix_blend_mode::T as ComputedMixBlendMode;
|
use style::computed_values::mix_blend_mode::T as ComputedMixBlendMode;
|
||||||
use style::computed_values::position::T as ComputedPosition;
|
use style::computed_values::position::T as ComputedPosition;
|
||||||
use style::computed_values::transform_style::T as ComputedTransformStyle;
|
use style::computed_values::transform_style::T as ComputedTransformStyle;
|
||||||
use style::logical_geometry::{Direction, WritingMode};
|
use style::computed_values::unicode_bidi::T as UnicodeBidi;
|
||||||
|
use style::logical_geometry::{Direction as AxisDirection, WritingMode};
|
||||||
use style::properties::longhands::backface_visibility::computed_value::T as BackfaceVisiblity;
|
use style::properties::longhands::backface_visibility::computed_value::T as BackfaceVisiblity;
|
||||||
use style::properties::longhands::box_sizing::computed_value::T as BoxSizing;
|
use style::properties::longhands::box_sizing::computed_value::T as BoxSizing;
|
||||||
use style::properties::longhands::column_span::computed_value::T as ColumnSpan;
|
use style::properties::longhands::column_span::computed_value::T as ColumnSpan;
|
||||||
|
@ -117,7 +119,7 @@ impl DisplayLayoutInternal {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Percentages resolved but not `auto` margins
|
/// Percentages resolved but not `auto` margins
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) struct PaddingBorderMargin {
|
pub(crate) struct PaddingBorderMargin {
|
||||||
pub padding: LogicalSides<Au>,
|
pub padding: LogicalSides<Au>,
|
||||||
pub border: LogicalSides<Au>,
|
pub border: LogicalSides<Au>,
|
||||||
|
@ -163,17 +165,17 @@ impl AspectRatio {
|
||||||
/// Given one side length, compute the other one.
|
/// Given one side length, compute the other one.
|
||||||
pub(crate) fn compute_dependent_size(
|
pub(crate) fn compute_dependent_size(
|
||||||
&self,
|
&self,
|
||||||
ratio_dependent_axis: Direction,
|
ratio_dependent_axis: AxisDirection,
|
||||||
ratio_determining_size: Au,
|
ratio_determining_size: Au,
|
||||||
) -> Au {
|
) -> Au {
|
||||||
match ratio_dependent_axis {
|
match ratio_dependent_axis {
|
||||||
// Calculate the inline size from the block size
|
// Calculate the inline size from the block size
|
||||||
Direction::Inline => {
|
AxisDirection::Inline => {
|
||||||
(ratio_determining_size + self.box_sizing_adjustment.block).scale_by(self.i_over_b) -
|
(ratio_determining_size + self.box_sizing_adjustment.block).scale_by(self.i_over_b) -
|
||||||
self.box_sizing_adjustment.inline
|
self.box_sizing_adjustment.inline
|
||||||
},
|
},
|
||||||
// Calculate the block size from the inline size
|
// Calculate the block size from the inline size
|
||||||
Direction::Block => {
|
AxisDirection::Block => {
|
||||||
(ratio_determining_size + self.box_sizing_adjustment.inline)
|
(ratio_determining_size + self.box_sizing_adjustment.inline)
|
||||||
.scale_by(1.0 / self.i_over_b) -
|
.scale_by(1.0 / self.i_over_b) -
|
||||||
self.box_sizing_adjustment.block
|
self.box_sizing_adjustment.block
|
||||||
|
@ -264,6 +266,7 @@ pub(crate) trait ComputedValuesExt {
|
||||||
) -> Option<AspectRatio>;
|
) -> Option<AspectRatio>;
|
||||||
fn background_is_transparent(&self) -> bool;
|
fn background_is_transparent(&self) -> bool;
|
||||||
fn get_webrender_primitive_flags(&self) -> wr::PrimitiveFlags;
|
fn get_webrender_primitive_flags(&self) -> wr::PrimitiveFlags;
|
||||||
|
fn bidi_control_chars(&self) -> (&'static str, &'static str);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ComputedValuesExt for ComputedValues {
|
impl ComputedValuesExt for ComputedValues {
|
||||||
|
@ -752,6 +755,31 @@ impl ComputedValuesExt for ComputedValues {
|
||||||
BackfaceVisiblity::Hidden => wr::PrimitiveFlags::empty(),
|
BackfaceVisiblity::Hidden => wr::PrimitiveFlags::empty(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If the 'unicode-bidi' property has a value other than 'normal', return the bidi control codes
|
||||||
|
/// to inject before and after the text content of the element.
|
||||||
|
/// See the table in <http://dev.w3.org/csswg/css-writing-modes/#unicode-bidi>.
|
||||||
|
fn bidi_control_chars(&self) -> (&'static str, &'static str) {
|
||||||
|
match (
|
||||||
|
self.get_text().unicode_bidi,
|
||||||
|
self.get_inherited_box().direction,
|
||||||
|
) {
|
||||||
|
(UnicodeBidi::Normal, _) => ("", ""),
|
||||||
|
(UnicodeBidi::Embed, Direction::Ltr) => ("\u{202a}", "\u{202c}"),
|
||||||
|
(UnicodeBidi::Embed, Direction::Rtl) => ("\u{202b}", "\u{202c}"),
|
||||||
|
(UnicodeBidi::Isolate, Direction::Ltr) => ("\u{2066}", "\u{2069}"),
|
||||||
|
(UnicodeBidi::Isolate, Direction::Rtl) => ("\u{2067}", "\u{2069}"),
|
||||||
|
(UnicodeBidi::BidiOverride, Direction::Ltr) => ("\u{202d}", "\u{202c}"),
|
||||||
|
(UnicodeBidi::BidiOverride, Direction::Rtl) => ("\u{202e}", "\u{202c}"),
|
||||||
|
(UnicodeBidi::IsolateOverride, Direction::Ltr) => {
|
||||||
|
("\u{2068}\u{202d}", "\u{202c}\u{2069}")
|
||||||
|
},
|
||||||
|
(UnicodeBidi::IsolateOverride, Direction::Rtl) => {
|
||||||
|
("\u{2068}\u{202e}", "\u{202c}\u{2069}")
|
||||||
|
},
|
||||||
|
(UnicodeBidi::Plaintext, _) => ("\u{2068}", "\u{2069}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<stylo::Display> for Display {
|
impl From<stylo::Display> for Display {
|
||||||
|
|
|
@ -188,6 +188,12 @@ svg > * {
|
||||||
all: inherit;
|
all: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*|*::-servo-anonymous-box {
|
||||||
|
unicode-bidi: inherit;
|
||||||
|
direction: inherit;
|
||||||
|
writing-mode: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
/* style for text node. */
|
/* style for text node. */
|
||||||
*|*::-servo-legacy-text {
|
*|*::-servo-legacy-text {
|
||||||
text-overflow: inherit;
|
text-overflow: inherit;
|
||||||
|
|
|
@ -130,14 +130,82 @@ wbr { display-outside: break-opportunity; } /* this also has bidi implications *
|
||||||
nobr wbr { white-space: normal; }
|
nobr wbr { white-space: normal; }
|
||||||
|
|
||||||
|
|
||||||
[dir]:dir(ltr), bdi:dir(ltr), input[type=tel]:dir(ltr) { direction: ltr; }
|
/* Eventually we will want the following, but currently Servo does not
|
||||||
[dir]:dir(rtl), bdi:dir(rtl) { direction: rtl; }
|
properly parse the :dir pseudo-selector.
|
||||||
|
[dir=ltr i], bdi:dir(ltr), input[type=tel]:dir(ltr) { direction: ltr; }
|
||||||
|
*/
|
||||||
|
[dir=ltr i] { direction: ltr; }
|
||||||
|
[dir=rtl i] { direction: rtl; }
|
||||||
|
[dir=ltr i], [dir=rtl i], [dir=auto i] { unicode-bidi: isolate; }
|
||||||
|
|
||||||
address, blockquote, center, div, figure, figcaption, footer, form, header, hr,
|
/* To ensure http://www.w3.org/TR/REC-html40/struct/dirlang.html#style-bidi:
|
||||||
legend, listing, main, p, plaintext, pre, summary, xmp, article, aside, h1, h2,
|
*
|
||||||
h3, h4, h5, h6, hgroup, nav, section, table, caption, colgroup, col, thead,
|
* "When a block element that does not have a dir attribute is transformed to
|
||||||
tbody, tfoot, tr, td, th, dir, dd, dl, dt, menu, ol, ul, li, bdi, output,
|
* the style of an inline element by a style sheet, the resulting presentation
|
||||||
[dir=ltr i], [dir=rtl i], [dir=auto i] {
|
* should be equivalent, in terms of bidirectional formatting, to the
|
||||||
|
* formatting obtained by explicitly adding a dir attribute (assigned the
|
||||||
|
* inherited value) to the transformed element."
|
||||||
|
*
|
||||||
|
* and the rules in http://dev.w3.org/html5/spec/rendering.html#rendering
|
||||||
|
*/
|
||||||
|
address,
|
||||||
|
article,
|
||||||
|
aside,
|
||||||
|
blockquote,
|
||||||
|
body,
|
||||||
|
caption,
|
||||||
|
center,
|
||||||
|
col,
|
||||||
|
colgroup,
|
||||||
|
dd,
|
||||||
|
dir,
|
||||||
|
div,
|
||||||
|
dl,
|
||||||
|
dt,
|
||||||
|
fieldset,
|
||||||
|
figcaption,
|
||||||
|
figure,
|
||||||
|
footer,
|
||||||
|
form,
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6,
|
||||||
|
header,
|
||||||
|
hgroup,
|
||||||
|
hr,
|
||||||
|
html,
|
||||||
|
legend,
|
||||||
|
li,
|
||||||
|
listing,
|
||||||
|
main,
|
||||||
|
marquee,
|
||||||
|
menu,
|
||||||
|
nav,
|
||||||
|
noframes,
|
||||||
|
ol,
|
||||||
|
p,
|
||||||
|
plaintext,
|
||||||
|
pre,
|
||||||
|
search,
|
||||||
|
section,
|
||||||
|
summary,
|
||||||
|
table,
|
||||||
|
tbody,
|
||||||
|
td,
|
||||||
|
tfoot,
|
||||||
|
th,
|
||||||
|
thead,
|
||||||
|
tr,
|
||||||
|
ul,
|
||||||
|
xmp
|
||||||
|
{
|
||||||
|
unicode-bidi: isolate;
|
||||||
|
}
|
||||||
|
|
||||||
|
bdi, output {
|
||||||
unicode-bidi: isolate;
|
unicode-bidi: isolate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
[bidi-box-model-006.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[bidi-breaking-001.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[bidi-breaking-002.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[bidi-glyph-mirroring-002.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[bidi-inline-001.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[bidi-inline-002.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[direction-002.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[direction-003.xht]
|
|
||||||
expected: FAIL
|
|
2
tests/wpt/meta/css/CSS2/bidi-text/direction-applies-to-001.xht.ini
vendored
Normal file
2
tests/wpt/meta/css/CSS2/bidi-text/direction-applies-to-001.xht.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[direction-applies-to-001.xht]
|
||||||
|
expected: FAIL
|
2
tests/wpt/meta/css/CSS2/bidi-text/direction-applies-to-002.xht.ini
vendored
Normal file
2
tests/wpt/meta/css/CSS2/bidi-text/direction-applies-to-002.xht.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[direction-applies-to-002.xht]
|
||||||
|
expected: FAIL
|
2
tests/wpt/meta/css/CSS2/bidi-text/direction-applies-to-003.xht.ini
vendored
Normal file
2
tests/wpt/meta/css/CSS2/bidi-text/direction-applies-to-003.xht.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[direction-applies-to-003.xht]
|
||||||
|
expected: FAIL
|
2
tests/wpt/meta/css/CSS2/bidi-text/direction-applies-to-004.xht.ini
vendored
Normal file
2
tests/wpt/meta/css/CSS2/bidi-text/direction-applies-to-004.xht.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[direction-applies-to-004.xht]
|
||||||
|
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
||||||
[direction-applies-to-007.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[direction-applies-to-008.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[direction-applies-to-009.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[direction-applies-to-012.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[direction-applies-to-013.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[direction-applies-to-014.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[direction-applies-to-015.xht]
|
|
||||||
expected: FAIL
|
|
2
tests/wpt/meta/css/CSS2/bidi-text/line-breaking-bidi-003.xht.ini
vendored
Normal file
2
tests/wpt/meta/css/CSS2/bidi-text/line-breaking-bidi-003.xht.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[line-breaking-bidi-003.xht]
|
||||||
|
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
||||||
[unicode-bidi-001.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[unicode-bidi-002.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[unicode-bidi-applies-to-008.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[bidi-generated-content-001.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[bidi-generated-content-002.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[inline-formatting-context-023.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +1,2 @@
|
||||||
[bidi-001.xht]
|
[blocks-018.xht]
|
||||||
expected: FAIL
|
expected: FAIL
|
|
@ -1,2 +1,2 @@
|
||||||
[bidi-002.xht]
|
[blocks-019.xht]
|
||||||
expected: FAIL
|
expected: FAIL
|
2
tests/wpt/meta/css/CSS2/normal-flow/replaced-intrinsic-002.xht.ini
vendored
Normal file
2
tests/wpt/meta/css/CSS2/normal-flow/replaced-intrinsic-002.xht.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[replaced-intrinsic-002.xht]
|
||||||
|
expected: FAIL
|
2
tests/wpt/meta/css/CSS2/positioning/abspos-block-level-001.html.ini
vendored
Normal file
2
tests/wpt/meta/css/CSS2/positioning/abspos-block-level-001.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[abspos-block-level-001.html]
|
||||||
|
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
||||||
[left-offset-003.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[left-offset-percentage-001.xht]
|
|
||||||
expected: FAIL
|
|
2
tests/wpt/meta/css/CSS2/text/bidi-flag-emoji.html.ini
vendored
Normal file
2
tests/wpt/meta/css/CSS2/text/bidi-flag-emoji.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[bidi-flag-emoji.html]
|
||||||
|
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
||||||
[text-align-white-space-006.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[text-align-white-space-008.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[text-indent-rtl-002.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[white-space-bidirectionality-001.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[white-space-collapsing-bidi-002.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[inline-formatting-context-001.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[all-prop-001.html]
|
|
||||||
expected: FAIL
|
|
2
tests/wpt/meta/css/css-flexbox/gap-001-rtl.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-flexbox/gap-001-rtl.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[gap-001-rtl.html]
|
||||||
|
expected: FAIL
|
2
tests/wpt/meta/css/css-flexbox/gap-004-rtl.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-flexbox/gap-004-rtl.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[gap-004-rtl.html]
|
||||||
|
expected: FAIL
|
2
tests/wpt/meta/css/css-flexbox/gap-007-rtl.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-flexbox/gap-007-rtl.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[gap-007-rtl.html]
|
||||||
|
expected: FAIL
|
2
tests/wpt/meta/css/css-lists/inline-list.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-lists/inline-list.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[inline-list.html]
|
||||||
|
expected: FAIL
|
|
@ -3,9 +3,6 @@
|
||||||
[Logical properties in animations respect the writing-mode]
|
[Logical properties in animations respect the writing-mode]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Logical properties in animations respect the direction]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Logical properties are able to override physical properties in @keyframes declaration blocks]
|
[Logical properties are able to override physical properties in @keyframes declaration blocks]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -26,6 +23,3 @@
|
||||||
|
|
||||||
[Animations update when the writing-mode is changed through a CSS variable]
|
[Animations update when the writing-mode is changed through a CSS variable]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Animations update when the direction is changed]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -2,9 +2,6 @@
|
||||||
[Logical properties in transitions respect the writing-mode]
|
[Logical properties in transitions respect the writing-mode]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Logical properties in transitions respect the direction]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Transitions update when the writing-mode is changed]
|
[Transitions update when the writing-mode is changed]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -20,11 +17,5 @@
|
||||||
[Transitions update when the writing-mode is changed through a CSS variable]
|
[Transitions update when the writing-mode is changed through a CSS variable]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Transitions update when the direction is changed]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Transitions from logical to physical update when the direction is changed]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Transitions from physical to logical update when the direction is changed]
|
[Transitions from physical to logical update when the direction is changed]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
|
@ -1,13 +1,4 @@
|
||||||
[logical-box-border-shorthands.html]
|
[logical-box-border-shorthands.html]
|
||||||
[Test that logical border-* properties share computed values with their physical associates, with 'writing-mode: horizontal-tb; direction: rtl; '.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Test that border-* properties honor order of appearance when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Test that border-* properties honor selector specificty when both logical and physical associates are declared, with 'writing-mode: horizontal-tb; direction: rtl; '.]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Test that logical border-* properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: rtl; '.]
|
[Test that logical border-* properties share computed values with their physical associates, with 'writing-mode: vertical-rl; direction: rtl; '.]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
[logical-values-float-clear-1.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[logical-values-float-clear-2.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[logical-values-float-clear-3.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[logical-values-float-clear-4.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,7 +1,4 @@
|
||||||
[marker-default-styles.html]
|
[marker-default-styles.html]
|
||||||
[Computed value of 'unicode-bidi' for outside symbol]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Computed value of 'font-variant-numeric' for outside symbol]
|
[Computed value of 'font-variant-numeric' for outside symbol]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -11,9 +8,6 @@
|
||||||
[Computed value of 'text-indent' for outside symbol]
|
[Computed value of 'text-indent' for outside symbol]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Computed value of 'unicode-bidi' for outside decimal]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Computed value of 'font-variant-numeric' for outside decimal]
|
[Computed value of 'font-variant-numeric' for outside decimal]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -23,9 +17,6 @@
|
||||||
[Computed value of 'text-indent' for outside decimal]
|
[Computed value of 'text-indent' for outside decimal]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Computed value of 'unicode-bidi' for outside string]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Computed value of 'font-variant-numeric' for outside string]
|
[Computed value of 'font-variant-numeric' for outside string]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -35,9 +26,6 @@
|
||||||
[Computed value of 'text-indent' for outside string]
|
[Computed value of 'text-indent' for outside string]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Computed value of 'unicode-bidi' for outside marker]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Computed value of 'font-variant-numeric' for outside marker]
|
[Computed value of 'font-variant-numeric' for outside marker]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -47,9 +35,6 @@
|
||||||
[Computed value of 'text-indent' for outside marker]
|
[Computed value of 'text-indent' for outside marker]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Computed value of 'unicode-bidi' for inside symbol]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Computed value of 'font-variant-numeric' for inside symbol]
|
[Computed value of 'font-variant-numeric' for inside symbol]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -59,9 +44,6 @@
|
||||||
[Computed value of 'text-indent' for inside symbol]
|
[Computed value of 'text-indent' for inside symbol]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Computed value of 'unicode-bidi' for inside decimal]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Computed value of 'font-variant-numeric' for inside decimal]
|
[Computed value of 'font-variant-numeric' for inside decimal]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -71,9 +53,6 @@
|
||||||
[Computed value of 'text-indent' for inside decimal]
|
[Computed value of 'text-indent' for inside decimal]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Computed value of 'unicode-bidi' for inside string]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Computed value of 'font-variant-numeric' for inside string]
|
[Computed value of 'font-variant-numeric' for inside string]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -83,9 +62,6 @@
|
||||||
[Computed value of 'text-indent' for inside string]
|
[Computed value of 'text-indent' for inside string]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Computed value of 'unicode-bidi' for inside marker]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Computed value of 'font-variant-numeric' for inside marker]
|
[Computed value of 'font-variant-numeric' for inside marker]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
[marker-unicode-bidi-default-ref.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[marker-unicode-bidi-normal-ref.html]
|
|
||||||
expected: FAIL
|
|
2
tests/wpt/meta/css/css-sizing/clone-nowrap-intrinsic-size-bidi.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-sizing/clone-nowrap-intrinsic-size-bidi.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[clone-nowrap-intrinsic-size-bidi.html]
|
||||||
|
expected: FAIL
|
2
tests/wpt/meta/css/css-sizing/slice-nowrap-intrinsic-size-bidi.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-sizing/slice-nowrap-intrinsic-size-bidi.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[slice-nowrap-intrinsic-size-bidi.html]
|
||||||
|
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
||||||
[text-decoration-skip-spaces-003.html]
|
|
||||||
expected: FAIL
|
|
2
tests/wpt/meta/css/css-text/bidi/bidi-lines-001.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-text/bidi/bidi-lines-001.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[bidi-lines-001.html]
|
||||||
|
expected: FAIL
|
2
tests/wpt/meta/css/css-text/bidi/bidi-lines-002.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-text/bidi/bidi-lines-002.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[bidi-lines-002.html]
|
||||||
|
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
||||||
[text-align-007.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[text-align-end-001.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[text-align-end-003.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[text-align-end-005.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[text-align-end-007.html]
|
|
||||||
expected: FAIL
|
|
2
tests/wpt/meta/css/css-text/text-align/text-align-end-010.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-text/text-align/text-align-end-010.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[text-align-end-010.html]
|
||||||
|
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
||||||
[text-align-end-014.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[text-align-justify-001.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[text-align-justify-003.html]
|
|
||||||
expected: FAIL
|
|
2
tests/wpt/meta/css/css-text/text-align/text-align-justify-006.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-text/text-align/text-align-justify-006.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[text-align-justify-006.html]
|
||||||
|
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
||||||
[text-align-last-010.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[text-align-last-011.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[text-align-last-justify-rtl.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[text-align-match-parent-001.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[text-align-start-001.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[text-align-start-003.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[text-align-start-005.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[text-align-start-007.html]
|
|
||||||
expected: FAIL
|
|
2
tests/wpt/meta/css/css-text/text-align/text-align-start-010.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-text/text-align/text-align-start-010.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[text-align-start-010.html]
|
||||||
|
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
||||||
[text-align-start-014.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[shaping-join-001.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[shaping-join-002.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[shaping-no-join-001.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[shaping-no-join-002.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[shaping-tatweel-001.html]
|
|
||||||
expected: FAIL
|
|
2
tests/wpt/meta/css/css-text/white-space/eol-spaces-bidi-002.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-text/white-space/eol-spaces-bidi-002.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[eol-spaces-bidi-002.html]
|
||||||
|
expected: FAIL
|
2
tests/wpt/meta/css/css-text/white-space/pre-wrap-018.html.ini
vendored
Normal file
2
tests/wpt/meta/css/css-text/white-space/pre-wrap-018.html.ini
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[pre-wrap-018.html]
|
||||||
|
expected: FAIL
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue