mirror of
https://github.com/servo/servo.git
synced 2025-08-03 20:50:07 +01:00
Auto merge of #22111 - emilio:gecko-sync, r=emilio
style: Sync changes from mozilla-central. See each individual commit for details. https://bugzilla.mozilla.org/show_bug.cgi?id=1504644 <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/22111) <!-- Reviewable:end -->
This commit is contained in:
commit
ff7c6953c8
47 changed files with 1330 additions and 1091 deletions
22
Cargo.lock
generated
22
Cargo.lock
generated
|
@ -329,7 +329,7 @@ dependencies = [
|
||||||
"azure 0.34.0 (git+https://github.com/servo/rust-azure)",
|
"azure 0.34.0 (git+https://github.com/servo/rust-azure)",
|
||||||
"canvas_traits 0.0.1",
|
"canvas_traits 0.0.1",
|
||||||
"compositing 0.0.1",
|
"compositing 0.0.1",
|
||||||
"cssparser 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cssparser 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gleam 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gleam 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -348,7 +348,7 @@ dependencies = [
|
||||||
name = "canvas_traits"
|
name = "canvas_traits"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cssparser 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cssparser 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gleam 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gleam 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ipc-channel 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ipc-channel 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -717,7 +717,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cssparser"
|
name = "cssparser"
|
||||||
version = "0.24.0"
|
version = "0.25.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cssparser-macros 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cssparser-macros 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -2200,7 +2200,7 @@ name = "malloc_size_of"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"app_units 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"app_units 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"cssparser 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cssparser 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"hashglobe 0.1.0",
|
"hashglobe 0.1.0",
|
||||||
"hyper 0.12.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hyper 0.12.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -3180,7 +3180,7 @@ dependencies = [
|
||||||
"chrono 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"chrono 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"cmake 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cmake 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"cookie 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cookie 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"cssparser 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cssparser 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"deny_public_fields 0.0.1",
|
"deny_public_fields 0.0.1",
|
||||||
"devtools_traits 0.0.1",
|
"devtools_traits 0.0.1",
|
||||||
"dom_struct 0.0.1",
|
"dom_struct 0.0.1",
|
||||||
|
@ -3265,7 +3265,7 @@ dependencies = [
|
||||||
"app_units 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"app_units 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"canvas_traits 0.0.1",
|
"canvas_traits 0.0.1",
|
||||||
"cssparser 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cssparser 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gfx_traits 0.0.1",
|
"gfx_traits 0.0.1",
|
||||||
"html5ever 0.22.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"html5ever 0.22.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -3350,7 +3350,7 @@ name = "selectors"
|
||||||
version = "0.20.0"
|
version = "0.20.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"cssparser 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cssparser 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -3785,7 +3785,7 @@ dependencies = [
|
||||||
"bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"cssparser 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cssparser 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"encoding_rs 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"encoding_rs 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"fallible 0.0.1",
|
"fallible 0.0.1",
|
||||||
|
@ -3848,7 +3848,7 @@ version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"app_units 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"app_units 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"cssparser 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cssparser 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"html5ever 0.22.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"html5ever 0.22.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parking_lot 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parking_lot 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -3871,7 +3871,7 @@ version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"app_units 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"app_units 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"cssparser 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cssparser 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"malloc_size_of 0.0.1",
|
"malloc_size_of 0.0.1",
|
||||||
"malloc_size_of_derive 0.0.1",
|
"malloc_size_of_derive 0.0.1",
|
||||||
|
@ -4803,7 +4803,7 @@ dependencies = [
|
||||||
"checksum crossbeam-epoch 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9c90f1474584f38e270b5b613e898c8c328aa4f3dea85e0a27ac2e642f009416"
|
"checksum crossbeam-epoch 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9c90f1474584f38e270b5b613e898c8c328aa4f3dea85e0a27ac2e642f009416"
|
||||||
"checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9"
|
"checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9"
|
||||||
"checksum crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "677d453a17e8bd2b913fa38e8b9cf04bcdbb5be790aa294f2389661d72036015"
|
"checksum crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "677d453a17e8bd2b913fa38e8b9cf04bcdbb5be790aa294f2389661d72036015"
|
||||||
"checksum cssparser 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)" = "495beddc39b1987b8e9f029354eccbd5ef88eb5f1cd24badb764dce338acf2e0"
|
"checksum cssparser 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)" = "730363a45c4e248d4f21d3e5c1156d1a9cdec0855056c0d9539e814bc59865c3"
|
||||||
"checksum cssparser-macros 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f3a5383ae18dbfdeb569ed62019f5bddb2a95cd2d3833313c475a0d014777805"
|
"checksum cssparser-macros 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f3a5383ae18dbfdeb569ed62019f5bddb2a95cd2d3833313c475a0d014777805"
|
||||||
"checksum darling 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2a78af487e4eb8f4421a1770687b328af6bb4494ca93435210678c6eea875c11"
|
"checksum darling 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2a78af487e4eb8f4421a1770687b328af6bb4494ca93435210678c6eea875c11"
|
||||||
"checksum darling_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b315f49c7b6db3708bca6e6913c194581a44ec619b7a39e131d4dd63733a3698"
|
"checksum darling_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b315f49c7b6db3708bca6e6913c194581a44ec619b7a39e131d4dd63733a3698"
|
||||||
|
|
|
@ -70,6 +70,10 @@ resize
|
||||||
right
|
right
|
||||||
rtl
|
rtl
|
||||||
sans-serif
|
sans-serif
|
||||||
|
safe-area-inset-top
|
||||||
|
safe-area-inset-bottom
|
||||||
|
safe-area-inset-left
|
||||||
|
safe-area-inset-right
|
||||||
scan
|
scan
|
||||||
screen
|
screen
|
||||||
scroll-position
|
scroll-position
|
||||||
|
|
|
@ -16,7 +16,7 @@ webgl_backtrace = ["canvas_traits/webgl_backtrace"]
|
||||||
azure = {git = "https://github.com/servo/rust-azure"}
|
azure = {git = "https://github.com/servo/rust-azure"}
|
||||||
canvas_traits = {path = "../canvas_traits"}
|
canvas_traits = {path = "../canvas_traits"}
|
||||||
compositing = {path = "../compositing"}
|
compositing = {path = "../compositing"}
|
||||||
cssparser = "0.24"
|
cssparser = "0.25"
|
||||||
euclid = "0.19"
|
euclid = "0.19"
|
||||||
fnv = "1.0"
|
fnv = "1.0"
|
||||||
gleam = "0.6.4"
|
gleam = "0.6.4"
|
||||||
|
|
|
@ -13,7 +13,7 @@ path = "lib.rs"
|
||||||
webgl_backtrace = []
|
webgl_backtrace = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cssparser = "0.24.0"
|
cssparser = "0.25"
|
||||||
euclid = "0.19"
|
euclid = "0.19"
|
||||||
ipc-channel = "0.11"
|
ipc-channel = "0.11"
|
||||||
gleam = "0.6"
|
gleam = "0.6"
|
||||||
|
|
|
@ -26,7 +26,7 @@ servo = [
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
app_units = "0.7"
|
app_units = "0.7"
|
||||||
cssparser = "0.24.0"
|
cssparser = "0.25"
|
||||||
euclid = "0.19"
|
euclid = "0.19"
|
||||||
hashglobe = { path = "../hashglobe" }
|
hashglobe = { path = "../hashglobe" }
|
||||||
hyper = { version = "0.12", optional = true }
|
hyper = { version = "0.12", optional = true }
|
||||||
|
|
|
@ -39,7 +39,7 @@ canvas_traits = {path = "../canvas_traits"}
|
||||||
caseless = "0.2"
|
caseless = "0.2"
|
||||||
cookie = "0.11"
|
cookie = "0.11"
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
cssparser = "0.24"
|
cssparser = "0.25"
|
||||||
deny_public_fields = {path = "../deny_public_fields"}
|
deny_public_fields = {path = "../deny_public_fields"}
|
||||||
devtools_traits = {path = "../devtools_traits"}
|
devtools_traits = {path = "../devtools_traits"}
|
||||||
dom_struct = {path = "../dom_struct"}
|
dom_struct = {path = "../dom_struct"}
|
||||||
|
|
|
@ -13,7 +13,7 @@ path = "lib.rs"
|
||||||
app_units = "0.7"
|
app_units = "0.7"
|
||||||
atomic_refcell = "0.1"
|
atomic_refcell = "0.1"
|
||||||
canvas_traits = {path = "../canvas_traits"}
|
canvas_traits = {path = "../canvas_traits"}
|
||||||
cssparser = "0.24"
|
cssparser = "0.25"
|
||||||
euclid = "0.19"
|
euclid = "0.19"
|
||||||
gfx_traits = {path = "../gfx_traits"}
|
gfx_traits = {path = "../gfx_traits"}
|
||||||
html5ever = "0.22"
|
html5ever = "0.22"
|
||||||
|
|
|
@ -22,7 +22,7 @@ bench = []
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitflags = "1.0"
|
bitflags = "1.0"
|
||||||
matches = "0.1"
|
matches = "0.1"
|
||||||
cssparser = "0.24.0"
|
cssparser = "0.25"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
fxhash = "0.2"
|
fxhash = "0.2"
|
||||||
phf = "0.7.18"
|
phf = "0.7.18"
|
||||||
|
|
|
@ -36,6 +36,11 @@ pub trait PseudoElement: Sized + ToCss {
|
||||||
) -> bool {
|
) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether this pseudo-element is valid after a ::slotted(..) pseudo.
|
||||||
|
fn valid_after_slotted(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A trait that represents a pseudo-class.
|
/// A trait that represents a pseudo-class.
|
||||||
|
@ -69,6 +74,8 @@ pub enum SelectorParseErrorKind<'i> {
|
||||||
DanglingCombinator,
|
DanglingCombinator,
|
||||||
NonSimpleSelectorInNegation,
|
NonSimpleSelectorInNegation,
|
||||||
NonCompoundSelector,
|
NonCompoundSelector,
|
||||||
|
NonPseudoElementAfterSlotted,
|
||||||
|
InvalidPseudoElementAfterSlotted,
|
||||||
UnexpectedTokenInAttributeSelector(Token<'i>),
|
UnexpectedTokenInAttributeSelector(Token<'i>),
|
||||||
PseudoElementExpectedColon(Token<'i>),
|
PseudoElementExpectedColon(Token<'i>),
|
||||||
PseudoElementExpectedIdent(Token<'i>),
|
PseudoElementExpectedIdent(Token<'i>),
|
||||||
|
@ -1832,7 +1839,6 @@ where
|
||||||
input.skip_whitespace();
|
input.skip_whitespace();
|
||||||
|
|
||||||
let mut empty = true;
|
let mut empty = true;
|
||||||
let mut slot = false;
|
|
||||||
if !parse_type_selector(parser, input, builder)? {
|
if !parse_type_selector(parser, input, builder)? {
|
||||||
if let Some(url) = parser.default_namespace() {
|
if let Some(url) = parser.default_namespace() {
|
||||||
// If there was no explicit type selector, but there is a
|
// If there was no explicit type selector, but there is a
|
||||||
|
@ -1845,6 +1851,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut pseudo = false;
|
let mut pseudo = false;
|
||||||
|
let mut slot = false;
|
||||||
loop {
|
loop {
|
||||||
let parse_result =
|
let parse_result =
|
||||||
match parse_one_simple_selector(parser, input, /* inside_negation = */ false)? {
|
match parse_one_simple_selector(parser, input, /* inside_negation = */ false)? {
|
||||||
|
@ -1852,16 +1859,55 @@ where
|
||||||
Some(result) => result,
|
Some(result) => result,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
empty = false;
|
||||||
|
|
||||||
|
let slotted_selector;
|
||||||
|
let pseudo_element;
|
||||||
|
|
||||||
match parse_result {
|
match parse_result {
|
||||||
SimpleSelectorParseResult::SimpleSelector(s) => {
|
SimpleSelectorParseResult::SimpleSelector(s) => {
|
||||||
builder.push_simple_selector(s);
|
builder.push_simple_selector(s);
|
||||||
empty = false
|
continue;
|
||||||
},
|
},
|
||||||
SimpleSelectorParseResult::PseudoElement(p) => {
|
SimpleSelectorParseResult::PseudoElement(p) => {
|
||||||
// Try to parse state to its right. There are only 3 allowable
|
slotted_selector = None;
|
||||||
// state selectors that can go on pseudo-elements.
|
pseudo_element = Some(p);
|
||||||
let mut state_selectors = SmallVec::<[Component<Impl>; 3]>::new();
|
}
|
||||||
|
SimpleSelectorParseResult::SlottedPseudo(selector) => {
|
||||||
|
slotted_selector = Some(selector);
|
||||||
|
let maybe_pseudo = parse_one_simple_selector(
|
||||||
|
parser,
|
||||||
|
input,
|
||||||
|
/* inside_negation = */ false,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
pseudo_element = match maybe_pseudo {
|
||||||
|
None => None,
|
||||||
|
Some(SimpleSelectorParseResult::PseudoElement(pseudo)) => {
|
||||||
|
if !pseudo.valid_after_slotted() {
|
||||||
|
return Err(input.new_custom_error(
|
||||||
|
SelectorParseErrorKind::InvalidPseudoElementAfterSlotted
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Some(pseudo)
|
||||||
|
}
|
||||||
|
Some(SimpleSelectorParseResult::SimpleSelector(..)) |
|
||||||
|
Some(SimpleSelectorParseResult::SlottedPseudo(..)) => {
|
||||||
|
return Err(input.new_custom_error(
|
||||||
|
SelectorParseErrorKind::NonPseudoElementAfterSlotted
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_assert!(slotted_selector.is_some() || pseudo_element.is_some());
|
||||||
|
// Try to parse state to the right of the pseudo-element.
|
||||||
|
//
|
||||||
|
// There are only 3 allowable state selectors that can go on
|
||||||
|
// pseudo-elements as of right now.
|
||||||
|
let mut state_selectors = SmallVec::<[Component<Impl>; 3]>::new();
|
||||||
|
if let Some(ref p) = pseudo_element {
|
||||||
loop {
|
loop {
|
||||||
let location = input.current_source_location();
|
let location = input.current_source_location();
|
||||||
match input.next_including_whitespace() {
|
match input.next_including_whitespace() {
|
||||||
|
@ -1894,33 +1940,30 @@ where
|
||||||
}
|
}
|
||||||
state_selectors.push(Component::NonTSPseudoClass(pseudo_class));
|
state_selectors.push(Component::NonTSPseudoClass(pseudo_class));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(slotted) = slotted_selector {
|
||||||
|
slot = true;
|
||||||
|
if !builder.is_empty() {
|
||||||
|
builder.push_combinator(Combinator::SlotAssignment);
|
||||||
|
}
|
||||||
|
builder.push_simple_selector(Component::Slotted(slotted));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(p) = pseudo_element {
|
||||||
|
pseudo = true;
|
||||||
if !builder.is_empty() {
|
if !builder.is_empty() {
|
||||||
builder.push_combinator(Combinator::PseudoElement);
|
builder.push_combinator(Combinator::PseudoElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.push_simple_selector(Component::PseudoElement(p));
|
builder.push_simple_selector(Component::PseudoElement(p));
|
||||||
|
|
||||||
for state_selector in state_selectors.drain() {
|
for state_selector in state_selectors.drain() {
|
||||||
builder.push_simple_selector(state_selector);
|
builder.push_simple_selector(state_selector);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pseudo = true;
|
|
||||||
empty = false;
|
|
||||||
break;
|
break;
|
||||||
},
|
|
||||||
SimpleSelectorParseResult::SlottedPseudo(selector) => {
|
|
||||||
empty = false;
|
|
||||||
slot = true;
|
|
||||||
if !builder.is_empty() {
|
|
||||||
builder.push_combinator(Combinator::SlotAssignment);
|
|
||||||
}
|
|
||||||
builder.push_simple_selector(Component::Slotted(selector));
|
|
||||||
// FIXME(emilio): ::slotted() should support ::before and
|
|
||||||
// ::after after it, so we shouldn't break, but we shouldn't
|
|
||||||
// push more type selectors either.
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if empty {
|
if empty {
|
||||||
// An empty selector is invalid.
|
// An empty selector is invalid.
|
||||||
|
@ -2133,6 +2176,10 @@ pub mod tests {
|
||||||
PseudoClass::Active | PseudoClass::Lang(..) => false,
|
PseudoClass::Active | PseudoClass::Lang(..) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn valid_after_slotted(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl parser::NonTSPseudoClass for PseudoClass {
|
impl parser::NonTSPseudoClass for PseudoClass {
|
||||||
|
@ -2818,8 +2865,7 @@ pub mod tests {
|
||||||
assert!(parse("div + slot::slotted(div)").is_ok());
|
assert!(parse("div + slot::slotted(div)").is_ok());
|
||||||
assert!(parse("div + slot::slotted(div.foo)").is_ok());
|
assert!(parse("div + slot::slotted(div.foo)").is_ok());
|
||||||
assert!(parse("slot::slotted(div,foo)::first-line").is_err());
|
assert!(parse("slot::slotted(div,foo)::first-line").is_err());
|
||||||
// TODO
|
assert!(parse("::slotted(div)::before").is_ok());
|
||||||
assert!(parse("::slotted(div)::before").is_err());
|
|
||||||
assert!(parse("slot::slotted(div,foo)").is_err());
|
assert!(parse("slot::slotted(div,foo)").is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ atomic_refcell = "0.1"
|
||||||
bitflags = "1.0"
|
bitflags = "1.0"
|
||||||
byteorder = "1.0"
|
byteorder = "1.0"
|
||||||
cfg-if = "0.1.0"
|
cfg-if = "0.1.0"
|
||||||
cssparser = "0.24.0"
|
cssparser = "0.25"
|
||||||
new_debug_unreachable = "1.0"
|
new_debug_unreachable = "1.0"
|
||||||
encoding_rs = {version = "0.7", optional = true}
|
encoding_rs = {version = "0.7", optional = true}
|
||||||
euclid = "0.19"
|
euclid = "0.19"
|
||||||
|
|
|
@ -392,6 +392,12 @@ mod bindings {
|
||||||
.handle_str_items("whitelist-vars", |b, item| b.whitelist_var(item))
|
.handle_str_items("whitelist-vars", |b, item| b.whitelist_var(item))
|
||||||
.handle_str_items("whitelist-types", |b, item| b.whitelist_type(item))
|
.handle_str_items("whitelist-types", |b, item| b.whitelist_type(item))
|
||||||
.handle_str_items("opaque-types", |b, item| b.opaque_type(item))
|
.handle_str_items("opaque-types", |b, item| b.opaque_type(item))
|
||||||
|
.handle_table_items("cbindgen-types", |b, item| {
|
||||||
|
let gecko = item["gecko"].as_str().unwrap();
|
||||||
|
let servo = item["servo"].as_str().unwrap();
|
||||||
|
b.blacklist_type(format!("mozilla::{}", gecko))
|
||||||
|
.module_raw_line("root::mozilla", format!("pub use {} as {};", servo, gecko))
|
||||||
|
})
|
||||||
.handle_table_items("mapped-generic-types", |builder, item| {
|
.handle_table_items("mapped-generic-types", |builder, item| {
|
||||||
let generic = item["generic"].as_bool().unwrap();
|
let generic = item["generic"].as_bool().unwrap();
|
||||||
let gecko = item["gecko"].as_str().unwrap();
|
let gecko = item["gecko"].as_str().unwrap();
|
||||||
|
|
|
@ -38,19 +38,19 @@ derive_helper_methods = true
|
||||||
[export]
|
[export]
|
||||||
prefix = "Style"
|
prefix = "Style"
|
||||||
include = [
|
include = [
|
||||||
"StyleAppearance",
|
"Appearance",
|
||||||
"StyleComputedFontStretchRange",
|
"ComputedFontStretchRange",
|
||||||
"StyleComputedFontStyleDescriptor",
|
"ComputedFontStyleDescriptor",
|
||||||
"StyleComputedFontWeightRange",
|
"ComputedFontWeightRange",
|
||||||
"StyleComputedTimingFunction",
|
"ComputedTimingFunction",
|
||||||
"StyleDisplay",
|
"Display",
|
||||||
"StyleDisplayMode",
|
"DisplayMode",
|
||||||
"StyleFillRule",
|
"FillRule",
|
||||||
"StyleFontDisplay",
|
"FontDisplay",
|
||||||
"StyleFontFaceSourceListComponent",
|
"FontFaceSourceListComponent",
|
||||||
"StyleFontLanguageOverride",
|
"FontLanguageOverride",
|
||||||
"StyleTimingFunction",
|
"TimingFunction",
|
||||||
"StylePathCommand",
|
"PathCommand",
|
||||||
"StyleUnicodeRange",
|
"UnicodeRange",
|
||||||
]
|
]
|
||||||
item_types = ["enums", "structs", "typedefs"]
|
item_types = ["enums", "structs", "typedefs"]
|
||||||
|
|
|
@ -21,6 +21,60 @@ use std::fmt::{self, Write};
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
|
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
|
||||||
|
|
||||||
|
/// The environment from which to get `env` function values.
|
||||||
|
///
|
||||||
|
/// TODO(emilio): If this becomes a bit more complex we should probably move it
|
||||||
|
/// to the `media_queries` module, or something.
|
||||||
|
#[derive(Debug, MallocSizeOf)]
|
||||||
|
pub struct CssEnvironment;
|
||||||
|
|
||||||
|
struct EnvironmentVariable {
|
||||||
|
name: Atom,
|
||||||
|
value: VariableValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! make_variable {
|
||||||
|
($name:expr, $value:expr) => {{
|
||||||
|
EnvironmentVariable {
|
||||||
|
name: $name,
|
||||||
|
value: {
|
||||||
|
// TODO(emilio): We could make this be more efficient (though a
|
||||||
|
// bit less convenient).
|
||||||
|
let mut input = ParserInput::new($value);
|
||||||
|
let mut input = Parser::new(&mut input);
|
||||||
|
|
||||||
|
let (first_token_type, css, last_token_type) =
|
||||||
|
parse_self_contained_declaration_value(&mut input, None).unwrap();
|
||||||
|
|
||||||
|
VariableValue {
|
||||||
|
css: css.into_owned(),
|
||||||
|
first_token_type,
|
||||||
|
last_token_type,
|
||||||
|
references: Default::default(),
|
||||||
|
references_environment: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref ENVIRONMENT_VARIABLES: [EnvironmentVariable; 4] = [
|
||||||
|
make_variable!(atom!("safe-area-inset-top"), "0px"),
|
||||||
|
make_variable!(atom!("safe-area-inset-bottom"), "0px"),
|
||||||
|
make_variable!(atom!("safe-area-inset-left"), "0px"),
|
||||||
|
make_variable!(atom!("safe-area-inset-right"), "0px"),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CssEnvironment {
|
||||||
|
#[inline]
|
||||||
|
fn get(&self, name: &Atom) -> Option<&VariableValue> {
|
||||||
|
let var = ENVIRONMENT_VARIABLES.iter().find(|var| var.name == *name)?;
|
||||||
|
Some(&var.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A custom property name is just an `Atom`.
|
/// A custom property name is just an `Atom`.
|
||||||
///
|
///
|
||||||
/// Note that this does not include the `--` prefix
|
/// Note that this does not include the `--` prefix
|
||||||
|
@ -48,6 +102,12 @@ pub struct VariableValue {
|
||||||
first_token_type: TokenSerializationType,
|
first_token_type: TokenSerializationType,
|
||||||
last_token_type: TokenSerializationType,
|
last_token_type: TokenSerializationType,
|
||||||
|
|
||||||
|
/// Whether a variable value has a reference to an environment variable.
|
||||||
|
///
|
||||||
|
/// If this is the case, we need to perform variable substitution on the
|
||||||
|
/// value.
|
||||||
|
references_environment: bool,
|
||||||
|
|
||||||
/// Custom property names in var() functions.
|
/// Custom property names in var() functions.
|
||||||
references: PrecomputedHashSet<Name>,
|
references: PrecomputedHashSet<Name>,
|
||||||
}
|
}
|
||||||
|
@ -171,18 +231,6 @@ where
|
||||||
self.index.remove(index);
|
self.index.remove(index);
|
||||||
self.values.remove(key)
|
self.values.remove(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_set<S>(&mut self, set: &::hash::HashSet<K, S>)
|
|
||||||
where
|
|
||||||
S: ::std::hash::BuildHasher,
|
|
||||||
{
|
|
||||||
if set.is_empty() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.index.retain(|key| !set.contains(key));
|
|
||||||
self.values.retain(|key, _| !set.contains(key));
|
|
||||||
debug_assert_eq!(self.values.len(), self.index.len());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An iterator for OrderedMap.
|
/// An iterator for OrderedMap.
|
||||||
|
@ -216,6 +264,14 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A struct holding information about the external references to that a custom
|
||||||
|
/// property value may have.
|
||||||
|
#[derive(Default)]
|
||||||
|
struct VarOrEnvReferences {
|
||||||
|
custom_property_references: PrecomputedHashSet<Name>,
|
||||||
|
references_environment: bool,
|
||||||
|
}
|
||||||
|
|
||||||
impl VariableValue {
|
impl VariableValue {
|
||||||
fn empty() -> Self {
|
fn empty() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -223,6 +279,7 @@ impl VariableValue {
|
||||||
last_token_type: TokenSerializationType::nothing(),
|
last_token_type: TokenSerializationType::nothing(),
|
||||||
first_token_type: TokenSerializationType::nothing(),
|
first_token_type: TokenSerializationType::nothing(),
|
||||||
references: PrecomputedHashSet::default(),
|
references: PrecomputedHashSet::default(),
|
||||||
|
references_environment: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,7 +330,7 @@ impl VariableValue {
|
||||||
|
|
||||||
/// Parse a custom property value.
|
/// Parse a custom property value.
|
||||||
pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Arc<Self>, ParseError<'i>> {
|
pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Arc<Self>, ParseError<'i>> {
|
||||||
let mut references = PrecomputedHashSet::default();
|
let mut references = VarOrEnvReferences::default();
|
||||||
|
|
||||||
let (first_token_type, css, last_token_type) =
|
let (first_token_type, css, last_token_type) =
|
||||||
parse_self_contained_declaration_value(input, Some(&mut references))?;
|
parse_self_contained_declaration_value(input, Some(&mut references))?;
|
||||||
|
@ -282,7 +339,8 @@ impl VariableValue {
|
||||||
css: css.into_owned(),
|
css: css.into_owned(),
|
||||||
first_token_type,
|
first_token_type,
|
||||||
last_token_type,
|
last_token_type,
|
||||||
references,
|
references: references.custom_property_references,
|
||||||
|
references_environment: references.references_environment,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -297,7 +355,7 @@ pub fn parse_non_custom_with_var<'i, 't>(
|
||||||
|
|
||||||
fn parse_self_contained_declaration_value<'i, 't>(
|
fn parse_self_contained_declaration_value<'i, 't>(
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
references: Option<&mut PrecomputedHashSet<Name>>,
|
references: Option<&mut VarOrEnvReferences>,
|
||||||
) -> Result<(TokenSerializationType, Cow<'i, str>, TokenSerializationType), ParseError<'i>> {
|
) -> Result<(TokenSerializationType, Cow<'i, str>, TokenSerializationType), ParseError<'i>> {
|
||||||
let start_position = input.position();
|
let start_position = input.position();
|
||||||
let mut missing_closing_characters = String::new();
|
let mut missing_closing_characters = String::new();
|
||||||
|
@ -317,7 +375,7 @@ fn parse_self_contained_declaration_value<'i, 't>(
|
||||||
/// <https://drafts.csswg.org/css-syntax-3/#typedef-declaration-value>
|
/// <https://drafts.csswg.org/css-syntax-3/#typedef-declaration-value>
|
||||||
fn parse_declaration_value<'i, 't>(
|
fn parse_declaration_value<'i, 't>(
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
references: Option<&mut PrecomputedHashSet<Name>>,
|
references: Option<&mut VarOrEnvReferences>,
|
||||||
missing_closing_characters: &mut String,
|
missing_closing_characters: &mut String,
|
||||||
) -> Result<(TokenSerializationType, TokenSerializationType), ParseError<'i>> {
|
) -> Result<(TokenSerializationType, TokenSerializationType), ParseError<'i>> {
|
||||||
input.parse_until_before(Delimiter::Bang | Delimiter::Semicolon, |input| {
|
input.parse_until_before(Delimiter::Bang | Delimiter::Semicolon, |input| {
|
||||||
|
@ -334,7 +392,7 @@ fn parse_declaration_value<'i, 't>(
|
||||||
/// invalid at the top level
|
/// invalid at the top level
|
||||||
fn parse_declaration_value_block<'i, 't>(
|
fn parse_declaration_value_block<'i, 't>(
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
mut references: Option<&mut PrecomputedHashSet<Name>>,
|
mut references: Option<&mut VarOrEnvReferences>,
|
||||||
missing_closing_characters: &mut String,
|
missing_closing_characters: &mut String,
|
||||||
) -> Result<(TokenSerializationType, TokenSerializationType), ParseError<'i>> {
|
) -> Result<(TokenSerializationType, TokenSerializationType), ParseError<'i>> {
|
||||||
let mut token_start = input.position();
|
let mut token_start = input.position();
|
||||||
|
@ -407,6 +465,12 @@ fn parse_declaration_value_block<'i, 't>(
|
||||||
parse_var_function(input, references.as_mut().map(|r| &mut **r))
|
parse_var_function(input, references.as_mut().map(|r| &mut **r))
|
||||||
})?;
|
})?;
|
||||||
input.reset(&args_start);
|
input.reset(&args_start);
|
||||||
|
} else if name.eq_ignore_ascii_case("env") {
|
||||||
|
let args_start = input.state();
|
||||||
|
input.parse_nested_block(|input| {
|
||||||
|
parse_env_function(input, references.as_mut().map(|r| &mut **r))
|
||||||
|
})?;
|
||||||
|
input.reset(&args_start);
|
||||||
}
|
}
|
||||||
nested!();
|
nested!();
|
||||||
check_closed!(")");
|
check_closed!(")");
|
||||||
|
@ -468,17 +532,7 @@ fn parse_declaration_value_block<'i, 't>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the var function is valid, return Ok((custom_property_name, fallback))
|
fn parse_fallback<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(), ParseError<'i>> {
|
||||||
fn parse_var_function<'i, 't>(
|
|
||||||
input: &mut Parser<'i, 't>,
|
|
||||||
references: Option<&mut PrecomputedHashSet<Name>>,
|
|
||||||
) -> Result<(), ParseError<'i>> {
|
|
||||||
let name = input.expect_ident_cloned()?;
|
|
||||||
let name: Result<_, ParseError> = parse_name(&name).map_err(|()| {
|
|
||||||
input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))
|
|
||||||
});
|
|
||||||
let name = name?;
|
|
||||||
if input.try(|input| input.expect_comma()).is_ok() {
|
|
||||||
// Exclude `!` and `;` at the top level
|
// Exclude `!` and `;` at the top level
|
||||||
// https://drafts.csswg.org/css-syntax/#typedef-declaration-value
|
// https://drafts.csswg.org/css-syntax/#typedef-declaration-value
|
||||||
input.parse_until_before(Delimiter::Bang | Delimiter::Semicolon, |input| {
|
input.parse_until_before(Delimiter::Bang | Delimiter::Semicolon, |input| {
|
||||||
|
@ -487,10 +541,39 @@ fn parse_var_function<'i, 't>(
|
||||||
// Skip until the end.
|
// Skip until the end.
|
||||||
while let Ok(_) = input.next_including_whitespace_and_comments() {}
|
while let Ok(_) = input.next_including_whitespace_and_comments() {}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the var function is valid, return Ok((custom_property_name, fallback))
|
||||||
|
fn parse_var_function<'i, 't>(
|
||||||
|
input: &mut Parser<'i, 't>,
|
||||||
|
references: Option<&mut VarOrEnvReferences>,
|
||||||
|
) -> Result<(), ParseError<'i>> {
|
||||||
|
let name = input.expect_ident_cloned()?;
|
||||||
|
let name = parse_name(&name).map_err(|()| {
|
||||||
|
input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))
|
||||||
})?;
|
})?;
|
||||||
|
if input.try(|input| input.expect_comma()).is_ok() {
|
||||||
|
parse_fallback(input)?;
|
||||||
}
|
}
|
||||||
if let Some(refs) = references {
|
if let Some(refs) = references {
|
||||||
refs.insert(Atom::from(name));
|
refs.custom_property_references.insert(Atom::from(name));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_env_function<'i, 't>(
|
||||||
|
input: &mut Parser<'i, 't>,
|
||||||
|
references: Option<&mut VarOrEnvReferences>,
|
||||||
|
) -> Result<(), ParseError<'i>> {
|
||||||
|
// TODO(emilio): This should be <custom-ident> per spec, but no other
|
||||||
|
// browser does that, see https://github.com/w3c/csswg-drafts/issues/3262.
|
||||||
|
input.expect_ident()?;
|
||||||
|
if input.try(|input| input.expect_comma()).is_ok() {
|
||||||
|
parse_fallback(input)?;
|
||||||
|
}
|
||||||
|
if let Some(references) = references {
|
||||||
|
references.references_environment = true;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -502,25 +585,26 @@ pub struct CustomPropertiesBuilder<'a> {
|
||||||
may_have_cycles: bool,
|
may_have_cycles: bool,
|
||||||
custom_properties: Option<CustomPropertiesMap>,
|
custom_properties: Option<CustomPropertiesMap>,
|
||||||
inherited: Option<&'a Arc<CustomPropertiesMap>>,
|
inherited: Option<&'a Arc<CustomPropertiesMap>>,
|
||||||
|
environment: &'a CssEnvironment,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> CustomPropertiesBuilder<'a> {
|
impl<'a> CustomPropertiesBuilder<'a> {
|
||||||
/// Create a new builder, inheriting from a given custom properties map.
|
/// Create a new builder, inheriting from a given custom properties map.
|
||||||
pub fn new(inherited: Option<&'a Arc<CustomPropertiesMap>>) -> Self {
|
pub fn new(
|
||||||
|
inherited: Option<&'a Arc<CustomPropertiesMap>>,
|
||||||
|
environment: &'a CssEnvironment,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
seen: PrecomputedHashSet::default(),
|
seen: PrecomputedHashSet::default(),
|
||||||
may_have_cycles: false,
|
may_have_cycles: false,
|
||||||
custom_properties: None,
|
custom_properties: None,
|
||||||
inherited,
|
inherited,
|
||||||
|
environment,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cascade a given custom property declaration.
|
/// Cascade a given custom property declaration.
|
||||||
pub fn cascade(
|
pub fn cascade(&mut self, name: &'a Name, specified_value: &CustomDeclarationValue) {
|
||||||
&mut self,
|
|
||||||
name: &'a Name,
|
|
||||||
specified_value: &CustomDeclarationValue,
|
|
||||||
) {
|
|
||||||
let was_already_present = !self.seen.insert(name);
|
let was_already_present = !self.seen.insert(name);
|
||||||
if was_already_present {
|
if was_already_present {
|
||||||
return;
|
return;
|
||||||
|
@ -540,8 +624,29 @@ impl<'a> CustomPropertiesBuilder<'a> {
|
||||||
let map = self.custom_properties.as_mut().unwrap();
|
let map = self.custom_properties.as_mut().unwrap();
|
||||||
match *specified_value {
|
match *specified_value {
|
||||||
CustomDeclarationValue::Value(ref unparsed_value) => {
|
CustomDeclarationValue::Value(ref unparsed_value) => {
|
||||||
self.may_have_cycles |= !unparsed_value.references.is_empty();
|
let has_references = !unparsed_value.references.is_empty();
|
||||||
map.insert(name.clone(), (*unparsed_value).clone());
|
self.may_have_cycles |= has_references;
|
||||||
|
|
||||||
|
// If the variable value has no references and it has an
|
||||||
|
// environment variable here, perform substitution here instead
|
||||||
|
// of forcing a full traversal in `substitute_all` afterwards.
|
||||||
|
let value = if !has_references && unparsed_value.references_environment {
|
||||||
|
let result = substitute_references_in_value(
|
||||||
|
unparsed_value,
|
||||||
|
&map,
|
||||||
|
&self.environment,
|
||||||
|
);
|
||||||
|
match result {
|
||||||
|
Ok(new_value) => Arc::new(new_value),
|
||||||
|
Err(..) => {
|
||||||
|
map.remove(name);
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(*unparsed_value).clone()
|
||||||
|
};
|
||||||
|
map.insert(name.clone(), value);
|
||||||
},
|
},
|
||||||
CustomDeclarationValue::CSSWideKeyword(keyword) => match keyword {
|
CustomDeclarationValue::CSSWideKeyword(keyword) => match keyword {
|
||||||
CSSWideKeyword::Initial => {
|
CSSWideKeyword::Initial => {
|
||||||
|
@ -553,11 +658,7 @@ impl<'a> CustomPropertiesBuilder<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn value_may_affect_style(
|
fn value_may_affect_style(&self, name: &Name, value: &CustomDeclarationValue) -> bool {
|
||||||
&self,
|
|
||||||
name: &Name,
|
|
||||||
value: &CustomDeclarationValue,
|
|
||||||
) -> bool {
|
|
||||||
match *value {
|
match *value {
|
||||||
CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Unset) |
|
CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Unset) |
|
||||||
CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Inherit) => {
|
CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Inherit) => {
|
||||||
|
@ -596,8 +697,8 @@ impl<'a> CustomPropertiesBuilder<'a> {
|
||||||
|
|
||||||
/// Returns the final map of applicable custom properties.
|
/// Returns the final map of applicable custom properties.
|
||||||
///
|
///
|
||||||
/// If there was any specified property, we've created a new map and now we need
|
/// If there was any specified property, we've created a new map and now we
|
||||||
/// to remove any potential cycles, and wrap it in an arc.
|
/// need to remove any potential cycles, and wrap it in an arc.
|
||||||
///
|
///
|
||||||
/// Otherwise, just use the inherited custom properties map.
|
/// Otherwise, just use the inherited custom properties map.
|
||||||
pub fn build(mut self) -> Option<Arc<CustomPropertiesMap>> {
|
pub fn build(mut self) -> Option<Arc<CustomPropertiesMap>> {
|
||||||
|
@ -605,9 +706,8 @@ impl<'a> CustomPropertiesBuilder<'a> {
|
||||||
Some(m) => m,
|
Some(m) => m,
|
||||||
None => return self.inherited.cloned(),
|
None => return self.inherited.cloned(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.may_have_cycles {
|
if self.may_have_cycles {
|
||||||
substitute_all(&mut map);
|
substitute_all(&mut map, self.environment);
|
||||||
}
|
}
|
||||||
Some(Arc::new(map))
|
Some(Arc::new(map))
|
||||||
}
|
}
|
||||||
|
@ -616,24 +716,12 @@ impl<'a> CustomPropertiesBuilder<'a> {
|
||||||
/// Resolve all custom properties to either substituted or invalid.
|
/// Resolve all custom properties to either substituted or invalid.
|
||||||
///
|
///
|
||||||
/// It does cycle dependencies removal at the same time as substitution.
|
/// It does cycle dependencies removal at the same time as substitution.
|
||||||
fn substitute_all(custom_properties_map: &mut CustomPropertiesMap) {
|
fn substitute_all(custom_properties_map: &mut CustomPropertiesMap, environment: &CssEnvironment) {
|
||||||
// The cycle dependencies removal in this function is a variant
|
// The cycle dependencies removal in this function is a variant
|
||||||
// of Tarjan's algorithm. It is mostly based on the pseudo-code
|
// of Tarjan's algorithm. It is mostly based on the pseudo-code
|
||||||
// listed in
|
// listed in
|
||||||
// https://en.wikipedia.org/w/index.php?
|
// https://en.wikipedia.org/w/index.php?
|
||||||
// title=Tarjan%27s_strongly_connected_components_algorithm&oldid=801728495
|
// title=Tarjan%27s_strongly_connected_components_algorithm&oldid=801728495
|
||||||
//
|
|
||||||
// FIXME This function currently does at least one addref to names
|
|
||||||
// for each variable regardless whether it has reference. Each
|
|
||||||
// variable with any reference would have an additional addref.
|
|
||||||
// There is another addref for each reference.
|
|
||||||
// Strictly speaking, these addrefs are not necessary, because we
|
|
||||||
// don't add/remove entry from custom properties map, and thus keys
|
|
||||||
// should be alive in the whole process until we start removing
|
|
||||||
// invalids. However, there is no safe way for us to prove this to
|
|
||||||
// the compiler. We may be able to fix this issue at some point if
|
|
||||||
// the standard library can provide some kind of hashmap wrapper
|
|
||||||
// with frozen keys.
|
|
||||||
|
|
||||||
/// Struct recording necessary information for each variable.
|
/// Struct recording necessary information for each variable.
|
||||||
struct VarInfo {
|
struct VarInfo {
|
||||||
|
@ -662,8 +750,8 @@ fn substitute_all(custom_properties_map: &mut CustomPropertiesMap) {
|
||||||
/// all unfinished strong connected components.
|
/// all unfinished strong connected components.
|
||||||
stack: SmallVec<[usize; 5]>,
|
stack: SmallVec<[usize; 5]>,
|
||||||
map: &'a mut CustomPropertiesMap,
|
map: &'a mut CustomPropertiesMap,
|
||||||
/// The set of invalid custom properties.
|
/// The environment to substitute `env()` variables.
|
||||||
invalid: &'a mut PrecomputedHashSet<Name>,
|
environment: &'a CssEnvironment,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function combines the traversal for cycle removal and value
|
/// This function combines the traversal for cycle removal and value
|
||||||
|
@ -675,9 +763,9 @@ fn substitute_all(custom_properties_map: &mut CustomPropertiesMap) {
|
||||||
/// in one of the following states:
|
/// in one of the following states:
|
||||||
/// * It is still in context.stack, which means it is part of an
|
/// * It is still in context.stack, which means it is part of an
|
||||||
/// potentially incomplete dependency circle.
|
/// potentially incomplete dependency circle.
|
||||||
/// * It has been added into the invalid set. It can be either that
|
/// * It has been removed from the map. It can be either that the
|
||||||
/// the substitution failed, or it is inside a dependency circle.
|
/// substitution failed, or it is inside a dependency circle.
|
||||||
/// When this function put a variable into the invalid set because
|
/// When this function removes a variable from the map because
|
||||||
/// of dependency circle, it would put all variables in the same
|
/// of dependency circle, it would put all variables in the same
|
||||||
/// strong connected component to the set together.
|
/// strong connected component to the set together.
|
||||||
/// * It doesn't have any reference, because either this variable
|
/// * It doesn't have any reference, because either this variable
|
||||||
|
@ -686,11 +774,18 @@ fn substitute_all(custom_properties_map: &mut CustomPropertiesMap) {
|
||||||
/// * There is no such variable at all.
|
/// * There is no such variable at all.
|
||||||
fn traverse<'a>(name: Name, context: &mut Context<'a>) -> Option<usize> {
|
fn traverse<'a>(name: Name, context: &mut Context<'a>) -> Option<usize> {
|
||||||
// Some shortcut checks.
|
// Some shortcut checks.
|
||||||
let (name, value) = if let Some(value) = context.map.get(&name) {
|
let (name, value) = {
|
||||||
// This variable has been resolved. Return the signal value.
|
let value = context.map.get(&name)?;
|
||||||
if value.references.is_empty() || context.invalid.contains(&name) {
|
|
||||||
|
// Nothing to resolve.
|
||||||
|
if value.references.is_empty() {
|
||||||
|
debug_assert!(
|
||||||
|
!value.references_environment,
|
||||||
|
"Should've been handled earlier"
|
||||||
|
);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Whether this variable has been visited in this traversal.
|
// Whether this variable has been visited in this traversal.
|
||||||
let key;
|
let key;
|
||||||
match context.index_map.entry(name) {
|
match context.index_map.entry(name) {
|
||||||
|
@ -702,12 +797,10 @@ fn substitute_all(custom_properties_map: &mut CustomPropertiesMap) {
|
||||||
entry.insert(context.count);
|
entry.insert(context.count);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hold a strong reference to the value so that we don't
|
// Hold a strong reference to the value so that we don't
|
||||||
// need to keep reference to context.map.
|
// need to keep reference to context.map.
|
||||||
(key, value.clone())
|
(key, value.clone())
|
||||||
} else {
|
|
||||||
// The variable doesn't exist at all.
|
|
||||||
return None;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add new entry to the information table.
|
// Add new entry to the information table.
|
||||||
|
@ -750,8 +843,9 @@ fn substitute_all(custom_properties_map: &mut CustomPropertiesMap) {
|
||||||
if lowlink != index {
|
if lowlink != index {
|
||||||
// This variable is in a loop, but it is not the root of
|
// This variable is in a loop, but it is not the root of
|
||||||
// this strong connected component. We simply return for
|
// this strong connected component. We simply return for
|
||||||
// now, and the root would add it into the invalid set.
|
// now, and the root would remove it from the map.
|
||||||
// This cannot be added into the invalid set here, because
|
//
|
||||||
|
// This cannot be removed from the map here, because
|
||||||
// otherwise the shortcut check at the beginning of this
|
// otherwise the shortcut check at the beginning of this
|
||||||
// function would return the wrong value.
|
// function would return the wrong value.
|
||||||
return Some(index);
|
return Some(index);
|
||||||
|
@ -778,44 +872,33 @@ fn substitute_all(custom_properties_map: &mut CustomPropertiesMap) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Anything here is in a loop which can traverse to the
|
// Anything here is in a loop which can traverse to the
|
||||||
// variable we are handling, so we should add it into
|
// variable we are handling, so remove it from the map, it's invalid
|
||||||
// the invalid set. We should never visit the variable
|
// at computed-value time.
|
||||||
// again so it's safe to just take the name away.
|
context.map.remove(&var_name);
|
||||||
context.invalid.insert(var_name);
|
|
||||||
in_loop = true;
|
in_loop = true;
|
||||||
}
|
}
|
||||||
if in_loop {
|
if in_loop {
|
||||||
// This variable is in loop. Resolve to invalid.
|
// This variable is in loop. Resolve to invalid.
|
||||||
context.invalid.insert(name);
|
context.map.remove(&name);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now we have shown that this variable is not in a loop, and
|
// Now we have shown that this variable is not in a loop, and
|
||||||
// all of its dependencies should have been resolved. We can
|
// all of its dependencies should have been resolved. We can
|
||||||
// start substitution now.
|
// start substitution now.
|
||||||
let mut computed_value = ComputedValue::empty();
|
let result = substitute_references_in_value(
|
||||||
let mut input = ParserInput::new(&value.css);
|
&value,
|
||||||
let mut input = Parser::new(&mut input);
|
&context.map,
|
||||||
let mut position = (input.position(), value.first_token_type);
|
&context.environment,
|
||||||
let result = substitute_block(
|
|
||||||
&mut input,
|
|
||||||
&mut position,
|
|
||||||
&mut computed_value,
|
|
||||||
&mut |name, partial_computed_value| {
|
|
||||||
if let Some(value) = context.map.get(name) {
|
|
||||||
if !context.invalid.contains(name) {
|
|
||||||
partial_computed_value.push_variable(value);
|
|
||||||
return Ok(value.last_token_type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(())
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
if let Ok(last_token_type) = result {
|
|
||||||
computed_value.push_from(position, &input, last_token_type);
|
match result {
|
||||||
|
Ok(computed_value) => {
|
||||||
context.map.insert(name, Arc::new(computed_value));
|
context.map.insert(name, Arc::new(computed_value));
|
||||||
} else {
|
},
|
||||||
context.invalid.insert(name);
|
Err(..) => {
|
||||||
|
context.map.remove(&name);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// All resolved, so return the signal value.
|
// All resolved, so return the signal value.
|
||||||
|
@ -825,7 +908,6 @@ fn substitute_all(custom_properties_map: &mut CustomPropertiesMap) {
|
||||||
// We have to clone the names so that we can mutably borrow the map
|
// We have to clone the names so that we can mutably borrow the map
|
||||||
// in the context we create for traversal.
|
// in the context we create for traversal.
|
||||||
let names = custom_properties_map.index.clone();
|
let names = custom_properties_map.index.clone();
|
||||||
let mut invalid = PrecomputedHashSet::default();
|
|
||||||
for name in names.into_iter() {
|
for name in names.into_iter() {
|
||||||
let mut context = Context {
|
let mut context = Context {
|
||||||
count: 0,
|
count: 0,
|
||||||
|
@ -833,33 +915,54 @@ fn substitute_all(custom_properties_map: &mut CustomPropertiesMap) {
|
||||||
stack: SmallVec::new(),
|
stack: SmallVec::new(),
|
||||||
var_info: SmallVec::new(),
|
var_info: SmallVec::new(),
|
||||||
map: custom_properties_map,
|
map: custom_properties_map,
|
||||||
invalid: &mut invalid,
|
environment,
|
||||||
};
|
};
|
||||||
traverse(name, &mut context);
|
traverse(name, &mut context);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
custom_properties_map.remove_set(&invalid);
|
/// Replace `var()` and `env()` functions in a pre-existing variable value.
|
||||||
|
fn substitute_references_in_value<'i>(
|
||||||
|
value: &'i VariableValue,
|
||||||
|
custom_properties: &CustomPropertiesMap,
|
||||||
|
environment: &CssEnvironment,
|
||||||
|
) -> Result<ComputedValue, ParseError<'i>> {
|
||||||
|
debug_assert!(!value.references.is_empty() || value.references_environment);
|
||||||
|
|
||||||
|
let mut input = ParserInput::new(&value.css);
|
||||||
|
let mut input = Parser::new(&mut input);
|
||||||
|
let mut position = (input.position(), value.first_token_type);
|
||||||
|
let mut computed_value = ComputedValue::empty();
|
||||||
|
|
||||||
|
let last_token_type = substitute_block(
|
||||||
|
&mut input,
|
||||||
|
&mut position,
|
||||||
|
&mut computed_value,
|
||||||
|
custom_properties,
|
||||||
|
environment,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
computed_value.push_from(position, &input, last_token_type);
|
||||||
|
Ok(computed_value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Replace `var()` functions in an arbitrary bit of input.
|
/// Replace `var()` functions in an arbitrary bit of input.
|
||||||
///
|
///
|
||||||
/// The `substitute_one` callback is called for each `var()` function in `input`.
|
/// If the variable has its initial value, the callback should return `Err(())`
|
||||||
/// If the variable has its initial value,
|
/// and leave `partial_computed_value` unchanged.
|
||||||
/// the callback should return `Err(())` and leave `partial_computed_value` unchanged.
|
///
|
||||||
/// Otherwise, it should push the value of the variable (with its own `var()` functions replaced)
|
/// Otherwise, it should push the value of the variable (with its own `var()` functions replaced)
|
||||||
/// to `partial_computed_value` and return `Ok(last_token_type of what was pushed)`
|
/// to `partial_computed_value` and return `Ok(last_token_type of what was pushed)`
|
||||||
///
|
///
|
||||||
/// Return `Err(())` if `input` is invalid at computed-value time.
|
/// Return `Err(())` if `input` is invalid at computed-value time.
|
||||||
/// or `Ok(last_token_type that was pushed to partial_computed_value)` otherwise.
|
/// or `Ok(last_token_type that was pushed to partial_computed_value)` otherwise.
|
||||||
fn substitute_block<'i, 't, F>(
|
fn substitute_block<'i, 't>(
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
position: &mut (SourcePosition, TokenSerializationType),
|
position: &mut (SourcePosition, TokenSerializationType),
|
||||||
partial_computed_value: &mut ComputedValue,
|
partial_computed_value: &mut ComputedValue,
|
||||||
substitute_one: &mut F,
|
custom_properties: &CustomPropertiesMap,
|
||||||
) -> Result<TokenSerializationType, ParseError<'i>>
|
env: &CssEnvironment,
|
||||||
where
|
) -> Result<TokenSerializationType, ParseError<'i>> {
|
||||||
F: FnMut(&Name, &mut ComputedValue) -> Result<TokenSerializationType, ()>,
|
|
||||||
{
|
|
||||||
let mut last_token_type = TokenSerializationType::nothing();
|
let mut last_token_type = TokenSerializationType::nothing();
|
||||||
let mut set_position_at_next_iteration = false;
|
let mut set_position_at_next_iteration = false;
|
||||||
loop {
|
loop {
|
||||||
|
@ -883,27 +986,45 @@ where
|
||||||
Err(..) => break,
|
Err(..) => break,
|
||||||
};
|
};
|
||||||
match token {
|
match token {
|
||||||
Token::Function(ref name) if name.eq_ignore_ascii_case("var") => {
|
Token::Function(ref name)
|
||||||
|
if name.eq_ignore_ascii_case("var") || name.eq_ignore_ascii_case("env") =>
|
||||||
|
{
|
||||||
|
let is_env = name.eq_ignore_ascii_case("env");
|
||||||
|
|
||||||
partial_computed_value.push(
|
partial_computed_value.push(
|
||||||
input.slice(position.0..before_this_token),
|
input.slice(position.0..before_this_token),
|
||||||
position.1,
|
position.1,
|
||||||
last_token_type,
|
last_token_type,
|
||||||
);
|
);
|
||||||
input.parse_nested_block(|input| {
|
input.parse_nested_block(|input| {
|
||||||
// parse_var_function() ensures neither .unwrap() will fail.
|
// parse_var_function() / parse_env_function() ensure neither .unwrap() will fail.
|
||||||
let name = input.expect_ident_cloned().unwrap();
|
let name = {
|
||||||
let name = Atom::from(parse_name(&name).unwrap());
|
let name = input.expect_ident().unwrap();
|
||||||
|
if is_env {
|
||||||
|
Atom::from(&**name)
|
||||||
|
} else {
|
||||||
|
Atom::from(parse_name(&name).unwrap())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if let Ok(last) = substitute_one(&name, partial_computed_value) {
|
let value = if is_env {
|
||||||
last_token_type = last;
|
env.get(&name)
|
||||||
|
} else {
|
||||||
|
custom_properties.get(&name).map(|v| &**v)
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(v) = value {
|
||||||
|
last_token_type = v.last_token_type;
|
||||||
|
partial_computed_value.push_variable(v);
|
||||||
// Skip over the fallback, as `parse_nested_block` would return `Err`
|
// Skip over the fallback, as `parse_nested_block` would return `Err`
|
||||||
// if we don’t consume all of `input`.
|
// if we don't consume all of `input`.
|
||||||
// FIXME: Add a specialized method to cssparser to do this with less work.
|
// FIXME: Add a specialized method to cssparser to do this with less work.
|
||||||
while let Ok(_) = input.next() {}
|
while input.next().is_ok() {}
|
||||||
} else {
|
} else {
|
||||||
input.expect_comma()?;
|
input.expect_comma()?;
|
||||||
let after_comma = input.state();
|
let after_comma = input.state();
|
||||||
let first_token_type = input.next_including_whitespace_and_comments()
|
let first_token_type = input
|
||||||
|
.next_including_whitespace_and_comments()
|
||||||
// parse_var_function() ensures that .unwrap() will not fail.
|
// parse_var_function() ensures that .unwrap() will not fail.
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.serialization_type();
|
.serialization_type();
|
||||||
|
@ -913,23 +1034,29 @@ where
|
||||||
input,
|
input,
|
||||||
&mut position,
|
&mut position,
|
||||||
partial_computed_value,
|
partial_computed_value,
|
||||||
substitute_one,
|
custom_properties,
|
||||||
|
env,
|
||||||
)?;
|
)?;
|
||||||
partial_computed_value.push_from(position, input, last_token_type);
|
partial_computed_value.push_from(position, input, last_token_type);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
set_position_at_next_iteration = true
|
set_position_at_next_iteration = true
|
||||||
},
|
}
|
||||||
|
|
||||||
Token::Function(_) |
|
Token::Function(_) |
|
||||||
Token::ParenthesisBlock |
|
Token::ParenthesisBlock |
|
||||||
Token::CurlyBracketBlock |
|
Token::CurlyBracketBlock |
|
||||||
Token::SquareBracketBlock => {
|
Token::SquareBracketBlock => {
|
||||||
input.parse_nested_block(|input| {
|
input.parse_nested_block(|input| {
|
||||||
substitute_block(input, position, partial_computed_value, substitute_one)
|
substitute_block(
|
||||||
|
input,
|
||||||
|
position,
|
||||||
|
partial_computed_value,
|
||||||
|
custom_properties,
|
||||||
|
env,
|
||||||
|
)
|
||||||
})?;
|
})?;
|
||||||
// It’s the same type for CloseCurlyBracket and CloseSquareBracket.
|
// It's the same type for CloseCurlyBracket and CloseSquareBracket.
|
||||||
last_token_type = Token::CloseParenthesis.serialization_type();
|
last_token_type = Token::CloseParenthesis.serialization_type();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -945,29 +1072,30 @@ where
|
||||||
Ok(last_token_type)
|
Ok(last_token_type)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Replace `var()` functions for a non-custom property.
|
/// Replace `var()` and `env()` functions for a non-custom property.
|
||||||
|
///
|
||||||
/// Return `Err(())` for invalid at computed time.
|
/// Return `Err(())` for invalid at computed time.
|
||||||
pub fn substitute<'i>(
|
pub fn substitute<'i>(
|
||||||
input: &'i str,
|
input: &'i str,
|
||||||
first_token_type: TokenSerializationType,
|
first_token_type: TokenSerializationType,
|
||||||
computed_values_map: Option<&Arc<CustomPropertiesMap>>,
|
computed_values_map: Option<&Arc<CustomPropertiesMap>>,
|
||||||
|
env: &CssEnvironment,
|
||||||
) -> Result<String, ParseError<'i>> {
|
) -> Result<String, ParseError<'i>> {
|
||||||
let mut substituted = ComputedValue::empty();
|
let mut substituted = ComputedValue::empty();
|
||||||
let mut input = ParserInput::new(input);
|
let mut input = ParserInput::new(input);
|
||||||
let mut input = Parser::new(&mut input);
|
let mut input = Parser::new(&mut input);
|
||||||
let mut position = (input.position(), first_token_type);
|
let mut position = (input.position(), first_token_type);
|
||||||
|
let empty_map = CustomPropertiesMap::new();
|
||||||
|
let custom_properties = match computed_values_map {
|
||||||
|
Some(m) => &**m,
|
||||||
|
None => &empty_map,
|
||||||
|
};
|
||||||
let last_token_type = substitute_block(
|
let last_token_type = substitute_block(
|
||||||
&mut input,
|
&mut input,
|
||||||
&mut position,
|
&mut position,
|
||||||
&mut substituted,
|
&mut substituted,
|
||||||
&mut |name, substituted| {
|
&custom_properties,
|
||||||
if let Some(value) = computed_values_map.and_then(|map| map.get(name)) {
|
env,
|
||||||
substituted.push_variable(value);
|
|
||||||
Ok(value.last_token_type)
|
|
||||||
} else {
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)?;
|
)?;
|
||||||
substituted.push_from(position, &input, last_token_type);
|
substituted.push_from(position, &input, last_token_type);
|
||||||
Ok(substituted.css)
|
Ok(substituted.css)
|
||||||
|
|
|
@ -13,7 +13,7 @@ use invalidation::element::invalidator::{InvalidationProcessor, InvalidationVect
|
||||||
use selectors::{Element, NthIndexCache, SelectorList};
|
use selectors::{Element, NthIndexCache, SelectorList};
|
||||||
use selectors::attr::CaseSensitivity;
|
use selectors::attr::CaseSensitivity;
|
||||||
use selectors::matching::{self, MatchingContext, MatchingMode};
|
use selectors::matching::{self, MatchingContext, MatchingMode};
|
||||||
use selectors::parser::{Combinator, Component, LocalName};
|
use selectors::parser::{Combinator, Component, LocalName, SelectorImpl};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
|
|
||||||
|
@ -333,6 +333,19 @@ fn collect_elements_with_id<E, Q, F>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn local_name_matches<E>(element: E, local_name: &LocalName<E::Impl>) -> bool
|
||||||
|
where
|
||||||
|
E: TElement,
|
||||||
|
{
|
||||||
|
let LocalName { ref name, ref lower_name } = *local_name;
|
||||||
|
if element.is_html_element_in_html_document() {
|
||||||
|
element.local_name() == lower_name.borrow()
|
||||||
|
} else {
|
||||||
|
element.local_name() == name.borrow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Fast paths for querySelector with a single simple selector.
|
/// Fast paths for querySelector with a single simple selector.
|
||||||
fn query_selector_single_query<E, Q>(
|
fn query_selector_single_query<E, Q>(
|
||||||
root: E::ConcreteNode,
|
root: E::ConcreteNode,
|
||||||
|
@ -357,16 +370,11 @@ where
|
||||||
element.has_class(class, case_sensitivity)
|
element.has_class(class, case_sensitivity)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
Component::LocalName(LocalName {
|
Component::LocalName(ref local_name) => {
|
||||||
ref name,
|
collect_all_elements::<E, Q, _>(root, results, |element| {
|
||||||
ref lower_name,
|
local_name_matches(element, local_name)
|
||||||
}) => collect_all_elements::<E, Q, _>(root, results, |element| {
|
})
|
||||||
if element.is_html_element_in_html_document() {
|
},
|
||||||
element.local_name() == lower_name.borrow()
|
|
||||||
} else {
|
|
||||||
element.local_name() == name.borrow()
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
// TODO(emilio): More fast paths?
|
// TODO(emilio): More fast paths?
|
||||||
_ => return Err(()),
|
_ => return Err(()),
|
||||||
}
|
}
|
||||||
|
@ -374,8 +382,18 @@ where
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum SimpleFilter<'a, Impl: SelectorImpl> {
|
||||||
|
Class(&'a Atom),
|
||||||
|
LocalName(&'a LocalName<Impl>),
|
||||||
|
}
|
||||||
|
|
||||||
/// Fast paths for a given selector query.
|
/// Fast paths for a given selector query.
|
||||||
///
|
///
|
||||||
|
/// When there's only one component, we go directly to
|
||||||
|
/// `query_selector_single_query`, otherwise, we try to optimize by looking just
|
||||||
|
/// at the subtrees rooted at ids in the selector, and otherwise we try to look
|
||||||
|
/// up by class name or local name in the rightmost compound.
|
||||||
|
///
|
||||||
/// FIXME(emilio, nbp): This may very well be a good candidate for code to be
|
/// FIXME(emilio, nbp): This may very well be a good candidate for code to be
|
||||||
/// replaced by HolyJit :)
|
/// replaced by HolyJit :)
|
||||||
fn query_selector_fast<E, Q>(
|
fn query_selector_fast<E, Q>(
|
||||||
|
@ -410,7 +428,12 @@ where
|
||||||
let mut iter = selector.iter();
|
let mut iter = selector.iter();
|
||||||
let mut combinator: Option<Combinator> = None;
|
let mut combinator: Option<Combinator> = None;
|
||||||
|
|
||||||
loop {
|
// We want to optimize some cases where there's no id involved whatsoever,
|
||||||
|
// like `.foo .bar`, but we don't want to make `#foo .bar` slower because of
|
||||||
|
// that.
|
||||||
|
let mut simple_filter = None;
|
||||||
|
|
||||||
|
'selector_loop: loop {
|
||||||
debug_assert!(combinator.map_or(true, |c| !c.is_sibling()));
|
debug_assert!(combinator.map_or(true, |c| !c.is_sibling()));
|
||||||
|
|
||||||
'component_loop: for component in &mut iter {
|
'component_loop: for component in &mut iter {
|
||||||
|
@ -469,13 +492,28 @@ where
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
},
|
},
|
||||||
|
Component::Class(ref class) => {
|
||||||
|
if combinator.is_none() {
|
||||||
|
simple_filter = Some(SimpleFilter::Class(class));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Component::LocalName(ref local_name) => {
|
||||||
|
if combinator.is_none() {
|
||||||
|
// Prefer to look at class rather than local-name if
|
||||||
|
// both are present.
|
||||||
|
if let Some(SimpleFilter::Class(..)) = simple_filter {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
simple_filter = Some(SimpleFilter::LocalName(local_name));
|
||||||
|
}
|
||||||
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let next_combinator = match iter.next_sequence() {
|
let next_combinator = match iter.next_sequence() {
|
||||||
None => return Err(()),
|
None => break 'selector_loop,
|
||||||
Some(c) => c,
|
Some(c) => c,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -492,6 +530,39 @@ where
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We got here without finding any ID or such that we could handle. Try to
|
||||||
|
// use one of the simple filters.
|
||||||
|
let simple_filter = match simple_filter {
|
||||||
|
Some(f) => f,
|
||||||
|
None => return Err(()),
|
||||||
|
};
|
||||||
|
|
||||||
|
match simple_filter {
|
||||||
|
SimpleFilter::Class(ref class) => {
|
||||||
|
let case_sensitivity = quirks_mode.classes_and_ids_case_sensitivity();
|
||||||
|
collect_all_elements::<E, Q, _>(root, results, |element| {
|
||||||
|
element.has_class(class, case_sensitivity) &&
|
||||||
|
matching::matches_selector_list(
|
||||||
|
selector_list,
|
||||||
|
&element,
|
||||||
|
matching_context,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
SimpleFilter::LocalName(ref local_name) => {
|
||||||
|
collect_all_elements::<E, Q, _>(root, results, |element| {
|
||||||
|
local_name_matches(element, local_name) &&
|
||||||
|
matching::matches_selector_list(
|
||||||
|
selector_list,
|
||||||
|
&element,
|
||||||
|
matching_context,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Slow path for a given selector query.
|
// Slow path for a given selector query.
|
||||||
|
|
|
@ -151,10 +151,20 @@ impl_range!(FontWeightRange, AbsoluteFontWeight);
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub struct ComputedFontWeightRange(f32, f32);
|
pub struct ComputedFontWeightRange(f32, f32);
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn sort_range<T: PartialOrd>(a: T, b: T) -> (T, T) {
|
||||||
|
if a > b {
|
||||||
|
(b, a)
|
||||||
|
} else {
|
||||||
|
(a, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FontWeightRange {
|
impl FontWeightRange {
|
||||||
/// Returns a computed font-stretch range.
|
/// Returns a computed font-stretch range.
|
||||||
pub fn compute(&self) -> ComputedFontWeightRange {
|
pub fn compute(&self) -> ComputedFontWeightRange {
|
||||||
ComputedFontWeightRange(self.0.compute().0, self.1.compute().0)
|
let (min, max) = sort_range(self.0.compute().0, self.1.compute().0);
|
||||||
|
ComputedFontWeightRange(min, max)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,7 +192,11 @@ impl FontStretchRange {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ComputedFontStretchRange(compute_stretch(&self.0), compute_stretch(&self.1))
|
let (min, max) = sort_range(
|
||||||
|
compute_stretch(&self.0),
|
||||||
|
compute_stretch(&self.1),
|
||||||
|
);
|
||||||
|
ComputedFontStretchRange(min, max)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,10 +272,11 @@ impl FontStyle {
|
||||||
FontStyle::Normal => ComputedFontStyleDescriptor::Normal,
|
FontStyle::Normal => ComputedFontStyleDescriptor::Normal,
|
||||||
FontStyle::Italic => ComputedFontStyleDescriptor::Italic,
|
FontStyle::Italic => ComputedFontStyleDescriptor::Italic,
|
||||||
FontStyle::Oblique(ref first, ref second) => {
|
FontStyle::Oblique(ref first, ref second) => {
|
||||||
ComputedFontStyleDescriptor::Oblique(
|
let (min, max) = sort_range(
|
||||||
SpecifiedFontStyle::compute_angle_degrees(first),
|
SpecifiedFontStyle::compute_angle_degrees(first),
|
||||||
SpecifiedFontStyle::compute_angle_degrees(second),
|
SpecifiedFontStyle::compute_angle_degrees(second),
|
||||||
)
|
);
|
||||||
|
ComputedFontStyleDescriptor::Oblique(min, max)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -649,7 +649,7 @@ pub mod basic_shape {
|
||||||
|
|
||||||
use gecko::values::GeckoStyleCoordConvertible;
|
use gecko::values::GeckoStyleCoordConvertible;
|
||||||
use gecko_bindings::structs;
|
use gecko_bindings::structs;
|
||||||
use gecko_bindings::structs::{StyleBasicShape, StyleBasicShapeType, StyleFillRule};
|
use gecko_bindings::structs::{StyleBasicShape, StyleBasicShapeType};
|
||||||
use gecko_bindings::structs::{StyleGeometryBox, StyleShapeSource, StyleShapeSourceType};
|
use gecko_bindings::structs::{StyleGeometryBox, StyleShapeSource, StyleShapeSourceType};
|
||||||
use gecko_bindings::structs::{nsStyleCoord, nsStyleCorners};
|
use gecko_bindings::structs::{nsStyleCoord, nsStyleCorners};
|
||||||
use gecko_bindings::sugar::ns_style_coord::{CoordDataMut, CoordDataValue};
|
use gecko_bindings::sugar::ns_style_coord::{CoordDataMut, CoordDataValue};
|
||||||
|
@ -662,7 +662,7 @@ pub mod basic_shape {
|
||||||
use values::computed::position;
|
use values::computed::position;
|
||||||
use values::computed::url::ComputedUrl;
|
use values::computed::url::ComputedUrl;
|
||||||
use values::generics::basic_shape::{BasicShape as GenericBasicShape, InsetRect, Polygon};
|
use values::generics::basic_shape::{BasicShape as GenericBasicShape, InsetRect, Polygon};
|
||||||
use values::generics::basic_shape::{Circle, Ellipse, FillRule, Path, PolygonCoord};
|
use values::generics::basic_shape::{Circle, Ellipse, Path, PolygonCoord};
|
||||||
use values::generics::basic_shape::{GeometryBox, ShapeBox, ShapeSource};
|
use values::generics::basic_shape::{GeometryBox, ShapeBox, ShapeSource};
|
||||||
use values::generics::border::BorderRadius as GenericBorderRadius;
|
use values::generics::border::BorderRadius as GenericBorderRadius;
|
||||||
use values::generics::rect::Rect;
|
use values::generics::rect::Rect;
|
||||||
|
@ -693,12 +693,7 @@ pub mod basic_shape {
|
||||||
StyleShapeSourceType::URL | StyleShapeSourceType::Image => None,
|
StyleShapeSourceType::URL | StyleShapeSourceType::Image => None,
|
||||||
StyleShapeSourceType::Path => {
|
StyleShapeSourceType::Path => {
|
||||||
let path = self.to_svg_path().expect("expect an SVGPathData");
|
let path = self.to_svg_path().expect("expect an SVGPathData");
|
||||||
let gecko_path = unsafe { &*self.__bindgen_anon_1.mSVGPath.as_ref().mPtr };
|
let fill = unsafe { &*self.__bindgen_anon_1.mSVGPath.as_ref().mPtr }.mFillRule;
|
||||||
let fill = if gecko_path.mFillRule == StyleFillRule::Evenodd {
|
|
||||||
FillRule::Evenodd
|
|
||||||
} else {
|
|
||||||
FillRule::Nonzero
|
|
||||||
};
|
|
||||||
Some(ShapeSource::Path(Path { fill, path }))
|
Some(ShapeSource::Path(Path { fill, path }))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -706,18 +701,11 @@ pub mod basic_shape {
|
||||||
|
|
||||||
/// Generate a SVGPathData from StyleShapeSource if possible.
|
/// Generate a SVGPathData from StyleShapeSource if possible.
|
||||||
fn to_svg_path(&self) -> Option<SVGPathData> {
|
fn to_svg_path(&self) -> Option<SVGPathData> {
|
||||||
use gecko_bindings::structs::StylePathCommand;
|
|
||||||
use values::specified::svg_path::PathCommand;
|
use values::specified::svg_path::PathCommand;
|
||||||
match self.mType {
|
match self.mType {
|
||||||
StyleShapeSourceType::Path => {
|
StyleShapeSourceType::Path => {
|
||||||
let gecko_path = unsafe { &*self.__bindgen_anon_1.mSVGPath.as_ref().mPtr };
|
let gecko_path = unsafe { &*self.__bindgen_anon_1.mSVGPath.as_ref().mPtr };
|
||||||
let result: Vec<PathCommand> = gecko_path
|
let result: Vec<PathCommand> = gecko_path.mPath.iter().cloned().collect();
|
||||||
.mPath
|
|
||||||
.iter()
|
|
||||||
.map(|gecko: &StylePathCommand| {
|
|
||||||
// unsafe: cbindgen ensures the representation is the same.
|
|
||||||
unsafe { ::std::mem::transmute(*gecko) }
|
|
||||||
}).collect();
|
|
||||||
Some(SVGPathData::new(result.into_boxed_slice()))
|
Some(SVGPathData::new(result.into_boxed_slice()))
|
||||||
},
|
},
|
||||||
_ => None,
|
_ => None,
|
||||||
|
@ -730,7 +718,7 @@ pub mod basic_shape {
|
||||||
match other.mType {
|
match other.mType {
|
||||||
StyleShapeSourceType::URL => unsafe {
|
StyleShapeSourceType::URL => unsafe {
|
||||||
let shape_image = &*other.__bindgen_anon_1.mShapeImage.as_ref().mPtr;
|
let shape_image = &*other.__bindgen_anon_1.mShapeImage.as_ref().mPtr;
|
||||||
let other_url = RefPtr::new(*shape_image.__bindgen_anon_1.mURLValue.as_ref());
|
let other_url = RefPtr::new(*shape_image.__bindgen_anon_1.mURLValue.as_ref() as *mut _);
|
||||||
let url = ComputedUrl::from_url_value(other_url);
|
let url = ComputedUrl::from_url_value(other_url);
|
||||||
ShapeSource::ImageOrUrl(url)
|
ShapeSource::ImageOrUrl(url)
|
||||||
},
|
},
|
||||||
|
@ -805,11 +793,6 @@ pub mod basic_shape {
|
||||||
position: (&other.mPosition).into(),
|
position: (&other.mPosition).into(),
|
||||||
}),
|
}),
|
||||||
StyleBasicShapeType::Polygon => {
|
StyleBasicShapeType::Polygon => {
|
||||||
let fill_rule = if other.mFillRule == StyleFillRule::Evenodd {
|
|
||||||
FillRule::Evenodd
|
|
||||||
} else {
|
|
||||||
FillRule::Nonzero
|
|
||||||
};
|
|
||||||
let mut coords = Vec::with_capacity(other.mCoordinates.len() / 2);
|
let mut coords = Vec::with_capacity(other.mCoordinates.len() / 2);
|
||||||
for i in 0..(other.mCoordinates.len() / 2) {
|
for i in 0..(other.mCoordinates.len() / 2) {
|
||||||
let x = 2 * i;
|
let x = 2 * i;
|
||||||
|
@ -828,7 +811,7 @@ pub mod basic_shape {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
GenericBasicShape::Polygon(Polygon {
|
GenericBasicShape::Polygon(Polygon {
|
||||||
fill: fill_rule,
|
fill: other.mFillRule,
|
||||||
coordinates: coords,
|
coordinates: coords,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
|
@ -179,7 +179,7 @@ fn eval_device_orientation(device: &Device, value: Option<Orientation>) -> bool
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Values for the display-mode media feature.
|
/// Values for the display-mode media feature.
|
||||||
#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
|
#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub enum DisplayMode {
|
pub enum DisplayMode {
|
||||||
|
@ -191,16 +191,10 @@ pub enum DisplayMode {
|
||||||
|
|
||||||
/// https://w3c.github.io/manifest/#the-display-mode-media-feature
|
/// https://w3c.github.io/manifest/#the-display-mode-media-feature
|
||||||
fn eval_display_mode(device: &Device, query_value: Option<DisplayMode>) -> bool {
|
fn eval_display_mode(device: &Device, query_value: Option<DisplayMode>) -> bool {
|
||||||
let query_value = match query_value {
|
match query_value {
|
||||||
Some(v) => v,
|
Some(v) => v == unsafe { bindings::Gecko_MediaFeatures_GetDisplayMode(device.document()) },
|
||||||
None => return true,
|
None => true,
|
||||||
};
|
}
|
||||||
|
|
||||||
let gecko_display_mode =
|
|
||||||
unsafe { bindings::Gecko_MediaFeatures_GetDisplayMode(device.document()) };
|
|
||||||
|
|
||||||
// NOTE: cbindgen guarantees the same representation.
|
|
||||||
gecko_display_mode as u8 == query_value as u8
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// https://drafts.csswg.org/mediaqueries-4/#grid
|
/// https://drafts.csswg.org/mediaqueries-4/#grid
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
use app_units::AU_PER_PX;
|
use app_units::AU_PER_PX;
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use cssparser::RGBA;
|
use cssparser::RGBA;
|
||||||
|
use custom_properties::CssEnvironment;
|
||||||
use euclid::Size2D;
|
use euclid::Size2D;
|
||||||
use euclid::TypedScale;
|
use euclid::TypedScale;
|
||||||
use gecko::values::{convert_nscolor_to_rgba, convert_rgba_to_nscolor};
|
use gecko::values::{convert_nscolor_to_rgba, convert_rgba_to_nscolor};
|
||||||
|
@ -52,6 +53,9 @@ pub struct Device {
|
||||||
/// Whether any styles computed in the document relied on the viewport size
|
/// Whether any styles computed in the document relied on the viewport size
|
||||||
/// by using vw/vh/vmin/vmax units.
|
/// by using vw/vh/vmin/vmax units.
|
||||||
used_viewport_size: AtomicBool,
|
used_viewport_size: AtomicBool,
|
||||||
|
/// The CssEnvironment object responsible of getting CSS environment
|
||||||
|
/// variables.
|
||||||
|
environment: CssEnvironment,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Device {
|
impl fmt::Debug for Device {
|
||||||
|
@ -87,9 +91,16 @@ impl Device {
|
||||||
body_text_color: AtomicUsize::new(unsafe { &*pres_context }.mDefaultColor as usize),
|
body_text_color: AtomicUsize::new(unsafe { &*pres_context }.mDefaultColor as usize),
|
||||||
used_root_font_size: AtomicBool::new(false),
|
used_root_font_size: AtomicBool::new(false),
|
||||||
used_viewport_size: AtomicBool::new(false),
|
used_viewport_size: AtomicBool::new(false),
|
||||||
|
environment: CssEnvironment,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the relevant environment to resolve `env()` functions.
|
||||||
|
#[inline]
|
||||||
|
pub fn environment(&self) -> &CssEnvironment {
|
||||||
|
&self.environment
|
||||||
|
}
|
||||||
|
|
||||||
/// Tells the device that a new viewport rule has been found, and stores the
|
/// Tells the device that a new viewport rule has been found, and stores the
|
||||||
/// relevant viewport constraints.
|
/// relevant viewport constraints.
|
||||||
pub fn account_for_viewport_rule(&mut self, _constraints: &ViewportConstraints) {
|
pub fn account_for_viewport_rule(&mut self, _constraints: &ViewportConstraints) {
|
||||||
|
|
|
@ -27,6 +27,14 @@ include!(concat!(
|
||||||
impl ::selectors::parser::PseudoElement for PseudoElement {
|
impl ::selectors::parser::PseudoElement for PseudoElement {
|
||||||
type Impl = SelectorImpl;
|
type Impl = SelectorImpl;
|
||||||
|
|
||||||
|
fn valid_after_slotted(&self) -> bool {
|
||||||
|
// TODO(emilio): Remove this function or this comment after [1] is
|
||||||
|
// resolved.
|
||||||
|
//
|
||||||
|
// [1]: https://github.com/w3c/csswg-drafts/issues/3150
|
||||||
|
self.is_before_or_after()
|
||||||
|
}
|
||||||
|
|
||||||
fn supports_pseudo_class(&self, pseudo_class: &NonTSPseudoClass) -> bool {
|
fn supports_pseudo_class(&self, pseudo_class: &NonTSPseudoClass) -> bool {
|
||||||
if !self.supports_user_action_state() {
|
if !self.supports_user_action_state() {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -243,7 +243,10 @@ where
|
||||||
|
|
||||||
let mut declarations = SmallVec::<[(&_, CascadeLevel); 32]>::new();
|
let mut declarations = SmallVec::<[(&_, CascadeLevel); 32]>::new();
|
||||||
let custom_properties = {
|
let custom_properties = {
|
||||||
let mut builder = CustomPropertiesBuilder::new(inherited_style.custom_properties());
|
let mut builder = CustomPropertiesBuilder::new(
|
||||||
|
inherited_style.custom_properties(),
|
||||||
|
device.environment(),
|
||||||
|
);
|
||||||
|
|
||||||
for (declaration, cascade_level) in iter_declarations() {
|
for (declaration, cascade_level) in iter_declarations() {
|
||||||
declarations.push((declaration, cascade_level));
|
declarations.push((declaration, cascade_level));
|
||||||
|
@ -420,6 +423,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
||||||
declaration.id,
|
declaration.id,
|
||||||
self.context.builder.custom_properties.as_ref(),
|
self.context.builder.custom_properties.as_ref(),
|
||||||
self.context.quirks_mode,
|
self.context.quirks_mode,
|
||||||
|
self.context.device().environment(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -298,6 +298,7 @@ class Longhand(object):
|
||||||
"ColumnCount",
|
"ColumnCount",
|
||||||
"Contain",
|
"Contain",
|
||||||
"Display",
|
"Display",
|
||||||
|
"FillRule",
|
||||||
"Float",
|
"Float",
|
||||||
"FontSizeAdjust",
|
"FontSizeAdjust",
|
||||||
"FontStretch",
|
"FontStretch",
|
||||||
|
@ -431,6 +432,8 @@ class Alias(object):
|
||||||
self.original = original
|
self.original = original
|
||||||
self.enabled_in = original.enabled_in
|
self.enabled_in = original.enabled_in
|
||||||
self.servo_pref = original.servo_pref
|
self.servo_pref = original.servo_pref
|
||||||
|
self.animatable = original.animatable
|
||||||
|
self.transitionable = original.transitionable
|
||||||
self.gecko_pref = gecko_pref
|
self.gecko_pref = gecko_pref
|
||||||
self.allowed_in_page_rule = original.allowed_in_page_rule
|
self.allowed_in_page_rule = original.allowed_in_page_rule
|
||||||
self.allowed_in_keyframe_block = original.allowed_in_keyframe_block
|
self.allowed_in_keyframe_block = original.allowed_in_keyframe_block
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
use context::QuirksMode;
|
use context::QuirksMode;
|
||||||
use cssparser::{DeclarationListParser, parse_important, ParserInput, CowRcStr};
|
use cssparser::{DeclarationListParser, parse_important, ParserInput, CowRcStr};
|
||||||
use cssparser::{Parser, AtRuleParser, DeclarationParser, Delimiter, ParseErrorKind};
|
use cssparser::{Parser, AtRuleParser, DeclarationParser, Delimiter, ParseErrorKind};
|
||||||
use custom_properties::CustomPropertiesBuilder;
|
use custom_properties::{CustomPropertiesBuilder, CssEnvironment};
|
||||||
use error_reporting::{ParseErrorReporter, ContextualParseError};
|
use error_reporting::{ParseErrorReporter, ContextualParseError};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use parser::ParserContext;
|
use parser::ParserContext;
|
||||||
|
@ -760,13 +760,19 @@ impl PropertyDeclarationBlock {
|
||||||
None => return Err(fmt::Error),
|
None => return Err(fmt::Error),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO(emilio): When we implement any environment variable without
|
||||||
|
// hard-coding the values we're going to need to get something
|
||||||
|
// meaningful out of here... All this code path is so terribly hacky
|
||||||
|
// ;_;.
|
||||||
|
let env = CssEnvironment;
|
||||||
|
|
||||||
let custom_properties = if let Some(cv) = computed_values {
|
let custom_properties = if let Some(cv) = computed_values {
|
||||||
// If there are extra custom properties for this declaration block,
|
// If there are extra custom properties for this declaration block,
|
||||||
// factor them in too.
|
// factor them in too.
|
||||||
if let Some(block) = custom_properties_block {
|
if let Some(block) = custom_properties_block {
|
||||||
// FIXME(emilio): This is not super-efficient here, and all this
|
// FIXME(emilio): This is not super-efficient here, and all this
|
||||||
// feels like a hack anyway...
|
// feels like a hack anyway...
|
||||||
block.cascade_custom_properties(cv.custom_properties())
|
block.cascade_custom_properties(cv.custom_properties(), &env)
|
||||||
} else {
|
} else {
|
||||||
cv.custom_properties().cloned()
|
cv.custom_properties().cloned()
|
||||||
}
|
}
|
||||||
|
@ -790,6 +796,7 @@ impl PropertyDeclarationBlock {
|
||||||
declaration.id,
|
declaration.id,
|
||||||
custom_properties.as_ref(),
|
custom_properties.as_ref(),
|
||||||
QuirksMode::NoQuirks,
|
QuirksMode::NoQuirks,
|
||||||
|
&env,
|
||||||
).to_css(dest)
|
).to_css(dest)
|
||||||
},
|
},
|
||||||
(ref d, _) => d.to_css(dest),
|
(ref d, _) => d.to_css(dest),
|
||||||
|
@ -835,17 +842,24 @@ impl PropertyDeclarationBlock {
|
||||||
&self,
|
&self,
|
||||||
context: &Context,
|
context: &Context,
|
||||||
) -> Option<Arc<::custom_properties::CustomPropertiesMap>> {
|
) -> Option<Arc<::custom_properties::CustomPropertiesMap>> {
|
||||||
self.cascade_custom_properties(context.style().custom_properties())
|
self.cascade_custom_properties(
|
||||||
|
context.style().custom_properties(),
|
||||||
|
context.device().environment(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a custom properties map which is the result of cascading custom
|
/// Returns a custom properties map which is the result of cascading custom
|
||||||
/// properties in this declaration block along with the given custom
|
/// properties in this declaration block along with the given custom
|
||||||
/// properties.
|
/// properties.
|
||||||
pub fn cascade_custom_properties(
|
fn cascade_custom_properties(
|
||||||
&self,
|
&self,
|
||||||
inherited_custom_properties: Option<&Arc<::custom_properties::CustomPropertiesMap>>,
|
inherited_custom_properties: Option<&Arc<::custom_properties::CustomPropertiesMap>>,
|
||||||
|
environment: &CssEnvironment,
|
||||||
) -> Option<Arc<::custom_properties::CustomPropertiesMap>> {
|
) -> Option<Arc<::custom_properties::CustomPropertiesMap>> {
|
||||||
let mut builder = CustomPropertiesBuilder::new(inherited_custom_properties);
|
let mut builder = CustomPropertiesBuilder::new(
|
||||||
|
inherited_custom_properties,
|
||||||
|
environment,
|
||||||
|
);
|
||||||
|
|
||||||
for declaration in self.normal_declaration_iter() {
|
for declaration in self.normal_declaration_iter() {
|
||||||
if let PropertyDeclaration::Custom(ref declaration) = *declaration {
|
if let PropertyDeclaration::Custom(ref declaration) = *declaration {
|
||||||
|
|
|
@ -368,34 +368,6 @@ def set_gecko_property(ffi_name, expr):
|
||||||
return "self.gecko.%s = %s;" % (ffi_name, expr)
|
return "self.gecko.%s = %s;" % (ffi_name, expr)
|
||||||
%>
|
%>
|
||||||
|
|
||||||
<%def name="impl_cbindgen_keyword(ident, gecko_ffi_name)">
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
#[inline]
|
|
||||||
pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
|
|
||||||
// unsafe: cbindgen ensures the representations match.
|
|
||||||
${set_gecko_property(gecko_ffi_name, "unsafe { transmute(v) }")}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
#[inline]
|
|
||||||
pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
|
|
||||||
// unsafe: cbindgen ensures the representations match.
|
|
||||||
unsafe { transmute(${get_gecko_property(gecko_ffi_name)}) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
#[inline]
|
|
||||||
pub fn copy_${ident}_from(&mut self, other: &Self) {
|
|
||||||
self.gecko.${gecko_ffi_name} = other.gecko.${gecko_ffi_name};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
#[inline]
|
|
||||||
pub fn reset_${ident}(&mut self, other: &Self) {
|
|
||||||
self.copy_${ident}_from(other)
|
|
||||||
}
|
|
||||||
</%def>
|
|
||||||
|
|
||||||
<%def name="impl_keyword_setter(ident, gecko_ffi_name, keyword, cast_type='u8', on_set=None)">
|
<%def name="impl_keyword_setter(ident, gecko_ffi_name, keyword, cast_type='u8', on_set=None)">
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
|
pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
|
||||||
|
@ -589,17 +561,24 @@ def set_gecko_property(ffi_name, expr):
|
||||||
return SVGLength::ContextValue;
|
return SVGLength::ContextValue;
|
||||||
}
|
}
|
||||||
let length = match self.gecko.${gecko_ffi_name}.as_value() {
|
let length = match self.gecko.${gecko_ffi_name}.as_value() {
|
||||||
CoordDataValue::Factor(number) =>
|
CoordDataValue::Factor(number) => {
|
||||||
SvgLengthOrPercentageOrNumber::Number(number),
|
SvgLengthOrPercentageOrNumber::Number(number)
|
||||||
CoordDataValue::Coord(coord) =>
|
},
|
||||||
|
CoordDataValue::Coord(coord) => {
|
||||||
SvgLengthOrPercentageOrNumber::LengthOrPercentage(
|
SvgLengthOrPercentageOrNumber::LengthOrPercentage(
|
||||||
LengthOrPercentage::Length(Au(coord).into())),
|
LengthOrPercentage::Length(Au(coord).into())
|
||||||
CoordDataValue::Percent(p) =>
|
)
|
||||||
|
},
|
||||||
|
CoordDataValue::Percent(p) => {
|
||||||
SvgLengthOrPercentageOrNumber::LengthOrPercentage(
|
SvgLengthOrPercentageOrNumber::LengthOrPercentage(
|
||||||
LengthOrPercentage::Percentage(Percentage(p))),
|
LengthOrPercentage::Percentage(Percentage(p))
|
||||||
CoordDataValue::Calc(calc) =>
|
)
|
||||||
|
},
|
||||||
|
CoordDataValue::Calc(calc) => {
|
||||||
SvgLengthOrPercentageOrNumber::LengthOrPercentage(
|
SvgLengthOrPercentageOrNumber::LengthOrPercentage(
|
||||||
LengthOrPercentage::Calc(calc.into())),
|
LengthOrPercentage::Calc(calc.into())
|
||||||
|
)
|
||||||
|
},
|
||||||
_ => unreachable!("Unexpected coordinate in ${ident}"),
|
_ => unreachable!("Unexpected coordinate in ${ident}"),
|
||||||
};
|
};
|
||||||
SVGLength::Length(length.into())
|
SVGLength::Length(length.into())
|
||||||
|
@ -1425,6 +1404,7 @@ impl Clone for ${style_struct.gecko_struct_name} {
|
||||||
"length::LengthOrNormal": impl_style_coord,
|
"length::LengthOrNormal": impl_style_coord,
|
||||||
"length::NonNegativeLengthOrAuto": impl_style_coord,
|
"length::NonNegativeLengthOrAuto": impl_style_coord,
|
||||||
"length::NonNegativeLengthOrPercentageOrNormal": impl_style_coord,
|
"length::NonNegativeLengthOrPercentageOrNormal": impl_style_coord,
|
||||||
|
"FillRule": impl_simple,
|
||||||
"FlexBasis": impl_style_coord,
|
"FlexBasis": impl_style_coord,
|
||||||
"Length": impl_absolute_length,
|
"Length": impl_absolute_length,
|
||||||
"LengthOrNormal": impl_style_coord,
|
"LengthOrNormal": impl_style_coord,
|
||||||
|
@ -3059,9 +3039,8 @@ fn static_assert() {
|
||||||
<%self:impl_trait style_struct_name="Box" skip_longhands="${skip_box_longhands}">
|
<%self:impl_trait style_struct_name="Box" skip_longhands="${skip_box_longhands}">
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_display(&mut self, v: longhands::display::computed_value::T) {
|
pub fn set_display(&mut self, v: longhands::display::computed_value::T) {
|
||||||
// unsafe: cbindgen ensures the representation is the same.
|
self.gecko.mDisplay = v;
|
||||||
self.gecko.mDisplay = unsafe { transmute(v) };
|
self.gecko.mOriginalDisplay = v;
|
||||||
self.gecko.mOriginalDisplay = unsafe { transmute(v) };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -3081,17 +3060,15 @@ fn static_assert() {
|
||||||
v: longhands::display::computed_value::T,
|
v: longhands::display::computed_value::T,
|
||||||
_is_item_or_root: bool
|
_is_item_or_root: bool
|
||||||
) {
|
) {
|
||||||
// unsafe: cbindgen ensures the representation is the same.
|
self.gecko.mDisplay = v;
|
||||||
self.gecko.mDisplay = unsafe { transmute(v) };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn clone_display(&self) -> longhands::display::computed_value::T {
|
pub fn clone_display(&self) -> longhands::display::computed_value::T {
|
||||||
// unsafe: cbindgen ensures the representation is the same.
|
self.gecko.mDisplay
|
||||||
unsafe { transmute(self.gecko.mDisplay) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
${impl_cbindgen_keyword('_moz_appearance', 'mAppearance')}
|
${impl_simple('_moz_appearance', 'mAppearance')}
|
||||||
|
|
||||||
<% float_keyword = Keyword("float", "Left Right None", gecko_enum_prefix="StyleFloat") %>
|
<% float_keyword = Keyword("float", "Left Right None", gecko_enum_prefix="StyleFloat") %>
|
||||||
${impl_keyword('float', 'mFloat', float_keyword)}
|
${impl_keyword('float', 'mFloat', float_keyword)}
|
||||||
|
@ -4977,15 +4954,10 @@ fn set_style_svg_path(
|
||||||
&mut shape_source.__bindgen_anon_1.mSVGPath.as_mut().mPtr.as_mut().unwrap()
|
&mut shape_source.__bindgen_anon_1.mSVGPath.as_mut().mPtr.as_mut().unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
let iter = servo_path.commands().iter().map(|command| {
|
gecko_path.mPath.assign_from_iter_pod(servo_path.commands().iter().cloned());
|
||||||
// unsafe: cbindgen ensures the representation is the same.
|
|
||||||
unsafe { transmute(*command) }
|
|
||||||
});
|
|
||||||
gecko_path.mPath.assign_from_iter_pod(iter);
|
|
||||||
|
|
||||||
// Setup fill-rule.
|
// Setup fill-rule.
|
||||||
// unsafe: cbindgen ensures the representation is the same.
|
gecko_path.mFillRule = fill;
|
||||||
gecko_path.mFillRule = unsafe { transmute(fill) };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
<%def name="impl_shape_source(ident, gecko_ffi_name)">
|
<%def name="impl_shape_source(ident, gecko_ffi_name)">
|
||||||
|
@ -5090,8 +5062,7 @@ fn set_style_svg_path(
|
||||||
coord.0.to_gecko_style_coord(&mut shape.mCoordinates[2 * i]);
|
coord.0.to_gecko_style_coord(&mut shape.mCoordinates[2 * i]);
|
||||||
coord.1.to_gecko_style_coord(&mut shape.mCoordinates[2 * i + 1]);
|
coord.1.to_gecko_style_coord(&mut shape.mCoordinates[2 * i + 1]);
|
||||||
}
|
}
|
||||||
// unsafe: cbindgen ensures the representation is the same.
|
shape.mFillRule = poly.fill;
|
||||||
shape.mFillRule = unsafe { transmute(poly.fill) };
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,10 +17,8 @@ use itertools::{EitherOrBoth, Itertools};
|
||||||
use num_traits::Zero;
|
use num_traits::Zero;
|
||||||
use properties::{CSSWideKeyword, PropertyDeclaration};
|
use properties::{CSSWideKeyword, PropertyDeclaration};
|
||||||
use properties::longhands;
|
use properties::longhands;
|
||||||
use properties::longhands::font_weight::computed_value::T as FontWeight;
|
|
||||||
use properties::longhands::visibility::computed_value::T as Visibility;
|
use properties::longhands::visibility::computed_value::T as Visibility;
|
||||||
use properties::PropertyId;
|
use properties::LonghandId;
|
||||||
use properties::{LonghandId, ShorthandId};
|
|
||||||
use servo_arc::Arc;
|
use servo_arc::Arc;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::{cmp, ptr};
|
use std::{cmp, ptr};
|
||||||
|
@ -29,15 +27,12 @@ use hash::FxHashMap;
|
||||||
use super::ComputedValues;
|
use super::ComputedValues;
|
||||||
use values::CSSFloat;
|
use values::CSSFloat;
|
||||||
use values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero};
|
use values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero};
|
||||||
use values::animated::color::Color as AnimatedColor;
|
|
||||||
use values::animated::effects::Filter as AnimatedFilter;
|
use values::animated::effects::Filter as AnimatedFilter;
|
||||||
#[cfg(feature = "gecko")] use values::computed::TransitionProperty;
|
#[cfg(feature = "gecko")] use values::computed::TransitionProperty;
|
||||||
use values::computed::{Angle, CalcLengthOrPercentage};
|
use values::computed::Angle;
|
||||||
use values::computed::{ClipRect, Context};
|
use values::computed::{ClipRect, Context};
|
||||||
use values::computed::{Length, LengthOrPercentage, LengthOrPercentageOrAuto};
|
use values::computed::{Length, LengthOrPercentage};
|
||||||
use values::computed::LengthOrPercentageOrNone;
|
use values::computed::{Number, Percentage};
|
||||||
use values::computed::{NonNegativeNumber, Number, NumberOrPercentage, Percentage};
|
|
||||||
use values::computed::length::NonNegativeLengthOrPercentage;
|
|
||||||
use values::computed::ToComputedValue;
|
use values::computed::ToComputedValue;
|
||||||
use values::computed::transform::{DirectionVector, Matrix, Matrix3D};
|
use values::computed::transform::{DirectionVector, Matrix, Matrix3D};
|
||||||
use values::computed::transform::TransformOperation as ComputedTransformOperation;
|
use values::computed::transform::TransformOperation as ComputedTransformOperation;
|
||||||
|
@ -45,35 +40,17 @@ use values::computed::transform::Transform as ComputedTransform;
|
||||||
use values::computed::transform::Rotate as ComputedRotate;
|
use values::computed::transform::Rotate as ComputedRotate;
|
||||||
use values::computed::transform::Translate as ComputedTranslate;
|
use values::computed::transform::Translate as ComputedTranslate;
|
||||||
use values::computed::transform::Scale as ComputedScale;
|
use values::computed::transform::Scale as ComputedScale;
|
||||||
use values::computed::url::ComputedUrl;
|
|
||||||
use values::generics::transform::{self, Rotate, Translate, Scale, Transform, TransformOperation};
|
use values::generics::transform::{self, Rotate, Translate, Scale, Transform, TransformOperation};
|
||||||
use values::distance::{ComputeSquaredDistance, SquaredDistance};
|
use values::distance::{ComputeSquaredDistance, SquaredDistance};
|
||||||
use values::generics::font::{FontSettings as GenericFontSettings, FontTag, VariationValue};
|
|
||||||
use values::computed::font::FontVariationSettings;
|
|
||||||
use values::generics::effects::Filter;
|
use values::generics::effects::Filter;
|
||||||
use values::generics::svg::{SVGLength, SvgLengthOrPercentageOrNumber, SVGPaint};
|
|
||||||
use values::generics::svg::{SVGPaintKind, SVGStrokeDashArray, SVGOpacity};
|
|
||||||
use void::{self, Void};
|
use void::{self, Void};
|
||||||
|
|
||||||
|
|
||||||
/// Returns true if this nsCSSPropertyID is one of the animatable properties.
|
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
pub fn nscsspropertyid_is_animatable(property: nsCSSPropertyID) -> bool {
|
|
||||||
match property {
|
|
||||||
% for prop in data.longhands + data.shorthands_except_all():
|
|
||||||
% if prop.animatable:
|
|
||||||
${prop.nscsspropertyid()} => true,
|
|
||||||
% endif
|
|
||||||
% endfor
|
|
||||||
_ => false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert nsCSSPropertyID to TransitionProperty
|
/// Convert nsCSSPropertyID to TransitionProperty
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
impl From<nsCSSPropertyID> for TransitionProperty {
|
impl From<nsCSSPropertyID> for TransitionProperty {
|
||||||
fn from(property: nsCSSPropertyID) -> TransitionProperty {
|
fn from(property: nsCSSPropertyID) -> TransitionProperty {
|
||||||
|
use properties::ShorthandId;
|
||||||
match property {
|
match property {
|
||||||
% for prop in data.longhands:
|
% for prop in data.longhands:
|
||||||
${prop.nscsspropertyid()} => {
|
${prop.nscsspropertyid()} => {
|
||||||
|
@ -95,19 +72,6 @@ impl From<nsCSSPropertyID> for TransitionProperty {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if this nsCSSPropertyID is one of the transitionable properties.
|
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
pub fn nscsspropertyid_is_transitionable(property: nsCSSPropertyID) -> bool {
|
|
||||||
match property {
|
|
||||||
% for prop in data.longhands + data.shorthands_except_all():
|
|
||||||
% if prop.transitionable:
|
|
||||||
${prop.nscsspropertyid()} => true,
|
|
||||||
% endif
|
|
||||||
% endfor
|
|
||||||
_ => false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An animated property interpolation between two computed values for that
|
/// An animated property interpolation between two computed values for that
|
||||||
/// property.
|
/// property.
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
@ -556,6 +520,7 @@ impl AnimationValue {
|
||||||
declaration.id,
|
declaration.id,
|
||||||
custom_properties,
|
custom_properties,
|
||||||
context.quirks_mode,
|
context.quirks_mode,
|
||||||
|
context.device().environment(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
return AnimationValue::from_declaration(
|
return AnimationValue::from_declaration(
|
||||||
|
@ -846,197 +811,6 @@ impl ToAnimatedZero for Visibility {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <https://drafts.csswg.org/css-transitions/#animtype-lpcalc>
|
|
||||||
impl Animate for CalcLengthOrPercentage {
|
|
||||||
#[inline]
|
|
||||||
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
||||||
let animate_percentage_half = |this: Option<Percentage>, other: Option<Percentage>| {
|
|
||||||
if this.is_none() && other.is_none() {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
let this = this.unwrap_or_default();
|
|
||||||
let other = other.unwrap_or_default();
|
|
||||||
Ok(Some(this.animate(&other, procedure)?))
|
|
||||||
};
|
|
||||||
|
|
||||||
let length = self.unclamped_length().animate(&other.unclamped_length(), procedure)?;
|
|
||||||
let percentage = animate_percentage_half(self.percentage, other.percentage)?;
|
|
||||||
Ok(CalcLengthOrPercentage::with_clamping_mode(length, percentage, self.clamping_mode))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToAnimatedZero for LengthOrPercentageOrAuto {
|
|
||||||
#[inline]
|
|
||||||
fn to_animated_zero(&self) -> Result<Self, ()> {
|
|
||||||
match *self {
|
|
||||||
LengthOrPercentageOrAuto::Length(_) |
|
|
||||||
LengthOrPercentageOrAuto::Percentage(_) |
|
|
||||||
LengthOrPercentageOrAuto::Calc(_) => {
|
|
||||||
Ok(LengthOrPercentageOrAuto::Length(Length::new(0.)))
|
|
||||||
},
|
|
||||||
LengthOrPercentageOrAuto::Auto => Err(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToAnimatedZero for LengthOrPercentageOrNone {
|
|
||||||
#[inline]
|
|
||||||
fn to_animated_zero(&self) -> Result<Self, ()> {
|
|
||||||
match *self {
|
|
||||||
LengthOrPercentageOrNone::Length(_) |
|
|
||||||
LengthOrPercentageOrNone::Percentage(_) |
|
|
||||||
LengthOrPercentageOrNone::Calc(_) => {
|
|
||||||
Ok(LengthOrPercentageOrNone::Length(Length::new(0.)))
|
|
||||||
},
|
|
||||||
LengthOrPercentageOrNone::None => Err(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToAnimatedZero for FontWeight {
|
|
||||||
#[inline]
|
|
||||||
fn to_animated_zero(&self) -> Result<Self, ()> {
|
|
||||||
Ok(FontWeight::normal())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def>
|
|
||||||
impl Animate for FontVariationSettings {
|
|
||||||
#[inline]
|
|
||||||
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
||||||
FontSettingTagIter::new(self, other)?
|
|
||||||
.map(|r| r.and_then(|(st, ot)| st.animate(&ot, procedure)))
|
|
||||||
.collect::<Result<Vec<ComputedVariationValue>, ()>>()
|
|
||||||
.map(|v| GenericFontSettings(v.into_boxed_slice()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ComputeSquaredDistance for FontVariationSettings {
|
|
||||||
#[inline]
|
|
||||||
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
|
||||||
FontSettingTagIter::new(self, other)?
|
|
||||||
.map(|r| r.and_then(|(st, ot)| st.compute_squared_distance(&ot)))
|
|
||||||
.sum()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToAnimatedZero for FontVariationSettings {
|
|
||||||
#[inline]
|
|
||||||
fn to_animated_zero(&self) -> Result<Self, ()> {
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type ComputedVariationValue = VariationValue<Number>;
|
|
||||||
|
|
||||||
// FIXME: Could do a rename, this is only used for font variations.
|
|
||||||
struct FontSettingTagIterState<'a> {
|
|
||||||
tags: Vec<(&'a ComputedVariationValue)>,
|
|
||||||
index: usize,
|
|
||||||
prev_tag: FontTag,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> FontSettingTagIterState<'a> {
|
|
||||||
fn new(tags: Vec<<&'a ComputedVariationValue>) -> FontSettingTagIterState<'a> {
|
|
||||||
FontSettingTagIterState {
|
|
||||||
index: tags.len(),
|
|
||||||
tags,
|
|
||||||
prev_tag: FontTag(0),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Iterator for font-variation-settings tag lists
|
|
||||||
///
|
|
||||||
/// [CSS fonts level 4](https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-variation-settings)
|
|
||||||
/// defines the animation of font-variation-settings as follows:
|
|
||||||
///
|
|
||||||
/// Two declarations of font-feature-settings[sic] can be animated between if
|
|
||||||
/// they are "like". "Like" declarations are ones where the same set of
|
|
||||||
/// properties appear (in any order). Because succesive[sic] duplicate
|
|
||||||
/// properties are applied instead of prior duplicate properties, two
|
|
||||||
/// declarations can be "like" even if they have differing number of
|
|
||||||
/// properties. If two declarations are "like" then animation occurs pairwise
|
|
||||||
/// between corresponding values in the declarations.
|
|
||||||
///
|
|
||||||
/// In other words if we have the following lists:
|
|
||||||
///
|
|
||||||
/// "wght" 1.4, "wdth" 5, "wght" 2
|
|
||||||
/// "wdth" 8, "wght" 4, "wdth" 10
|
|
||||||
///
|
|
||||||
/// We should animate between:
|
|
||||||
///
|
|
||||||
/// "wdth" 5, "wght" 2
|
|
||||||
/// "wght" 4, "wdth" 10
|
|
||||||
///
|
|
||||||
/// This iterator supports this by sorting the two lists, then iterating them in
|
|
||||||
/// reverse, and skipping entries with repeated tag names. It will return
|
|
||||||
/// Some(Err()) if it reaches the end of one list before the other, or if the
|
|
||||||
/// tag names do not match.
|
|
||||||
///
|
|
||||||
/// For the above example, this iterator would return:
|
|
||||||
///
|
|
||||||
/// Some(Ok("wght" 2, "wght" 4))
|
|
||||||
/// Some(Ok("wdth" 5, "wdth" 10))
|
|
||||||
/// None
|
|
||||||
///
|
|
||||||
struct FontSettingTagIter<'a> {
|
|
||||||
a_state: FontSettingTagIterState<'a>,
|
|
||||||
b_state: FontSettingTagIterState<'a>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> FontSettingTagIter<'a> {
|
|
||||||
fn new(
|
|
||||||
a_settings: &'a FontVariationSettings,
|
|
||||||
b_settings: &'a FontVariationSettings,
|
|
||||||
) -> Result<FontSettingTagIter<'a>, ()> {
|
|
||||||
if a_settings.0.is_empty() || b_settings.0.is_empty() {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
fn as_new_sorted_tags(tags: &[ComputedVariationValue]) -> Vec<<&ComputedVariationValue> {
|
|
||||||
use std::iter::FromIterator;
|
|
||||||
let mut sorted_tags = Vec::from_iter(tags.iter());
|
|
||||||
sorted_tags.sort_by_key(|k| k.tag.0);
|
|
||||||
sorted_tags
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(FontSettingTagIter {
|
|
||||||
a_state: FontSettingTagIterState::new(as_new_sorted_tags(&a_settings.0)),
|
|
||||||
b_state: FontSettingTagIterState::new(as_new_sorted_tags(&b_settings.0)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next_tag(state: &mut FontSettingTagIterState<'a>) -> Option<<&'a ComputedVariationValue> {
|
|
||||||
if state.index == 0 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
state.index -= 1;
|
|
||||||
let tag = state.tags[state.index];
|
|
||||||
if tag.tag == state.prev_tag {
|
|
||||||
FontSettingTagIter::next_tag(state)
|
|
||||||
} else {
|
|
||||||
state.prev_tag = tag.tag;
|
|
||||||
Some(tag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Iterator for FontSettingTagIter<'a> {
|
|
||||||
type Item = Result<(&'a ComputedVariationValue, &'a ComputedVariationValue), ()>;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Result<(&'a ComputedVariationValue, &'a ComputedVariationValue), ()>> {
|
|
||||||
match (
|
|
||||||
FontSettingTagIter::next_tag(&mut self.a_state),
|
|
||||||
FontSettingTagIter::next_tag(&mut self.b_state),
|
|
||||||
) {
|
|
||||||
(Some(at), Some(bt)) if at.tag == bt.tag => Some(Ok((at, bt))),
|
|
||||||
(None, None) => None,
|
|
||||||
_ => Some(Err(())), // Mismatch number of unique tags or tag names.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <https://drafts.csswg.org/css-transitions/#animtype-rect>
|
/// <https://drafts.csswg.org/css-transitions/#animtype-rect>
|
||||||
impl Animate for ClipRect {
|
impl Animate for ClipRect {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -2351,10 +2125,21 @@ impl Animate for ComputedRotate {
|
||||||
let from = ComputedRotate::resolve(self);
|
let from = ComputedRotate::resolve(self);
|
||||||
let to = ComputedRotate::resolve(other);
|
let to = ComputedRotate::resolve(other);
|
||||||
|
|
||||||
let (fx, fy, fz, fa) =
|
let (mut fx, mut fy, mut fz, fa) =
|
||||||
transform::get_normalized_vector_and_angle(from.0, from.1, from.2, from.3);
|
transform::get_normalized_vector_and_angle(from.0, from.1, from.2, from.3);
|
||||||
let (tx, ty, tz, ta) =
|
let (mut tx, mut ty, mut tz, ta) =
|
||||||
transform::get_normalized_vector_and_angle(to.0, to.1, to.2, to.3);
|
transform::get_normalized_vector_and_angle(to.0, to.1, to.2, to.3);
|
||||||
|
|
||||||
|
if fa == Angle::from_degrees(0.) {
|
||||||
|
fx = tx;
|
||||||
|
fy = ty;
|
||||||
|
fz = tz;
|
||||||
|
} else if ta == Angle::from_degrees(0.) {
|
||||||
|
tx = fx;
|
||||||
|
ty = fy;
|
||||||
|
tz = fz;
|
||||||
|
}
|
||||||
|
|
||||||
if (fx, fy, fz) == (tx, ty, tz) {
|
if (fx, fy, fz) == (tx, ty, tz) {
|
||||||
return Ok(Rotate::Rotate3D(fx, fy, fz, fa.animate(&ta, procedure)?));
|
return Ok(Rotate::Rotate3D(fx, fy, fz, fa.animate(&ta, procedure)?));
|
||||||
}
|
}
|
||||||
|
@ -2747,205 +2532,6 @@ impl ComputeSquaredDistance for ComputedTransform {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Animated SVGPaint
|
|
||||||
pub type IntermediateSVGPaint = SVGPaint<AnimatedColor, ComputedUrl>;
|
|
||||||
|
|
||||||
/// Animated SVGPaintKind
|
|
||||||
pub type IntermediateSVGPaintKind = SVGPaintKind<AnimatedColor, ComputedUrl>;
|
|
||||||
|
|
||||||
impl ToAnimatedZero for IntermediateSVGPaint {
|
|
||||||
#[inline]
|
|
||||||
fn to_animated_zero(&self) -> Result<Self, ()> {
|
|
||||||
Ok(IntermediateSVGPaint {
|
|
||||||
kind: self.kind.to_animated_zero()?,
|
|
||||||
fallback: self.fallback.and_then(|v| v.to_animated_zero().ok()),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<NonNegativeLengthOrPercentage> for NumberOrPercentage {
|
|
||||||
fn from(lop: NonNegativeLengthOrPercentage) -> NumberOrPercentage {
|
|
||||||
lop.0.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<NonNegativeNumber> for NumberOrPercentage {
|
|
||||||
fn from(num: NonNegativeNumber) -> NumberOrPercentage {
|
|
||||||
num.0.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<LengthOrPercentage> for NumberOrPercentage {
|
|
||||||
fn from(lop: LengthOrPercentage) -> NumberOrPercentage {
|
|
||||||
match lop {
|
|
||||||
LengthOrPercentage::Length(len) => NumberOrPercentage::Number(len.px()),
|
|
||||||
LengthOrPercentage::Percentage(p) => NumberOrPercentage::Percentage(p),
|
|
||||||
LengthOrPercentage::Calc(_) => {
|
|
||||||
panic!("We dont't expected calc interpolation for SvgLengthOrPercentageOrNumber");
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Number> for NumberOrPercentage {
|
|
||||||
fn from(num: Number) -> NumberOrPercentage {
|
|
||||||
NumberOrPercentage::Number(num)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn convert_to_number_or_percentage<LengthOrPercentageType, NumberType>(
|
|
||||||
from: SvgLengthOrPercentageOrNumber<LengthOrPercentageType, NumberType>)
|
|
||||||
-> NumberOrPercentage
|
|
||||||
where LengthOrPercentageType: Into<NumberOrPercentage>,
|
|
||||||
NumberType: Into<NumberOrPercentage>
|
|
||||||
{
|
|
||||||
match from {
|
|
||||||
SvgLengthOrPercentageOrNumber::LengthOrPercentage(lop) => {
|
|
||||||
lop.into()
|
|
||||||
}
|
|
||||||
SvgLengthOrPercentageOrNumber::Number(num) => {
|
|
||||||
num.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn convert_from_number_or_percentage<LengthOrPercentageType, NumberType>(
|
|
||||||
from: NumberOrPercentage)
|
|
||||||
-> SvgLengthOrPercentageOrNumber<LengthOrPercentageType, NumberType>
|
|
||||||
where LengthOrPercentageType: From<LengthOrPercentage>,
|
|
||||||
NumberType: From<Number>
|
|
||||||
{
|
|
||||||
match from {
|
|
||||||
NumberOrPercentage::Number(num) =>
|
|
||||||
SvgLengthOrPercentageOrNumber::Number(num.into()),
|
|
||||||
NumberOrPercentage::Percentage(p) =>
|
|
||||||
SvgLengthOrPercentageOrNumber::LengthOrPercentage(
|
|
||||||
(LengthOrPercentage::Percentage(p)).into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <L, N> Animate for SvgLengthOrPercentageOrNumber<L, N>
|
|
||||||
where
|
|
||||||
L: Animate + From<LengthOrPercentage> + Into<NumberOrPercentage> + Copy,
|
|
||||||
N: Animate + From<Number> + Into<NumberOrPercentage>,
|
|
||||||
LengthOrPercentage: From<L>,
|
|
||||||
Self: Copy,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
||||||
if self.has_calc() || other.has_calc() {
|
|
||||||
// TODO: We need to treat calc value.
|
|
||||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1386967
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let this = convert_to_number_or_percentage(*self);
|
|
||||||
let other = convert_to_number_or_percentage(*other);
|
|
||||||
|
|
||||||
match (this, other) {
|
|
||||||
(
|
|
||||||
NumberOrPercentage::Number(ref this),
|
|
||||||
NumberOrPercentage::Number(ref other),
|
|
||||||
) => {
|
|
||||||
Ok(convert_from_number_or_percentage(
|
|
||||||
NumberOrPercentage::Number(this.animate(other, procedure)?)
|
|
||||||
))
|
|
||||||
},
|
|
||||||
(
|
|
||||||
NumberOrPercentage::Percentage(ref this),
|
|
||||||
NumberOrPercentage::Percentage(ref other),
|
|
||||||
) => {
|
|
||||||
Ok(convert_from_number_or_percentage(
|
|
||||||
NumberOrPercentage::Percentage(this.animate(other, procedure)?)
|
|
||||||
))
|
|
||||||
},
|
|
||||||
_ => Err(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<L> Animate for SVGLength<L>
|
|
||||||
where
|
|
||||||
L: Animate + Clone,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
||||||
match (self, other) {
|
|
||||||
(&SVGLength::Length(ref this), &SVGLength::Length(ref other)) => {
|
|
||||||
Ok(SVGLength::Length(this.animate(other, procedure)?))
|
|
||||||
},
|
|
||||||
_ => Err(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <https://www.w3.org/TR/SVG11/painting.html#StrokeDasharrayProperty>
|
|
||||||
impl<L> Animate for SVGStrokeDashArray<L>
|
|
||||||
where
|
|
||||||
L: Clone + Animate,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
||||||
if matches!(procedure, Procedure::Add | Procedure::Accumulate { .. }) {
|
|
||||||
// Non-additive.
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
match (self, other) {
|
|
||||||
(&SVGStrokeDashArray::Values(ref this), &SVGStrokeDashArray::Values(ref other)) => {
|
|
||||||
Ok(SVGStrokeDashArray::Values(this.animate_repeatable_list(other, procedure)?))
|
|
||||||
},
|
|
||||||
_ => Err(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<L> ComputeSquaredDistance for SVGStrokeDashArray<L>
|
|
||||||
where
|
|
||||||
L: ComputeSquaredDistance,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
|
||||||
match (self, other) {
|
|
||||||
(&SVGStrokeDashArray::Values(ref this), &SVGStrokeDashArray::Values(ref other)) => {
|
|
||||||
this.squared_distance_repeatable_list(other)
|
|
||||||
},
|
|
||||||
_ => Err(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<L> ToAnimatedZero for SVGStrokeDashArray<L>
|
|
||||||
where
|
|
||||||
L: ToAnimatedZero,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn to_animated_zero(&self) -> Result<Self, ()> {
|
|
||||||
match *self {
|
|
||||||
SVGStrokeDashArray::Values(ref values) => {
|
|
||||||
Ok(SVGStrokeDashArray::Values(
|
|
||||||
values.iter().map(ToAnimatedZero::to_animated_zero).collect::<Result<Vec<_>, _>>()?,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
SVGStrokeDashArray::ContextValue => Ok(SVGStrokeDashArray::ContextValue),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<O> Animate for SVGOpacity<O>
|
|
||||||
where
|
|
||||||
O: Animate + Clone,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
||||||
match (self, other) {
|
|
||||||
(&SVGOpacity::Opacity(ref this), &SVGOpacity::Opacity(ref other)) => {
|
|
||||||
Ok(SVGOpacity::Opacity(this.animate(other, procedure)?))
|
|
||||||
},
|
|
||||||
_ => Err(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
<%
|
<%
|
||||||
FILTER_FUNCTIONS = [ 'Blur', 'Brightness', 'Contrast', 'Grayscale',
|
FILTER_FUNCTIONS = [ 'Blur', 'Brightness', 'Contrast', 'Grayscale',
|
||||||
'HueRotate', 'Invert', 'Opacity', 'Saturate',
|
'HueRotate', 'Invert', 'Opacity', 'Saturate',
|
||||||
|
@ -2997,81 +2583,3 @@ impl ToAnimatedZero for AnimatedFilter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The category a property falls into for ordering purposes.
|
|
||||||
///
|
|
||||||
/// https://drafts.csswg.org/web-animations/#calculating-computed-keyframes
|
|
||||||
///
|
|
||||||
#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
|
|
||||||
enum PropertyCategory {
|
|
||||||
Custom,
|
|
||||||
PhysicalLonghand,
|
|
||||||
LogicalLonghand,
|
|
||||||
Shorthand,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PropertyCategory {
|
|
||||||
fn of(id: &PropertyId) -> Self {
|
|
||||||
match *id {
|
|
||||||
PropertyId::Shorthand(..) |
|
|
||||||
PropertyId::ShorthandAlias(..) => PropertyCategory::Shorthand,
|
|
||||||
PropertyId::Longhand(id) |
|
|
||||||
PropertyId::LonghandAlias(id, ..) => {
|
|
||||||
if id.is_logical() {
|
|
||||||
PropertyCategory::LogicalLonghand
|
|
||||||
} else {
|
|
||||||
PropertyCategory::PhysicalLonghand
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PropertyId::Custom(..) => PropertyCategory::Custom,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A comparator to sort PropertyIds such that physical longhands are sorted
|
|
||||||
/// before logical longhands and shorthands, shorthands with fewer components
|
|
||||||
/// are sorted before shorthands with more components, and otherwise shorthands
|
|
||||||
/// are sorted by IDL name as defined by [Web Animations][property-order].
|
|
||||||
///
|
|
||||||
/// Using this allows us to prioritize values specified by longhands (or smaller
|
|
||||||
/// shorthand subsets) when longhands and shorthands are both specified on the
|
|
||||||
/// one keyframe.
|
|
||||||
///
|
|
||||||
/// [property-order] https://drafts.csswg.org/web-animations/#calculating-computed-keyframes
|
|
||||||
pub fn compare_property_priority(a: &PropertyId, b: &PropertyId) -> cmp::Ordering {
|
|
||||||
let a_category = PropertyCategory::of(a);
|
|
||||||
let b_category = PropertyCategory::of(b);
|
|
||||||
|
|
||||||
if a_category != b_category {
|
|
||||||
return a_category.cmp(&b_category);
|
|
||||||
}
|
|
||||||
|
|
||||||
if a_category == PropertyCategory::Shorthand {
|
|
||||||
let a = a.as_shorthand().unwrap();
|
|
||||||
let b = b.as_shorthand().unwrap();
|
|
||||||
// Within shorthands, sort by the number of subproperties, then by IDL
|
|
||||||
// name.
|
|
||||||
let subprop_count_a = a.longhands().count();
|
|
||||||
let subprop_count_b = b.longhands().count();
|
|
||||||
return subprop_count_a.cmp(&subprop_count_b).then_with(|| {
|
|
||||||
get_idl_name_sort_order(a).cmp(&get_idl_name_sort_order(b))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
cmp::Ordering::Equal
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_idl_name_sort_order(shorthand: ShorthandId) -> u32 {
|
|
||||||
<%
|
|
||||||
# Sort by IDL name.
|
|
||||||
sorted_shorthands = sorted(data.shorthands, key=lambda p: to_idl_name(p.ident))
|
|
||||||
|
|
||||||
# Annotate with sorted position
|
|
||||||
sorted_shorthands = [(p, position) for position, p in enumerate(sorted_shorthands)]
|
|
||||||
%>
|
|
||||||
match shorthand {
|
|
||||||
% for property, position in sorted_shorthands:
|
|
||||||
ShorthandId::${property.camel_case} => ${position},
|
|
||||||
% endfor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -112,6 +112,7 @@ ${helpers.predefined_type(
|
||||||
animation_value_type="discrete",
|
animation_value_type="discrete",
|
||||||
flags="APPLIES_TO_FIRST_LETTER",
|
flags="APPLIES_TO_FIRST_LETTER",
|
||||||
boxed=product == "servo",
|
boxed=product == "servo",
|
||||||
|
ignored_when_colors_disabled=True
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${helpers.predefined_type(
|
${helpers.predefined_type(
|
||||||
|
|
|
@ -59,16 +59,14 @@ ${helpers.single_keyword(
|
||||||
spec="https://drafts.csswg.org/css-color/#propdef-color-adjust",
|
spec="https://drafts.csswg.org/css-color/#propdef-color-adjust",
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<% image_rendering_custom_consts = { "crisp-edges": "CRISPEDGES",
|
|
||||||
"-moz-crisp-edges": "CRISPEDGES" } %>
|
|
||||||
// According to to CSS-IMAGES-3, `optimizespeed` and `optimizequality` are synonyms for `auto`
|
// According to to CSS-IMAGES-3, `optimizespeed` and `optimizequality` are synonyms for `auto`
|
||||||
// And, firefox doesn't support `pixelated` yet (https://bugzilla.mozilla.org/show_bug.cgi?id=856337)
|
// And, firefox doesn't support `pixelated` yet (https://bugzilla.mozilla.org/show_bug.cgi?id=856337)
|
||||||
${helpers.single_keyword(
|
${helpers.single_keyword(
|
||||||
"image-rendering",
|
"image-rendering",
|
||||||
"auto",
|
"auto crisp-edges",
|
||||||
extra_gecko_values="optimizespeed optimizequality -moz-crisp-edges",
|
extra_gecko_values="optimizespeed optimizequality",
|
||||||
extra_servo_values="pixelated crisp-edges",
|
extra_servo_values="pixelated",
|
||||||
custom_consts=image_rendering_custom_consts,
|
extra_gecko_aliases="-moz-crisp-edges=crisp-edges",
|
||||||
animation_value_type="discrete",
|
animation_value_type="discrete",
|
||||||
spec="https://drafts.csswg.org/css-images/#propdef-image-rendering",
|
spec="https://drafts.csswg.org/css-images/#propdef-image-rendering",
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -55,10 +55,11 @@ ${helpers.predefined_type(
|
||||||
spec="https://www.w3.org/TR/SVG11/painting.html#FillOpacityProperty",
|
spec="https://www.w3.org/TR/SVG11/painting.html#FillOpacityProperty",
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${helpers.single_keyword(
|
${helpers.predefined_type(
|
||||||
"fill-rule",
|
"fill-rule",
|
||||||
"nonzero evenodd",
|
"FillRule",
|
||||||
gecko_enum_prefix="StyleFillRule",
|
"Default::default()",
|
||||||
|
needs_context=False,
|
||||||
products="gecko",
|
products="gecko",
|
||||||
animation_value_type="discrete",
|
animation_value_type="discrete",
|
||||||
spec="https://www.w3.org/TR/SVG11/painting.html#FillRuleProperty",
|
spec="https://www.w3.org/TR/SVG11/painting.html#FillRuleProperty",
|
||||||
|
@ -84,7 +85,7 @@ ${helpers.predefined_type(
|
||||||
|
|
||||||
${helpers.predefined_type(
|
${helpers.predefined_type(
|
||||||
"stroke-width", "SVGWidth",
|
"stroke-width", "SVGWidth",
|
||||||
"::values::computed::NonNegativeLength::new(1.).into()",
|
"computed::SVGWidth::one()",
|
||||||
products="gecko",
|
products="gecko",
|
||||||
animation_value_type="::values::computed::SVGWidth",
|
animation_value_type="::values::computed::SVGWidth",
|
||||||
spec="https://www.w3.org/TR/SVG2/painting.html#StrokeWidth",
|
spec="https://www.w3.org/TR/SVG2/painting.html#StrokeWidth",
|
||||||
|
@ -134,19 +135,21 @@ ${helpers.predefined_type(
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${helpers.predefined_type(
|
${helpers.predefined_type(
|
||||||
"stroke-dashoffset", "SVGLength",
|
"stroke-dashoffset",
|
||||||
"Au(0).into()",
|
"SVGLength",
|
||||||
|
"computed::SVGLength::zero()",
|
||||||
products="gecko",
|
products="gecko",
|
||||||
animation_value_type="ComputedValue",
|
animation_value_type="ComputedValue",
|
||||||
spec="https://www.w3.org/TR/SVG2/painting.html#StrokeDashing",
|
spec="https://www.w3.org/TR/SVG2/painting.html#StrokeDashing",
|
||||||
)}
|
)}
|
||||||
|
|
||||||
// Section 14 - Clipping, Masking and Compositing
|
// Section 14 - Clipping, Masking and Compositing
|
||||||
${helpers.single_keyword(
|
${helpers.predefined_type(
|
||||||
"clip-rule",
|
"clip-rule",
|
||||||
"nonzero evenodd",
|
"FillRule",
|
||||||
|
"Default::default()",
|
||||||
|
needs_context=False,
|
||||||
products="gecko",
|
products="gecko",
|
||||||
gecko_enum_prefix="StyleFillRule",
|
|
||||||
animation_value_type="discrete",
|
animation_value_type="discrete",
|
||||||
spec="https://www.w3.org/TR/SVG11/masking.html#ClipRuleProperty",
|
spec="https://www.w3.org/TR/SVG11/masking.html#ClipRuleProperty",
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -471,6 +471,20 @@ impl NonCustomPropertyId {
|
||||||
MAP[self.0]
|
MAP[self.0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns whether this property is transitionable.
|
||||||
|
#[inline]
|
||||||
|
pub fn is_transitionable(self) -> bool {
|
||||||
|
${static_non_custom_property_id_set("TRANSITIONABLE", lambda p: p.transitionable)}
|
||||||
|
TRANSITIONABLE.contains(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether this property is animatable.
|
||||||
|
#[inline]
|
||||||
|
pub fn is_animatable(self) -> bool {
|
||||||
|
${static_non_custom_property_id_set("ANIMATABLE", lambda p: p.animatable)}
|
||||||
|
ANIMATABLE.contains(self)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn enabled_for_all_content(self) -> bool {
|
fn enabled_for_all_content(self) -> bool {
|
||||||
${static_non_custom_property_id_set(
|
${static_non_custom_property_id_set(
|
||||||
|
@ -758,6 +772,41 @@ impl<'a> Iterator for LonghandIdSetIterator<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LonghandIdSet {
|
impl LonghandIdSet {
|
||||||
|
#[inline]
|
||||||
|
fn reset() -> &'static Self {
|
||||||
|
${static_longhand_id_set("RESET", lambda p: not p.style_struct.inherited)}
|
||||||
|
&RESET
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn animatable() -> &'static Self {
|
||||||
|
${static_longhand_id_set("ANIMATABLE", lambda p: p.animatable)}
|
||||||
|
&ANIMATABLE
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn discrete_animatable() -> &'static Self {
|
||||||
|
${static_longhand_id_set("DISCRETE_ANIMATABLE", lambda p: p.animation_value_type == "discrete")}
|
||||||
|
&DISCRETE_ANIMATABLE
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn logical() -> &'static Self {
|
||||||
|
${static_longhand_id_set("LOGICAL", lambda p: p.logical)}
|
||||||
|
&LOGICAL
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the set of longhands that are ignored when document colors are
|
||||||
|
/// disabled.
|
||||||
|
#[inline]
|
||||||
|
pub fn ignored_when_colors_disabled() -> &'static Self {
|
||||||
|
${static_longhand_id_set(
|
||||||
|
"IGNORED_WHEN_COLORS_DISABLED",
|
||||||
|
lambda p: p.ignored_when_colors_disabled
|
||||||
|
)}
|
||||||
|
&IGNORED_WHEN_COLORS_DISABLED
|
||||||
|
}
|
||||||
|
|
||||||
/// Iterate over the current longhand id set.
|
/// Iterate over the current longhand id set.
|
||||||
pub fn iter(&self) -> LonghandIdSetIterator {
|
pub fn iter(&self) -> LonghandIdSetIterator {
|
||||||
LonghandIdSetIterator { longhands: self, cur: 0, }
|
LonghandIdSetIterator { longhands: self, cur: 0, }
|
||||||
|
@ -784,6 +833,14 @@ impl LonghandIdSet {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Remove all the given properties from the set.
|
||||||
|
#[inline]
|
||||||
|
pub fn remove_all(&mut self, other: &Self) {
|
||||||
|
for (self_cell, other_cell) in self.storage.iter_mut().zip(other.storage.iter()) {
|
||||||
|
*self_cell &= !*other_cell;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Create an empty set
|
/// Create an empty set
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new() -> LonghandIdSet {
|
pub fn new() -> LonghandIdSet {
|
||||||
|
@ -800,8 +857,7 @@ impl LonghandIdSet {
|
||||||
/// Return whether this set contains any reset longhand.
|
/// Return whether this set contains any reset longhand.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn contains_any_reset(&self) -> bool {
|
pub fn contains_any_reset(&self) -> bool {
|
||||||
${static_longhand_id_set("RESET", lambda p: not p.style_struct.inherited)}
|
self.contains_any(Self::reset())
|
||||||
self.contains_any(&RESET)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add the given property to the set
|
/// Add the given property to the set
|
||||||
|
@ -961,9 +1017,8 @@ impl LonghandId {
|
||||||
|
|
||||||
/// Returns whether the longhand property is inherited by default.
|
/// Returns whether the longhand property is inherited by default.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn inherited(&self) -> bool {
|
pub fn inherited(self) -> bool {
|
||||||
${static_longhand_id_set("INHERITED", lambda p: p.style_struct.inherited)}
|
!LonghandIdSet::reset().contains(self)
|
||||||
INHERITED.contains(*self)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shorthands(&self) -> NonCustomPropertyIterator<ShorthandId> {
|
fn shorthands(&self) -> NonCustomPropertyIterator<ShorthandId> {
|
||||||
|
@ -1034,15 +1089,13 @@ impl LonghandId {
|
||||||
/// Returns whether this property is animatable.
|
/// Returns whether this property is animatable.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_animatable(self) -> bool {
|
pub fn is_animatable(self) -> bool {
|
||||||
${static_longhand_id_set("ANIMATABLE", lambda p: p.animatable)}
|
LonghandIdSet::animatable().contains(self)
|
||||||
ANIMATABLE.contains(self)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether this property is animatable in a discrete way.
|
/// Returns whether this property is animatable in a discrete way.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_discrete_animatable(self) -> bool {
|
pub fn is_discrete_animatable(self) -> bool {
|
||||||
${static_longhand_id_set("DISCRETE_ANIMATABLE", lambda p: p.animation_value_type == "discrete")}
|
LonghandIdSet::discrete_animatable().contains(self)
|
||||||
DISCRETE_ANIMATABLE.contains(self)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts from a LonghandId to an adequate nsCSSPropertyID.
|
/// Converts from a LonghandId to an adequate nsCSSPropertyID.
|
||||||
|
@ -1065,9 +1118,8 @@ impl LonghandId {
|
||||||
|
|
||||||
/// Return whether this property is logical.
|
/// Return whether this property is logical.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_logical(&self) -> bool {
|
pub fn is_logical(self) -> bool {
|
||||||
${static_longhand_id_set("LOGICAL", lambda p: p.logical)}
|
LonghandIdSet::logical().contains(self)
|
||||||
LOGICAL.contains(*self)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If this is a logical property, return the corresponding physical one in
|
/// If this is a logical property, return the corresponding physical one in
|
||||||
|
@ -1157,12 +1209,7 @@ impl LonghandId {
|
||||||
/// colors are disabled.
|
/// colors are disabled.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn ignored_when_document_colors_disabled(self) -> bool {
|
fn ignored_when_document_colors_disabled(self) -> bool {
|
||||||
${static_longhand_id_set(
|
LonghandIdSet::ignored_when_colors_disabled().contains(self)
|
||||||
"IGNORED_WHEN_COLORS_DISABLED",
|
|
||||||
lambda p: p.ignored_when_colors_disabled
|
|
||||||
)}
|
|
||||||
|
|
||||||
IGNORED_WHEN_COLORS_DISABLED.contains(self)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The computed value of some properties depends on the (sometimes
|
/// The computed value of some properties depends on the (sometimes
|
||||||
|
@ -1420,6 +1467,25 @@ impl ShorthandId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the order in which this property appears relative to other
|
||||||
|
/// shorthands in idl-name-sorting order.
|
||||||
|
#[inline]
|
||||||
|
pub fn idl_name_sort_order(self) -> u32 {
|
||||||
|
<%
|
||||||
|
from data import to_idl_name
|
||||||
|
ordered = {}
|
||||||
|
sorted_shorthands = sorted(data.shorthands, key=lambda p: to_idl_name(p.ident))
|
||||||
|
for order, shorthand in enumerate(sorted_shorthands):
|
||||||
|
ordered[shorthand.ident] = order
|
||||||
|
%>
|
||||||
|
static IDL_NAME_SORT_ORDER: [u32; ${len(data.shorthands)}] = [
|
||||||
|
% for property in data.shorthands:
|
||||||
|
${ordered[property.ident]},
|
||||||
|
% endfor
|
||||||
|
];
|
||||||
|
IDL_NAME_SORT_ORDER[self as usize]
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_into<'i, 't>(
|
fn parse_into<'i, 't>(
|
||||||
&self,
|
&self,
|
||||||
declarations: &mut SourcePropertyDeclaration,
|
declarations: &mut SourcePropertyDeclaration,
|
||||||
|
@ -1470,10 +1536,14 @@ impl UnparsedValue {
|
||||||
longhand_id: LonghandId,
|
longhand_id: LonghandId,
|
||||||
custom_properties: Option<<&Arc<::custom_properties::CustomPropertiesMap>>,
|
custom_properties: Option<<&Arc<::custom_properties::CustomPropertiesMap>>,
|
||||||
quirks_mode: QuirksMode,
|
quirks_mode: QuirksMode,
|
||||||
|
environment: &::custom_properties::CssEnvironment,
|
||||||
) -> PropertyDeclaration {
|
) -> PropertyDeclaration {
|
||||||
::custom_properties::substitute(&self.css, self.first_token_type, custom_properties)
|
::custom_properties::substitute(
|
||||||
.ok()
|
&self.css,
|
||||||
.and_then(|css| {
|
self.first_token_type,
|
||||||
|
custom_properties,
|
||||||
|
environment,
|
||||||
|
).ok().and_then(|css| {
|
||||||
// As of this writing, only the base URL is used for property
|
// As of this writing, only the base URL is used for property
|
||||||
// values.
|
// values.
|
||||||
//
|
//
|
||||||
|
@ -2148,11 +2218,16 @@ impl PropertyDeclaration {
|
||||||
WideKeywordDeclaration { id, keyword },
|
WideKeywordDeclaration { id, keyword },
|
||||||
)
|
)
|
||||||
}).or_else(|()| {
|
}).or_else(|()| {
|
||||||
input.look_for_var_functions();
|
input.look_for_var_or_env_functions();
|
||||||
input.parse_entirely(|input| id.parse_value(context, input))
|
input.parse_entirely(|input| id.parse_value(context, input))
|
||||||
.or_else(|err| {
|
.or_else(|err| {
|
||||||
while let Ok(_) = input.next() {} // Look for var() after the error.
|
while let Ok(_) = input.next() {} // Look for var() after the error.
|
||||||
if input.seen_var_functions() {
|
if !input.seen_var_or_env_functions() {
|
||||||
|
return Err(StyleParseErrorKind::new_invalid(
|
||||||
|
non_custom_id.unwrap().name(),
|
||||||
|
err,
|
||||||
|
));
|
||||||
|
}
|
||||||
input.reset(&start);
|
input.reset(&start);
|
||||||
let (first_token_type, css) =
|
let (first_token_type, css) =
|
||||||
::custom_properties::parse_non_custom_with_var(input).map_err(|e| {
|
::custom_properties::parse_non_custom_with_var(input).map_err(|e| {
|
||||||
|
@ -2170,12 +2245,6 @@ impl PropertyDeclaration {
|
||||||
from_shorthand: None,
|
from_shorthand: None,
|
||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
} else {
|
|
||||||
Err(StyleParseErrorKind::new_invalid(
|
|
||||||
non_custom_id.unwrap().name(),
|
|
||||||
err,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}).map(|declaration| {
|
}).map(|declaration| {
|
||||||
declarations.push(declaration)
|
declarations.push(declaration)
|
||||||
|
@ -2198,12 +2267,13 @@ impl PropertyDeclaration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
input.look_for_var_functions();
|
input.look_for_var_or_env_functions();
|
||||||
// Not using parse_entirely here: each ${shorthand.ident}::parse_into function
|
// Not using parse_entirely here: each
|
||||||
// needs to do so *before* pushing to `declarations`.
|
// ${shorthand.ident}::parse_into function needs to do so
|
||||||
|
// *before* pushing to `declarations`.
|
||||||
id.parse_into(declarations, context, input).or_else(|err| {
|
id.parse_into(declarations, context, input).or_else(|err| {
|
||||||
while let Ok(_) = input.next() {} // Look for var() after the error.
|
while let Ok(_) = input.next() {} // Look for var() after the error.
|
||||||
if !input.seen_var_functions() {
|
if !input.seen_var_or_env_functions() {
|
||||||
return Err(StyleParseErrorKind::new_invalid(
|
return Err(StyleParseErrorKind::new_invalid(
|
||||||
non_custom_id.unwrap().name(),
|
non_custom_id.unwrap().name(),
|
||||||
err,
|
err,
|
||||||
|
|
|
@ -1288,24 +1288,12 @@ impl StrongRuleNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If author colors are not allowed, only claim to have author-specified
|
// If author colors are not allowed, don't look at those properties
|
||||||
// rules if we're looking at a non-color property or if we're looking at
|
// (except for background-color which is special and we handle below).
|
||||||
// the background color and it's set to transparent.
|
|
||||||
const IGNORED_WHEN_COLORS_DISABLED: &'static [LonghandId] = &[
|
|
||||||
LonghandId::BackgroundImage,
|
|
||||||
LonghandId::BorderTopColor,
|
|
||||||
LonghandId::BorderRightColor,
|
|
||||||
LonghandId::BorderBottomColor,
|
|
||||||
LonghandId::BorderLeftColor,
|
|
||||||
LonghandId::BorderInlineStartColor,
|
|
||||||
LonghandId::BorderInlineEndColor,
|
|
||||||
LonghandId::BorderBlockStartColor,
|
|
||||||
LonghandId::BorderBlockEndColor,
|
|
||||||
];
|
|
||||||
|
|
||||||
if !author_colors_allowed {
|
if !author_colors_allowed {
|
||||||
for id in IGNORED_WHEN_COLORS_DISABLED {
|
properties.remove_all(LonghandIdSet::ignored_when_colors_disabled());
|
||||||
properties.remove(*id);
|
if rule_type_mask & NS_AUTHOR_SPECIFIED_BACKGROUND != 0 {
|
||||||
|
properties.insert(LonghandId::BackgroundColor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use cssparser::RGBA;
|
use cssparser::RGBA;
|
||||||
|
use custom_properties::CssEnvironment;
|
||||||
use euclid::{Size2D, TypedScale, TypedSize2D};
|
use euclid::{Size2D, TypedScale, TypedSize2D};
|
||||||
use media_queries::MediaType;
|
use media_queries::MediaType;
|
||||||
use media_queries::media_feature::{AllowsRanges, ParsingRequirements};
|
use media_queries::media_feature::{AllowsRanges, ParsingRequirements};
|
||||||
|
@ -49,6 +50,9 @@ pub struct Device {
|
||||||
/// Whether any styles computed in the document relied on the viewport size.
|
/// Whether any styles computed in the document relied on the viewport size.
|
||||||
#[ignore_malloc_size_of = "Pure stack type"]
|
#[ignore_malloc_size_of = "Pure stack type"]
|
||||||
used_viewport_units: AtomicBool,
|
used_viewport_units: AtomicBool,
|
||||||
|
/// The CssEnvironment object responsible of getting CSS environment
|
||||||
|
/// variables.
|
||||||
|
environment: CssEnvironment,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Device {
|
impl Device {
|
||||||
|
@ -66,9 +70,16 @@ impl Device {
|
||||||
root_font_size: AtomicIsize::new(FontSize::medium().size().0 as isize),
|
root_font_size: AtomicIsize::new(FontSize::medium().size().0 as isize),
|
||||||
used_root_font_size: AtomicBool::new(false),
|
used_root_font_size: AtomicBool::new(false),
|
||||||
used_viewport_units: AtomicBool::new(false),
|
used_viewport_units: AtomicBool::new(false),
|
||||||
|
environment: CssEnvironment,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the relevant environment to resolve `env()` functions.
|
||||||
|
#[inline]
|
||||||
|
pub fn environment(&self) -> &CssEnvironment {
|
||||||
|
&self.environment
|
||||||
|
}
|
||||||
|
|
||||||
/// Return the default computed values for this device.
|
/// Return the default computed values for this device.
|
||||||
pub fn default_computed_values(&self) -> &ComputedValues {
|
pub fn default_computed_values(&self) -> &ComputedValues {
|
||||||
// FIXME(bz): This isn't really right, but it's no more wrong
|
// FIXME(bz): This isn't really right, but it's no more wrong
|
||||||
|
|
|
@ -605,11 +605,11 @@ impl Stylist {
|
||||||
maybe = maybe || f(&*data);
|
maybe = maybe || f(&*data);
|
||||||
});
|
});
|
||||||
|
|
||||||
if maybe || !doc_author_rules_apply {
|
if maybe || f(&self.cascade_data.user) {
|
||||||
return maybe;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
f(&self.cascade_data.author) || f(&self.cascade_data.user)
|
doc_author_rules_apply && f(&self.cascade_data.author)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes the style for a given "precomputed" pseudo-element, taking the
|
/// Computes the style for a given "precomputed" pseudo-element, taking the
|
||||||
|
@ -1491,24 +1491,8 @@ impl Stylist {
|
||||||
// the lookups, which means that the bitvecs are comparable. We verify
|
// the lookups, which means that the bitvecs are comparable. We verify
|
||||||
// this in the caller by asserting that the bitvecs are same-length.
|
// this in the caller by asserting that the bitvecs are same-length.
|
||||||
let mut results = SmallBitVec::new();
|
let mut results = SmallBitVec::new();
|
||||||
for (data, _) in self.cascade_data.iter_origins() {
|
|
||||||
data.selectors_for_cache_revalidation.lookup(
|
|
||||||
element,
|
|
||||||
self.quirks_mode,
|
|
||||||
|selector_and_hashes| {
|
|
||||||
results.push(matches_selector(
|
|
||||||
&selector_and_hashes.selector,
|
|
||||||
selector_and_hashes.selector_offset,
|
|
||||||
Some(&selector_and_hashes.hashes),
|
|
||||||
&element,
|
|
||||||
&mut matching_context,
|
|
||||||
flags_setter,
|
|
||||||
));
|
|
||||||
true
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
let matches_document_rules =
|
||||||
element.each_applicable_non_document_style_rule_data(|data, quirks_mode, host| {
|
element.each_applicable_non_document_style_rule_data(|data, quirks_mode, host| {
|
||||||
matching_context.with_shadow_host(host, |matching_context| {
|
matching_context.with_shadow_host(host, |matching_context| {
|
||||||
data.selectors_for_cache_revalidation.lookup(
|
data.selectors_for_cache_revalidation.lookup(
|
||||||
|
@ -1529,6 +1513,29 @@ impl Stylist {
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
for (data, origin) in self.cascade_data.iter_origins() {
|
||||||
|
if origin == Origin::Author && !matches_document_rules {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.selectors_for_cache_revalidation.lookup(
|
||||||
|
element,
|
||||||
|
self.quirks_mode,
|
||||||
|
|selector_and_hashes| {
|
||||||
|
results.push(matches_selector(
|
||||||
|
&selector_and_hashes.selector,
|
||||||
|
selector_and_hashes.selector_offset,
|
||||||
|
Some(&selector_and_hashes.hashes),
|
||||||
|
&element,
|
||||||
|
&mut matching_context,
|
||||||
|
flags_setter,
|
||||||
|
));
|
||||||
|
true
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
results
|
results
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
156
components/style/values/animated/font.rs
Normal file
156
components/style/values/animated/font.rs
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
//! Animation implementation for various font-related types.
|
||||||
|
|
||||||
|
use super::{Animate, Procedure, ToAnimatedZero};
|
||||||
|
use values::computed::Number;
|
||||||
|
use values::computed::font::{FontWeight, FontVariationSettings};
|
||||||
|
use values::distance::{ComputeSquaredDistance, SquaredDistance};
|
||||||
|
use values::generics::font::{FontSettings as GenericFontSettings, FontTag, VariationValue};
|
||||||
|
|
||||||
|
impl ToAnimatedZero for FontWeight {
|
||||||
|
#[inline]
|
||||||
|
fn to_animated_zero(&self) -> Result<Self, ()> {
|
||||||
|
Ok(FontWeight::normal())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def>
|
||||||
|
impl Animate for FontVariationSettings {
|
||||||
|
#[inline]
|
||||||
|
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
||||||
|
FontSettingTagIter::new(self, other)?
|
||||||
|
.map(|r| r.and_then(|(st, ot)| st.animate(&ot, procedure)))
|
||||||
|
.collect::<Result<Vec<ComputedVariationValue>, ()>>()
|
||||||
|
.map(|v| GenericFontSettings(v.into_boxed_slice()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComputeSquaredDistance for FontVariationSettings {
|
||||||
|
#[inline]
|
||||||
|
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
||||||
|
FontSettingTagIter::new(self, other)?
|
||||||
|
.map(|r| r.and_then(|(st, ot)| st.compute_squared_distance(&ot)))
|
||||||
|
.sum()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToAnimatedZero for FontVariationSettings {
|
||||||
|
#[inline]
|
||||||
|
fn to_animated_zero(&self) -> Result<Self, ()> {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ComputedVariationValue = VariationValue<Number>;
|
||||||
|
|
||||||
|
// FIXME: Could do a rename, this is only used for font variations.
|
||||||
|
struct FontSettingTagIterState<'a> {
|
||||||
|
tags: Vec<(&'a ComputedVariationValue)>,
|
||||||
|
index: usize,
|
||||||
|
prev_tag: FontTag,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> FontSettingTagIterState<'a> {
|
||||||
|
fn new(tags: Vec<&'a ComputedVariationValue>) -> FontSettingTagIterState<'a> {
|
||||||
|
FontSettingTagIterState {
|
||||||
|
index: tags.len(),
|
||||||
|
tags,
|
||||||
|
prev_tag: FontTag(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterator for font-variation-settings tag lists
|
||||||
|
///
|
||||||
|
/// [CSS fonts level 4](https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-variation-settings)
|
||||||
|
/// defines the animation of font-variation-settings as follows:
|
||||||
|
///
|
||||||
|
/// Two declarations of font-feature-settings[sic] can be animated between if
|
||||||
|
/// they are "like". "Like" declarations are ones where the same set of
|
||||||
|
/// properties appear (in any order). Because succesive[sic] duplicate
|
||||||
|
/// properties are applied instead of prior duplicate properties, two
|
||||||
|
/// declarations can be "like" even if they have differing number of
|
||||||
|
/// properties. If two declarations are "like" then animation occurs pairwise
|
||||||
|
/// between corresponding values in the declarations.
|
||||||
|
///
|
||||||
|
/// In other words if we have the following lists:
|
||||||
|
///
|
||||||
|
/// "wght" 1.4, "wdth" 5, "wght" 2
|
||||||
|
/// "wdth" 8, "wght" 4, "wdth" 10
|
||||||
|
///
|
||||||
|
/// We should animate between:
|
||||||
|
///
|
||||||
|
/// "wdth" 5, "wght" 2
|
||||||
|
/// "wght" 4, "wdth" 10
|
||||||
|
///
|
||||||
|
/// This iterator supports this by sorting the two lists, then iterating them in
|
||||||
|
/// reverse, and skipping entries with repeated tag names. It will return
|
||||||
|
/// Some(Err()) if it reaches the end of one list before the other, or if the
|
||||||
|
/// tag names do not match.
|
||||||
|
///
|
||||||
|
/// For the above example, this iterator would return:
|
||||||
|
///
|
||||||
|
/// Some(Ok("wght" 2, "wght" 4))
|
||||||
|
/// Some(Ok("wdth" 5, "wdth" 10))
|
||||||
|
/// None
|
||||||
|
///
|
||||||
|
struct FontSettingTagIter<'a> {
|
||||||
|
a_state: FontSettingTagIterState<'a>,
|
||||||
|
b_state: FontSettingTagIterState<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> FontSettingTagIter<'a> {
|
||||||
|
fn new(
|
||||||
|
a_settings: &'a FontVariationSettings,
|
||||||
|
b_settings: &'a FontVariationSettings,
|
||||||
|
) -> Result<FontSettingTagIter<'a>, ()> {
|
||||||
|
if a_settings.0.is_empty() || b_settings.0.is_empty() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_new_sorted_tags(tags: &[ComputedVariationValue]) -> Vec<&ComputedVariationValue> {
|
||||||
|
use std::iter::FromIterator;
|
||||||
|
let mut sorted_tags = Vec::from_iter(tags.iter());
|
||||||
|
sorted_tags.sort_by_key(|k| k.tag.0);
|
||||||
|
sorted_tags
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(FontSettingTagIter {
|
||||||
|
a_state: FontSettingTagIterState::new(as_new_sorted_tags(&a_settings.0)),
|
||||||
|
b_state: FontSettingTagIterState::new(as_new_sorted_tags(&b_settings.0)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_tag(state: &mut FontSettingTagIterState<'a>) -> Option<&'a ComputedVariationValue> {
|
||||||
|
if state.index == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.index -= 1;
|
||||||
|
let tag = state.tags[state.index];
|
||||||
|
if tag.tag == state.prev_tag {
|
||||||
|
FontSettingTagIter::next_tag(state)
|
||||||
|
} else {
|
||||||
|
state.prev_tag = tag.tag;
|
||||||
|
Some(tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for FontSettingTagIter<'a> {
|
||||||
|
type Item = Result<(&'a ComputedVariationValue, &'a ComputedVariationValue), ()>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Result<(&'a ComputedVariationValue, &'a ComputedVariationValue), ()>> {
|
||||||
|
match (
|
||||||
|
FontSettingTagIter::next_tag(&mut self.a_state),
|
||||||
|
FontSettingTagIter::next_tag(&mut self.b_state),
|
||||||
|
) {
|
||||||
|
(Some(at), Some(bt)) if at.tag == bt.tag => Some(Ok((at, bt))),
|
||||||
|
(None, None) => None,
|
||||||
|
_ => Some(Err(())), // Mismatch number of unique tags or tag names.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
118
components/style/values/animated/length.rs
Normal file
118
components/style/values/animated/length.rs
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
//! Animation implementation for various length-related types.
|
||||||
|
|
||||||
|
use super::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero};
|
||||||
|
use values::computed::MaxLength as ComputedMaxLength;
|
||||||
|
use values::computed::MozLength as ComputedMozLength;
|
||||||
|
use values::computed::Percentage;
|
||||||
|
use values::computed::length::{Length, CalcLengthOrPercentage, LengthOrPercentageOrNone, LengthOrPercentageOrAuto};
|
||||||
|
|
||||||
|
/// <https://drafts.csswg.org/css-transitions/#animtype-lpcalc>
|
||||||
|
impl Animate for CalcLengthOrPercentage {
|
||||||
|
#[inline]
|
||||||
|
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
||||||
|
let animate_percentage_half = |this: Option<Percentage>, other: Option<Percentage>| {
|
||||||
|
if this.is_none() && other.is_none() {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
let this = this.unwrap_or_default();
|
||||||
|
let other = other.unwrap_or_default();
|
||||||
|
Ok(Some(this.animate(&other, procedure)?))
|
||||||
|
};
|
||||||
|
|
||||||
|
let length = self.unclamped_length().animate(&other.unclamped_length(), procedure)?;
|
||||||
|
let percentage = animate_percentage_half(self.percentage, other.percentage)?;
|
||||||
|
Ok(CalcLengthOrPercentage::with_clamping_mode(length, percentage, self.clamping_mode))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToAnimatedZero for LengthOrPercentageOrAuto {
|
||||||
|
#[inline]
|
||||||
|
fn to_animated_zero(&self) -> Result<Self, ()> {
|
||||||
|
match *self {
|
||||||
|
LengthOrPercentageOrAuto::Length(_) |
|
||||||
|
LengthOrPercentageOrAuto::Percentage(_) |
|
||||||
|
LengthOrPercentageOrAuto::Calc(_) => {
|
||||||
|
Ok(LengthOrPercentageOrAuto::Length(Length::new(0.)))
|
||||||
|
},
|
||||||
|
LengthOrPercentageOrAuto::Auto => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToAnimatedZero for LengthOrPercentageOrNone {
|
||||||
|
#[inline]
|
||||||
|
fn to_animated_zero(&self) -> Result<Self, ()> {
|
||||||
|
match *self {
|
||||||
|
LengthOrPercentageOrNone::Length(_) |
|
||||||
|
LengthOrPercentageOrNone::Percentage(_) |
|
||||||
|
LengthOrPercentageOrNone::Calc(_) => {
|
||||||
|
Ok(LengthOrPercentageOrNone::Length(Length::new(0.)))
|
||||||
|
},
|
||||||
|
LengthOrPercentageOrNone::None => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToAnimatedValue for ComputedMaxLength {
|
||||||
|
type AnimatedValue = Self;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn to_animated_value(self) -> Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn from_animated_value(animated: Self::AnimatedValue) -> Self {
|
||||||
|
use values::computed::{Length, LengthOrPercentageOrNone, Percentage};
|
||||||
|
use values::generics::length::MaxLength as GenericMaxLength;
|
||||||
|
match animated {
|
||||||
|
GenericMaxLength::LengthOrPercentageOrNone(lopn) => {
|
||||||
|
let result = match lopn {
|
||||||
|
LengthOrPercentageOrNone::Length(px) => {
|
||||||
|
LengthOrPercentageOrNone::Length(Length::new(px.px().max(0.)))
|
||||||
|
},
|
||||||
|
LengthOrPercentageOrNone::Percentage(percentage) => {
|
||||||
|
LengthOrPercentageOrNone::Percentage(Percentage(percentage.0.max(0.)))
|
||||||
|
},
|
||||||
|
_ => lopn,
|
||||||
|
};
|
||||||
|
GenericMaxLength::LengthOrPercentageOrNone(result)
|
||||||
|
},
|
||||||
|
_ => animated,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToAnimatedValue for ComputedMozLength {
|
||||||
|
type AnimatedValue = Self;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn to_animated_value(self) -> Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn from_animated_value(animated: Self::AnimatedValue) -> Self {
|
||||||
|
use values::computed::{Length, LengthOrPercentageOrAuto, Percentage};
|
||||||
|
use values::generics::length::MozLength as GenericMozLength;
|
||||||
|
match animated {
|
||||||
|
GenericMozLength::LengthOrPercentageOrAuto(lopa) => {
|
||||||
|
let result = match lopa {
|
||||||
|
LengthOrPercentageOrAuto::Length(px) => {
|
||||||
|
LengthOrPercentageOrAuto::Length(Length::new(px.px().max(0.)))
|
||||||
|
},
|
||||||
|
LengthOrPercentageOrAuto::Percentage(percentage) => {
|
||||||
|
LengthOrPercentageOrAuto::Percentage(Percentage(percentage.0.max(0.)))
|
||||||
|
},
|
||||||
|
_ => lopa,
|
||||||
|
};
|
||||||
|
GenericMozLength::LengthOrPercentageOrAuto(result)
|
||||||
|
},
|
||||||
|
_ => animated,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,16 +10,81 @@
|
||||||
|
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use euclid::{Point2D, Size2D};
|
use euclid::{Point2D, Size2D};
|
||||||
|
use properties::PropertyId;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
use std::cmp;
|
||||||
use values::computed::Angle as ComputedAngle;
|
use values::computed::Angle as ComputedAngle;
|
||||||
use values::computed::BorderCornerRadius as ComputedBorderCornerRadius;
|
use values::computed::BorderCornerRadius as ComputedBorderCornerRadius;
|
||||||
use values::computed::MaxLength as ComputedMaxLength;
|
|
||||||
use values::computed::MozLength as ComputedMozLength;
|
|
||||||
use values::computed::length::CalcLengthOrPercentage;
|
use values::computed::length::CalcLengthOrPercentage;
|
||||||
use values::computed::url::ComputedUrl;
|
use values::computed::url::ComputedUrl;
|
||||||
|
|
||||||
pub mod color;
|
pub mod color;
|
||||||
pub mod effects;
|
pub mod effects;
|
||||||
|
mod font;
|
||||||
|
mod length;
|
||||||
|
mod svg;
|
||||||
|
|
||||||
|
/// The category a property falls into for ordering purposes.
|
||||||
|
///
|
||||||
|
/// https://drafts.csswg.org/web-animations/#calculating-computed-keyframes
|
||||||
|
#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
|
||||||
|
enum PropertyCategory {
|
||||||
|
Custom,
|
||||||
|
PhysicalLonghand,
|
||||||
|
LogicalLonghand,
|
||||||
|
Shorthand,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PropertyCategory {
|
||||||
|
fn of(id: &PropertyId) -> Self {
|
||||||
|
match *id {
|
||||||
|
PropertyId::Shorthand(..) |
|
||||||
|
PropertyId::ShorthandAlias(..) => PropertyCategory::Shorthand,
|
||||||
|
PropertyId::Longhand(id) |
|
||||||
|
PropertyId::LonghandAlias(id, ..) => {
|
||||||
|
if id.is_logical() {
|
||||||
|
PropertyCategory::LogicalLonghand
|
||||||
|
} else {
|
||||||
|
PropertyCategory::PhysicalLonghand
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PropertyId::Custom(..) => PropertyCategory::Custom,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A comparator to sort PropertyIds such that physical longhands are sorted
|
||||||
|
/// before logical longhands and shorthands, shorthands with fewer components
|
||||||
|
/// are sorted before shorthands with more components, and otherwise shorthands
|
||||||
|
/// are sorted by IDL name as defined by [Web Animations][property-order].
|
||||||
|
///
|
||||||
|
/// Using this allows us to prioritize values specified by longhands (or smaller
|
||||||
|
/// shorthand subsets) when longhands and shorthands are both specified on the
|
||||||
|
/// one keyframe.
|
||||||
|
///
|
||||||
|
/// [property-order] https://drafts.csswg.org/web-animations/#calculating-computed-keyframes
|
||||||
|
pub fn compare_property_priority(a: &PropertyId, b: &PropertyId) -> cmp::Ordering {
|
||||||
|
let a_category = PropertyCategory::of(a);
|
||||||
|
let b_category = PropertyCategory::of(b);
|
||||||
|
|
||||||
|
if a_category != b_category {
|
||||||
|
return a_category.cmp(&b_category);
|
||||||
|
}
|
||||||
|
|
||||||
|
if a_category != PropertyCategory::Shorthand {
|
||||||
|
return cmp::Ordering::Equal;
|
||||||
|
}
|
||||||
|
|
||||||
|
let a = a.as_shorthand().unwrap();
|
||||||
|
let b = b.as_shorthand().unwrap();
|
||||||
|
// Within shorthands, sort by the number of subproperties, then by IDL
|
||||||
|
// name.
|
||||||
|
let subprop_count_a = a.longhands().count();
|
||||||
|
let subprop_count_b = b.longhands().count();
|
||||||
|
subprop_count_a.cmp(&subprop_count_b).then_with(|| {
|
||||||
|
a.idl_name_sort_order().cmp(&b.idl_name_sort_order())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Animate from one value to another.
|
/// Animate from one value to another.
|
||||||
///
|
///
|
||||||
|
@ -281,66 +346,6 @@ impl ToAnimatedValue for ComputedBorderCornerRadius {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToAnimatedValue for ComputedMaxLength {
|
|
||||||
type AnimatedValue = Self;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn to_animated_value(self) -> Self {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn from_animated_value(animated: Self::AnimatedValue) -> Self {
|
|
||||||
use values::computed::{Length, LengthOrPercentageOrNone, Percentage};
|
|
||||||
use values::generics::length::MaxLength as GenericMaxLength;
|
|
||||||
match animated {
|
|
||||||
GenericMaxLength::LengthOrPercentageOrNone(lopn) => {
|
|
||||||
let result = match lopn {
|
|
||||||
LengthOrPercentageOrNone::Length(px) => {
|
|
||||||
LengthOrPercentageOrNone::Length(Length::new(px.px().max(0.)))
|
|
||||||
},
|
|
||||||
LengthOrPercentageOrNone::Percentage(percentage) => {
|
|
||||||
LengthOrPercentageOrNone::Percentage(Percentage(percentage.0.max(0.)))
|
|
||||||
},
|
|
||||||
_ => lopn,
|
|
||||||
};
|
|
||||||
GenericMaxLength::LengthOrPercentageOrNone(result)
|
|
||||||
},
|
|
||||||
_ => animated,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToAnimatedValue for ComputedMozLength {
|
|
||||||
type AnimatedValue = Self;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn to_animated_value(self) -> Self {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn from_animated_value(animated: Self::AnimatedValue) -> Self {
|
|
||||||
use values::computed::{Length, LengthOrPercentageOrAuto, Percentage};
|
|
||||||
use values::generics::length::MozLength as GenericMozLength;
|
|
||||||
match animated {
|
|
||||||
GenericMozLength::LengthOrPercentageOrAuto(lopa) => {
|
|
||||||
let result = match lopa {
|
|
||||||
LengthOrPercentageOrAuto::Length(px) => {
|
|
||||||
LengthOrPercentageOrAuto::Length(Length::new(px.px().max(0.)))
|
|
||||||
},
|
|
||||||
LengthOrPercentageOrAuto::Percentage(percentage) => {
|
|
||||||
LengthOrPercentageOrAuto::Percentage(Percentage(percentage.0.max(0.)))
|
|
||||||
},
|
|
||||||
_ => lopa,
|
|
||||||
};
|
|
||||||
GenericMozLength::LengthOrPercentageOrAuto(result)
|
|
||||||
},
|
|
||||||
_ => animated,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToAnimatedZero for Au {
|
impl ToAnimatedZero for Au {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn to_animated_zero(&self) -> Result<Self, ()> {
|
fn to_animated_zero(&self) -> Result<Self, ()> {
|
||||||
|
|
161
components/style/values/animated/svg.rs
Normal file
161
components/style/values/animated/svg.rs
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
//! Animation implementations for various SVG-related types.
|
||||||
|
|
||||||
|
use properties::animated_properties::ListAnimation;
|
||||||
|
use super::{Animate, Procedure, ToAnimatedZero};
|
||||||
|
use values::animated::color::Color as AnimatedColor;
|
||||||
|
use values::computed::{Number, NumberOrPercentage, LengthOrPercentage};
|
||||||
|
use values::computed::url::ComputedUrl;
|
||||||
|
use values::distance::{ComputeSquaredDistance, SquaredDistance};
|
||||||
|
use values::generics::svg::{SVGLength, SvgLengthOrPercentageOrNumber, SVGPaint};
|
||||||
|
use values::generics::svg::{SVGStrokeDashArray, SVGOpacity};
|
||||||
|
|
||||||
|
/// Animated SVGPaint.
|
||||||
|
pub type IntermediateSVGPaint = SVGPaint<AnimatedColor, ComputedUrl>;
|
||||||
|
|
||||||
|
impl ToAnimatedZero for IntermediateSVGPaint {
|
||||||
|
#[inline]
|
||||||
|
fn to_animated_zero(&self) -> Result<Self, ()> {
|
||||||
|
Ok(IntermediateSVGPaint {
|
||||||
|
kind: self.kind.to_animated_zero()?,
|
||||||
|
fallback: self.fallback.and_then(|v| v.to_animated_zero().ok()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: We need to handle calc here properly, see
|
||||||
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=1386967
|
||||||
|
fn to_number_or_percentage(
|
||||||
|
value: &SvgLengthOrPercentageOrNumber<LengthOrPercentage, Number>,
|
||||||
|
) -> Result<NumberOrPercentage, ()> {
|
||||||
|
Ok(match *value {
|
||||||
|
SvgLengthOrPercentageOrNumber::LengthOrPercentage(ref l) => {
|
||||||
|
match *l {
|
||||||
|
LengthOrPercentage::Length(ref l) => NumberOrPercentage::Number(l.px()),
|
||||||
|
LengthOrPercentage::Percentage(ref p) => NumberOrPercentage::Percentage(*p),
|
||||||
|
LengthOrPercentage::Calc(..) => return Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SvgLengthOrPercentageOrNumber::Number(ref n) => NumberOrPercentage::Number(*n),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Animate for SvgLengthOrPercentageOrNumber<LengthOrPercentage, Number> {
|
||||||
|
#[inline]
|
||||||
|
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
||||||
|
let this = to_number_or_percentage(self)?;
|
||||||
|
let other = to_number_or_percentage(other)?;
|
||||||
|
|
||||||
|
match (this, other) {
|
||||||
|
(
|
||||||
|
NumberOrPercentage::Number(ref this),
|
||||||
|
NumberOrPercentage::Number(ref other),
|
||||||
|
) => {
|
||||||
|
Ok(SvgLengthOrPercentageOrNumber::Number(
|
||||||
|
this.animate(other, procedure)?
|
||||||
|
))
|
||||||
|
},
|
||||||
|
(
|
||||||
|
NumberOrPercentage::Percentage(ref this),
|
||||||
|
NumberOrPercentage::Percentage(ref other),
|
||||||
|
) => {
|
||||||
|
Ok(SvgLengthOrPercentageOrNumber::LengthOrPercentage(
|
||||||
|
LengthOrPercentage::Percentage(this.animate(other, procedure)?)
|
||||||
|
))
|
||||||
|
},
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComputeSquaredDistance for SvgLengthOrPercentageOrNumber<LengthOrPercentage, Number> {
|
||||||
|
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
||||||
|
to_number_or_percentage(self)?
|
||||||
|
.compute_squared_distance(&to_number_or_percentage(other)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L> Animate for SVGLength<L>
|
||||||
|
where
|
||||||
|
L: Animate + Clone,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
||||||
|
match (self, other) {
|
||||||
|
(&SVGLength::Length(ref this), &SVGLength::Length(ref other)) => {
|
||||||
|
Ok(SVGLength::Length(this.animate(other, procedure)?))
|
||||||
|
},
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <https://www.w3.org/TR/SVG11/painting.html#StrokeDasharrayProperty>
|
||||||
|
impl<L> Animate for SVGStrokeDashArray<L>
|
||||||
|
where
|
||||||
|
L: Clone + Animate,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
||||||
|
if matches!(procedure, Procedure::Add | Procedure::Accumulate { .. }) {
|
||||||
|
// Non-additive.
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
match (self, other) {
|
||||||
|
(&SVGStrokeDashArray::Values(ref this), &SVGStrokeDashArray::Values(ref other)) => {
|
||||||
|
Ok(SVGStrokeDashArray::Values(this.animate_repeatable_list(other, procedure)?))
|
||||||
|
},
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L> ComputeSquaredDistance for SVGStrokeDashArray<L>
|
||||||
|
where
|
||||||
|
L: ComputeSquaredDistance,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
||||||
|
match (self, other) {
|
||||||
|
(&SVGStrokeDashArray::Values(ref this), &SVGStrokeDashArray::Values(ref other)) => {
|
||||||
|
this.squared_distance_repeatable_list(other)
|
||||||
|
},
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L> ToAnimatedZero for SVGStrokeDashArray<L>
|
||||||
|
where
|
||||||
|
L: ToAnimatedZero,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn to_animated_zero(&self) -> Result<Self, ()> {
|
||||||
|
match *self {
|
||||||
|
SVGStrokeDashArray::Values(ref values) => {
|
||||||
|
Ok(SVGStrokeDashArray::Values(
|
||||||
|
values.iter().map(ToAnimatedZero::to_animated_zero).collect::<Result<Vec<_>, _>>()?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
SVGStrokeDashArray::ContextValue => Ok(SVGStrokeDashArray::ContextValue),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<O> Animate for SVGOpacity<O>
|
||||||
|
where
|
||||||
|
O: Animate + Clone,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
||||||
|
match (self, other) {
|
||||||
|
(&SVGOpacity::Opacity(ref this), &SVGOpacity::Opacity(ref other)) => {
|
||||||
|
Ok(SVGOpacity::Opacity(this.animate(other, procedure)?))
|
||||||
|
},
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,6 +13,9 @@ use values::computed::{Image, LengthOrPercentage};
|
||||||
use values::computed::url::ComputedUrl;
|
use values::computed::url::ComputedUrl;
|
||||||
use values::generics::basic_shape as generic;
|
use values::generics::basic_shape as generic;
|
||||||
|
|
||||||
|
/// A computed alias for FillRule.
|
||||||
|
pub use values::generics::basic_shape::FillRule;
|
||||||
|
|
||||||
/// A computed clipping shape.
|
/// A computed clipping shape.
|
||||||
pub type ClippingShape = generic::ClippingShape<BasicShape, ComputedUrl>;
|
pub type ClippingShape = generic::ClippingShape<BasicShape, ComputedUrl>;
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ pub use self::align::{AlignContent, AlignItems, JustifyContent, JustifyItems, Se
|
||||||
pub use self::align::{AlignSelf, JustifySelf};
|
pub use self::align::{AlignSelf, JustifySelf};
|
||||||
pub use self::angle::Angle;
|
pub use self::angle::Angle;
|
||||||
pub use self::background::{BackgroundRepeat, BackgroundSize};
|
pub use self::background::{BackgroundRepeat, BackgroundSize};
|
||||||
|
pub use self::basic_shape::FillRule;
|
||||||
pub use self::border::{BorderImageRepeat, BorderImageSideWidth, BorderImageSlice, BorderImageWidth};
|
pub use self::border::{BorderImageRepeat, BorderImageSideWidth, BorderImageSlice, BorderImageWidth};
|
||||||
pub use self::border::{BorderCornerRadius, BorderRadius, BorderSpacing};
|
pub use self::border::{BorderCornerRadius, BorderRadius, BorderSpacing};
|
||||||
pub use self::font::{FontSize, FontSizeAdjust, FontStretch, FontSynthesis, FontVariantAlternates, FontWeight};
|
pub use self::font::{FontSize, FontSizeAdjust, FontStretch, FontSynthesis, FontVariantAlternates, FontWeight};
|
||||||
|
|
|
@ -4,11 +4,8 @@
|
||||||
|
|
||||||
//! Computed types for SVG properties.
|
//! Computed types for SVG properties.
|
||||||
|
|
||||||
use app_units::Au;
|
|
||||||
use values::RGBA;
|
use values::RGBA;
|
||||||
use values::computed::{LengthOrPercentage, NonNegativeLength};
|
use values::computed::{NonNegativeLengthOrPercentage, NonNegativeNumber, Number, LengthOrPercentage, Opacity};
|
||||||
use values::computed::{NonNegativeLengthOrPercentage, NonNegativeNumber, Number};
|
|
||||||
use values::computed::Opacity;
|
|
||||||
use values::computed::color::Color;
|
use values::computed::color::Color;
|
||||||
use values::computed::url::ComputedUrl;
|
use values::computed::url::ComputedUrl;
|
||||||
use values::generics::svg as generic;
|
use values::generics::svg as generic;
|
||||||
|
@ -50,10 +47,11 @@ pub type SvgLengthOrPercentageOrNumber =
|
||||||
/// <length> | <percentage> | <number> | context-value
|
/// <length> | <percentage> | <number> | context-value
|
||||||
pub type SVGLength = generic::SVGLength<SvgLengthOrPercentageOrNumber>;
|
pub type SVGLength = generic::SVGLength<SvgLengthOrPercentageOrNumber>;
|
||||||
|
|
||||||
impl From<Au> for SVGLength {
|
impl SVGLength {
|
||||||
fn from(length: Au) -> Self {
|
/// `0px`
|
||||||
|
pub fn zero() -> Self {
|
||||||
generic::SVGLength::Length(generic::SvgLengthOrPercentageOrNumber::LengthOrPercentage(
|
generic::SVGLength::Length(generic::SvgLengthOrPercentageOrNumber::LengthOrPercentage(
|
||||||
length.into(),
|
LengthOrPercentage::zero()
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,6 +61,8 @@ impl From<Au> for SVGLength {
|
||||||
pub type NonNegativeSvgLengthOrPercentageOrNumber =
|
pub type NonNegativeSvgLengthOrPercentageOrNumber =
|
||||||
generic::SvgLengthOrPercentageOrNumber<NonNegativeLengthOrPercentage, NonNegativeNumber>;
|
generic::SvgLengthOrPercentageOrNumber<NonNegativeLengthOrPercentage, NonNegativeNumber>;
|
||||||
|
|
||||||
|
// FIXME(emilio): This is really hacky, and can go away with a bit of work on
|
||||||
|
// the clone_stroke_width code in gecko.mako.rs.
|
||||||
impl Into<NonNegativeSvgLengthOrPercentageOrNumber> for SvgLengthOrPercentageOrNumber {
|
impl Into<NonNegativeSvgLengthOrPercentageOrNumber> for SvgLengthOrPercentageOrNumber {
|
||||||
fn into(self) -> NonNegativeSvgLengthOrPercentageOrNumber {
|
fn into(self) -> NonNegativeSvgLengthOrPercentageOrNumber {
|
||||||
match self {
|
match self {
|
||||||
|
@ -79,10 +79,12 @@ impl Into<NonNegativeSvgLengthOrPercentageOrNumber> for SvgLengthOrPercentageOrN
|
||||||
/// An non-negative wrapper of SVGLength.
|
/// An non-negative wrapper of SVGLength.
|
||||||
pub type SVGWidth = generic::SVGLength<NonNegativeSvgLengthOrPercentageOrNumber>;
|
pub type SVGWidth = generic::SVGLength<NonNegativeSvgLengthOrPercentageOrNumber>;
|
||||||
|
|
||||||
impl From<NonNegativeLength> for SVGWidth {
|
impl SVGWidth {
|
||||||
fn from(length: NonNegativeLength) -> Self {
|
/// `1px`.
|
||||||
|
pub fn one() -> Self {
|
||||||
|
use values::generics::NonNegative;
|
||||||
generic::SVGLength::Length(generic::SvgLengthOrPercentageOrNumber::LengthOrPercentage(
|
generic::SVGLength::Length(generic::SvgLengthOrPercentageOrNumber::LengthOrPercentage(
|
||||||
length.into(),
|
NonNegative(LengthOrPercentage::one())
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,7 +120,9 @@ impl Parse for GridLine<specified::Integer> {
|
||||||
if val_before_span || grid_line.ident.is_some() {
|
if val_before_span || grid_line.ident.is_some() {
|
||||||
return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
||||||
}
|
}
|
||||||
grid_line.ident = Some(CustomIdent::from_ident(location, &name, &[])?);
|
// NOTE(emilio): `span` is consumed above, so we only need to
|
||||||
|
// reject `auto`.
|
||||||
|
grid_line.ident = Some(CustomIdent::from_ident(location, &name, &["auto"])?);
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,6 @@ use cssparser::Parser;
|
||||||
use parser::{Parse, ParserContext};
|
use parser::{Parse, ParserContext};
|
||||||
use style_traits::{ParseError, StyleParseErrorKind};
|
use style_traits::{ParseError, StyleParseErrorKind};
|
||||||
use values::{Either, None_};
|
use values::{Either, None_};
|
||||||
use values::computed::NumberOrPercentage;
|
|
||||||
use values::computed::length::LengthOrPercentage;
|
|
||||||
use values::distance::{ComputeSquaredDistance, SquaredDistance};
|
|
||||||
|
|
||||||
/// An SVG paint value
|
/// An SVG paint value
|
||||||
///
|
///
|
||||||
|
@ -152,54 +149,6 @@ pub enum SvgLengthOrPercentageOrNumber<LengthOrPercentage, Number> {
|
||||||
Number(Number),
|
Number(Number),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L, N> ComputeSquaredDistance for SvgLengthOrPercentageOrNumber<L, N>
|
|
||||||
where
|
|
||||||
L: ComputeSquaredDistance + Copy + Into<NumberOrPercentage>,
|
|
||||||
N: ComputeSquaredDistance + Copy + Into<NumberOrPercentage>,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
|
||||||
match (self, other) {
|
|
||||||
(
|
|
||||||
&SvgLengthOrPercentageOrNumber::LengthOrPercentage(ref from),
|
|
||||||
&SvgLengthOrPercentageOrNumber::LengthOrPercentage(ref to),
|
|
||||||
) => from.compute_squared_distance(to),
|
|
||||||
(
|
|
||||||
&SvgLengthOrPercentageOrNumber::Number(ref from),
|
|
||||||
&SvgLengthOrPercentageOrNumber::Number(ref to),
|
|
||||||
) => from.compute_squared_distance(to),
|
|
||||||
(
|
|
||||||
&SvgLengthOrPercentageOrNumber::LengthOrPercentage(from),
|
|
||||||
&SvgLengthOrPercentageOrNumber::Number(to),
|
|
||||||
) => from.into().compute_squared_distance(&to.into()),
|
|
||||||
(
|
|
||||||
&SvgLengthOrPercentageOrNumber::Number(from),
|
|
||||||
&SvgLengthOrPercentageOrNumber::LengthOrPercentage(to),
|
|
||||||
) => from.into().compute_squared_distance(&to.into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<LengthOrPercentageType, NumberType>
|
|
||||||
SvgLengthOrPercentageOrNumber<LengthOrPercentageType, NumberType>
|
|
||||||
where
|
|
||||||
LengthOrPercentage: From<LengthOrPercentageType>,
|
|
||||||
LengthOrPercentageType: Copy,
|
|
||||||
{
|
|
||||||
/// return true if this struct has calc value.
|
|
||||||
pub fn has_calc(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
&SvgLengthOrPercentageOrNumber::LengthOrPercentage(lop) => {
|
|
||||||
match LengthOrPercentage::from(lop) {
|
|
||||||
LengthOrPercentage::Calc(_) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parsing the SvgLengthOrPercentageOrNumber. At first, we need to parse number
|
/// Parsing the SvgLengthOrPercentageOrNumber. At first, we need to parse number
|
||||||
/// since prevent converting to the length.
|
/// since prevent converting to the length.
|
||||||
impl<LengthOrPercentageType: Parse, NumberType: Parse> Parse
|
impl<LengthOrPercentageType: Parse, NumberType: Parse> Parse
|
||||||
|
@ -213,10 +162,8 @@ impl<LengthOrPercentageType: Parse, NumberType: Parse> Parse
|
||||||
return Ok(SvgLengthOrPercentageOrNumber::Number(num));
|
return Ok(SvgLengthOrPercentageOrNumber::Number(num));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(lop) = input.try(|i| LengthOrPercentageType::parse(context, i)) {
|
let lop = LengthOrPercentageType::parse(context, input)?;
|
||||||
return Ok(SvgLengthOrPercentageOrNumber::LengthOrPercentage(lop));
|
Ok(SvgLengthOrPercentageOrNumber::LengthOrPercentage(lop))
|
||||||
}
|
|
||||||
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ use std::fmt::{self, Write};
|
||||||
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
|
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
|
||||||
use values::computed::Percentage;
|
use values::computed::Percentage;
|
||||||
use values::generics::basic_shape as generic;
|
use values::generics::basic_shape as generic;
|
||||||
use values::generics::basic_shape::{FillRule, GeometryBox, Path, PolygonCoord};
|
use values::generics::basic_shape::{GeometryBox, Path, PolygonCoord};
|
||||||
use values::generics::basic_shape::{ShapeBox, ShapeSource};
|
use values::generics::basic_shape::{ShapeBox, ShapeSource};
|
||||||
use values::generics::rect::Rect;
|
use values::generics::rect::Rect;
|
||||||
use values::specified::LengthOrPercentage;
|
use values::specified::LengthOrPercentage;
|
||||||
|
@ -25,6 +25,9 @@ use values::specified::position::{HorizontalPosition, Position, PositionComponen
|
||||||
use values::specified::position::{Side, VerticalPosition};
|
use values::specified::position::{Side, VerticalPosition};
|
||||||
use values::specified::url::SpecifiedUrl;
|
use values::specified::url::SpecifiedUrl;
|
||||||
|
|
||||||
|
/// A specified alias for FillRule.
|
||||||
|
pub use values::generics::basic_shape::FillRule;
|
||||||
|
|
||||||
/// A specified clipping shape.
|
/// A specified clipping shape.
|
||||||
pub type ClippingShape = generic::ClippingShape<BasicShape, SpecifiedUrl>;
|
pub type ClippingShape = generic::ClippingShape<BasicShape, SpecifiedUrl>;
|
||||||
|
|
||||||
|
|
|
@ -95,7 +95,7 @@ pub fn parse_line_names<'i, 't>(
|
||||||
while let Ok((loc, ident)) = input.try(|i| -> Result<_, CssParseError<()>> {
|
while let Ok((loc, ident)) = input.try(|i| -> Result<_, CssParseError<()>> {
|
||||||
Ok((i.current_source_location(), i.expect_ident_cloned()?))
|
Ok((i.current_source_location(), i.expect_ident_cloned()?))
|
||||||
}) {
|
}) {
|
||||||
let ident = CustomIdent::from_ident(loc, &ident, &["span"])?;
|
let ident = CustomIdent::from_ident(loc, &ident, &["span", "auto"])?;
|
||||||
values.push(ident);
|
values.push(ident);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ pub use self::align::{AlignContent, AlignItems, AlignSelf, ContentDistribution};
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub use self::align::{JustifyContent, JustifyItems, JustifySelf, SelfAlignment};
|
pub use self::align::{JustifyContent, JustifyItems, JustifySelf, SelfAlignment};
|
||||||
pub use self::background::{BackgroundRepeat, BackgroundSize};
|
pub use self::background::{BackgroundRepeat, BackgroundSize};
|
||||||
|
pub use self::basic_shape::FillRule;
|
||||||
pub use self::border::{BorderCornerRadius, BorderImageSlice, BorderImageWidth};
|
pub use self::border::{BorderCornerRadius, BorderImageSlice, BorderImageWidth};
|
||||||
pub use self::border::{BorderImageRepeat, BorderImageSideWidth};
|
pub use self::border::{BorderImageRepeat, BorderImageSideWidth};
|
||||||
pub use self::border::{BorderRadius, BorderSideWidth, BorderSpacing};
|
pub use self::border::{BorderRadius, BorderSideWidth, BorderSpacing};
|
||||||
|
|
|
@ -488,13 +488,8 @@ impl TextAlign {
|
||||||
/// Convert an enumerated value coming from Gecko to a `TextAlign`.
|
/// Convert an enumerated value coming from Gecko to a `TextAlign`.
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub fn from_gecko_keyword(kw: u32) -> Self {
|
pub fn from_gecko_keyword(kw: u32) -> Self {
|
||||||
use gecko_bindings::structs::NS_STYLE_TEXT_ALIGN_MATCH_PARENT;
|
|
||||||
if kw == NS_STYLE_TEXT_ALIGN_MATCH_PARENT {
|
|
||||||
TextAlign::MatchParent
|
|
||||||
} else {
|
|
||||||
TextAlign::Keyword(TextAlignKeyword::from_gecko_keyword(kw))
|
TextAlign::Keyword(TextAlignKeyword::from_gecko_keyword(kw))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToComputedValue for TextAlign {
|
impl ToComputedValue for TextAlign {
|
||||||
|
|
|
@ -15,7 +15,7 @@ gecko = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
app_units = "0.7"
|
app_units = "0.7"
|
||||||
cssparser = "0.24.0"
|
cssparser = "0.25"
|
||||||
bitflags = "1.0"
|
bitflags = "1.0"
|
||||||
euclid = "0.19"
|
euclid = "0.19"
|
||||||
malloc_size_of = { path = "../malloc_size_of" }
|
malloc_size_of = { path = "../malloc_size_of" }
|
||||||
|
|
|
@ -12,7 +12,7 @@ doctest = false
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "1.0"
|
byteorder = "1.0"
|
||||||
app_units = "0.7"
|
app_units = "0.7"
|
||||||
cssparser = "0.24.0"
|
cssparser = "0.25"
|
||||||
euclid = "0.19"
|
euclid = "0.19"
|
||||||
html5ever = "0.22"
|
html5ever = "0.22"
|
||||||
parking_lot = "0.6"
|
parking_lot = "0.6"
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
use cssparser::{Parser, ParserInput};
|
use cssparser::{Parser, ParserInput};
|
||||||
use servo_arc::Arc;
|
use servo_arc::Arc;
|
||||||
use style::custom_properties::{Name, SpecifiedValue, CustomPropertiesMap, CustomPropertiesBuilder};
|
use style::custom_properties::{Name, SpecifiedValue, CustomPropertiesMap, CustomPropertiesBuilder, CssEnvironment};
|
||||||
use style::properties::CustomDeclarationValue;
|
use style::properties::CustomDeclarationValue;
|
||||||
use test::{self, Bencher};
|
use test::{self, Bencher};
|
||||||
|
|
||||||
|
@ -18,7 +18,8 @@ fn cascade(
|
||||||
(Name::from(name), SpecifiedValue::parse(&mut parser).unwrap())
|
(Name::from(name), SpecifiedValue::parse(&mut parser).unwrap())
|
||||||
}).collect::<Vec<_>>();
|
}).collect::<Vec<_>>();
|
||||||
|
|
||||||
let mut builder = CustomPropertiesBuilder::new(inherited);
|
let env = CssEnvironment;
|
||||||
|
let mut builder = CustomPropertiesBuilder::new(inherited, &env);
|
||||||
|
|
||||||
for &(ref name, ref val) in &values {
|
for &(ref name, ref val) in &values {
|
||||||
builder.cascade(name, &CustomDeclarationValue::Value(val.clone()));
|
builder.cascade(name, &CustomDeclarationValue::Value(val.clone()));
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue