mirror of
https://github.com/servo/servo.git
synced 2025-08-02 12:10:29 +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)",
|
||||
"canvas_traits 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)",
|
||||
"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)",
|
||||
|
@ -348,7 +348,7 @@ dependencies = [
|
|||
name = "canvas_traits"
|
||||
version = "0.0.1"
|
||||
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)",
|
||||
"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)",
|
||||
|
@ -717,7 +717,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "cssparser"
|
||||
version = "0.24.0"
|
||||
version = "0.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"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"
|
||||
dependencies = [
|
||||
"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)",
|
||||
"hashglobe 0.1.0",
|
||||
"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)",
|
||||
"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)",
|
||||
"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",
|
||||
"devtools_traits 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)",
|
||||
"atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"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)",
|
||||
"gfx_traits 0.0.1",
|
||||
"html5ever 0.22.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -3350,7 +3350,7 @@ name = "selectors"
|
|||
version = "0.20.0"
|
||||
dependencies = [
|
||||
"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)",
|
||||
"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)",
|
||||
|
@ -3785,7 +3785,7 @@ dependencies = [
|
|||
"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)",
|
||||
"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)",
|
||||
"euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fallible 0.0.1",
|
||||
|
@ -3848,7 +3848,7 @@ version = "0.0.1"
|
|||
dependencies = [
|
||||
"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)",
|
||||
"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)",
|
||||
"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)",
|
||||
|
@ -3871,7 +3871,7 @@ version = "0.0.1"
|
|||
dependencies = [
|
||||
"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)",
|
||||
"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)",
|
||||
"malloc_size_of 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-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 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 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"
|
||||
|
|
|
@ -70,6 +70,10 @@ resize
|
|||
right
|
||||
rtl
|
||||
sans-serif
|
||||
safe-area-inset-top
|
||||
safe-area-inset-bottom
|
||||
safe-area-inset-left
|
||||
safe-area-inset-right
|
||||
scan
|
||||
screen
|
||||
scroll-position
|
||||
|
|
|
@ -16,7 +16,7 @@ webgl_backtrace = ["canvas_traits/webgl_backtrace"]
|
|||
azure = {git = "https://github.com/servo/rust-azure"}
|
||||
canvas_traits = {path = "../canvas_traits"}
|
||||
compositing = {path = "../compositing"}
|
||||
cssparser = "0.24"
|
||||
cssparser = "0.25"
|
||||
euclid = "0.19"
|
||||
fnv = "1.0"
|
||||
gleam = "0.6.4"
|
||||
|
|
|
@ -13,7 +13,7 @@ path = "lib.rs"
|
|||
webgl_backtrace = []
|
||||
|
||||
[dependencies]
|
||||
cssparser = "0.24.0"
|
||||
cssparser = "0.25"
|
||||
euclid = "0.19"
|
||||
ipc-channel = "0.11"
|
||||
gleam = "0.6"
|
||||
|
|
|
@ -26,7 +26,7 @@ servo = [
|
|||
|
||||
[dependencies]
|
||||
app_units = "0.7"
|
||||
cssparser = "0.24.0"
|
||||
cssparser = "0.25"
|
||||
euclid = "0.19"
|
||||
hashglobe = { path = "../hashglobe" }
|
||||
hyper = { version = "0.12", optional = true }
|
||||
|
|
|
@ -39,7 +39,7 @@ canvas_traits = {path = "../canvas_traits"}
|
|||
caseless = "0.2"
|
||||
cookie = "0.11"
|
||||
chrono = "0.4"
|
||||
cssparser = "0.24"
|
||||
cssparser = "0.25"
|
||||
deny_public_fields = {path = "../deny_public_fields"}
|
||||
devtools_traits = {path = "../devtools_traits"}
|
||||
dom_struct = {path = "../dom_struct"}
|
||||
|
|
|
@ -13,7 +13,7 @@ path = "lib.rs"
|
|||
app_units = "0.7"
|
||||
atomic_refcell = "0.1"
|
||||
canvas_traits = {path = "../canvas_traits"}
|
||||
cssparser = "0.24"
|
||||
cssparser = "0.25"
|
||||
euclid = "0.19"
|
||||
gfx_traits = {path = "../gfx_traits"}
|
||||
html5ever = "0.22"
|
||||
|
|
|
@ -22,7 +22,7 @@ bench = []
|
|||
[dependencies]
|
||||
bitflags = "1.0"
|
||||
matches = "0.1"
|
||||
cssparser = "0.24.0"
|
||||
cssparser = "0.25"
|
||||
log = "0.4"
|
||||
fxhash = "0.2"
|
||||
phf = "0.7.18"
|
||||
|
|
|
@ -36,6 +36,11 @@ pub trait PseudoElement: Sized + ToCss {
|
|||
) -> bool {
|
||||
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.
|
||||
|
@ -69,6 +74,8 @@ pub enum SelectorParseErrorKind<'i> {
|
|||
DanglingCombinator,
|
||||
NonSimpleSelectorInNegation,
|
||||
NonCompoundSelector,
|
||||
NonPseudoElementAfterSlotted,
|
||||
InvalidPseudoElementAfterSlotted,
|
||||
UnexpectedTokenInAttributeSelector(Token<'i>),
|
||||
PseudoElementExpectedColon(Token<'i>),
|
||||
PseudoElementExpectedIdent(Token<'i>),
|
||||
|
@ -1832,7 +1839,6 @@ where
|
|||
input.skip_whitespace();
|
||||
|
||||
let mut empty = true;
|
||||
let mut slot = false;
|
||||
if !parse_type_selector(parser, input, builder)? {
|
||||
if let Some(url) = parser.default_namespace() {
|
||||
// If there was no explicit type selector, but there is a
|
||||
|
@ -1845,6 +1851,7 @@ where
|
|||
}
|
||||
|
||||
let mut pseudo = false;
|
||||
let mut slot = false;
|
||||
loop {
|
||||
let parse_result =
|
||||
match parse_one_simple_selector(parser, input, /* inside_negation = */ false)? {
|
||||
|
@ -1852,75 +1859,111 @@ where
|
|||
Some(result) => result,
|
||||
};
|
||||
|
||||
empty = false;
|
||||
|
||||
let slotted_selector;
|
||||
let pseudo_element;
|
||||
|
||||
match parse_result {
|
||||
SimpleSelectorParseResult::SimpleSelector(s) => {
|
||||
builder.push_simple_selector(s);
|
||||
empty = false
|
||||
continue;
|
||||
},
|
||||
SimpleSelectorParseResult::PseudoElement(p) => {
|
||||
// Try to parse state to its right. There are only 3 allowable
|
||||
// state selectors that can go on pseudo-elements.
|
||||
let mut state_selectors = SmallVec::<[Component<Impl>; 3]>::new();
|
||||
slotted_selector = None;
|
||||
pseudo_element = Some(p);
|
||||
}
|
||||
SimpleSelectorParseResult::SlottedPseudo(selector) => {
|
||||
slotted_selector = Some(selector);
|
||||
let maybe_pseudo = parse_one_simple_selector(
|
||||
parser,
|
||||
input,
|
||||
/* inside_negation = */ false,
|
||||
)?;
|
||||
|
||||
loop {
|
||||
let location = input.current_source_location();
|
||||
match input.next_including_whitespace() {
|
||||
Ok(&Token::Colon) => {},
|
||||
Ok(&Token::WhiteSpace(_)) | Err(_) => break,
|
||||
Ok(t) => {
|
||||
let e = SelectorParseErrorKind::PseudoElementExpectedColon(t.clone());
|
||||
return Err(location.new_custom_error(e));
|
||||
},
|
||||
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)
|
||||
}
|
||||
|
||||
let location = input.current_source_location();
|
||||
// TODO(emilio): Functional pseudo-classes too?
|
||||
// We don't need it for now.
|
||||
let name = match input.next_including_whitespace()? {
|
||||
&Token::Ident(ref name) => name.clone(),
|
||||
t => {
|
||||
return Err(location.new_custom_error(
|
||||
SelectorParseErrorKind::NoIdentForPseudo(t.clone()),
|
||||
))
|
||||
},
|
||||
};
|
||||
|
||||
let pseudo_class =
|
||||
P::parse_non_ts_pseudo_class(parser, location, name.clone())?;
|
||||
if !p.supports_pseudo_class(&pseudo_class) {
|
||||
Some(SimpleSelectorParseResult::SimpleSelector(..)) |
|
||||
Some(SimpleSelectorParseResult::SlottedPseudo(..)) => {
|
||||
return Err(input.new_custom_error(
|
||||
SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name),
|
||||
SelectorParseErrorKind::NonPseudoElementAfterSlotted
|
||||
));
|
||||
}
|
||||
state_selectors.push(Component::NonTSPseudoClass(pseudo_class));
|
||||
}
|
||||
|
||||
if !builder.is_empty() {
|
||||
builder.push_combinator(Combinator::PseudoElement);
|
||||
}
|
||||
|
||||
builder.push_simple_selector(Component::PseudoElement(p));
|
||||
for state_selector in state_selectors.drain() {
|
||||
builder.push_simple_selector(state_selector);
|
||||
}
|
||||
|
||||
pseudo = true;
|
||||
empty = false;
|
||||
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;
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
let location = input.current_source_location();
|
||||
match input.next_including_whitespace() {
|
||||
Ok(&Token::Colon) => {},
|
||||
Ok(&Token::WhiteSpace(_)) | Err(_) => break,
|
||||
Ok(t) => {
|
||||
let e = SelectorParseErrorKind::PseudoElementExpectedColon(t.clone());
|
||||
return Err(location.new_custom_error(e));
|
||||
},
|
||||
}
|
||||
|
||||
let location = input.current_source_location();
|
||||
// TODO(emilio): Functional pseudo-classes too?
|
||||
// We don't need it for now.
|
||||
let name = match input.next_including_whitespace()? {
|
||||
&Token::Ident(ref name) => name.clone(),
|
||||
t => {
|
||||
return Err(location.new_custom_error(
|
||||
SelectorParseErrorKind::NoIdentForPseudo(t.clone()),
|
||||
))
|
||||
},
|
||||
};
|
||||
|
||||
let pseudo_class =
|
||||
P::parse_non_ts_pseudo_class(parser, location, name.clone())?;
|
||||
if !p.supports_pseudo_class(&pseudo_class) {
|
||||
return Err(input.new_custom_error(
|
||||
SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name),
|
||||
));
|
||||
}
|
||||
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() {
|
||||
builder.push_combinator(Combinator::PseudoElement);
|
||||
}
|
||||
|
||||
builder.push_simple_selector(Component::PseudoElement(p));
|
||||
|
||||
for state_selector in state_selectors.drain() {
|
||||
builder.push_simple_selector(state_selector);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
if empty {
|
||||
// An empty selector is invalid.
|
||||
|
@ -2133,6 +2176,10 @@ pub mod tests {
|
|||
PseudoClass::Active | PseudoClass::Lang(..) => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn valid_after_slotted(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
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.foo)").is_ok());
|
||||
assert!(parse("slot::slotted(div,foo)::first-line").is_err());
|
||||
// TODO
|
||||
assert!(parse("::slotted(div)::before").is_err());
|
||||
assert!(parse("::slotted(div)::before").is_ok());
|
||||
assert!(parse("slot::slotted(div,foo)").is_err());
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ atomic_refcell = "0.1"
|
|||
bitflags = "1.0"
|
||||
byteorder = "1.0"
|
||||
cfg-if = "0.1.0"
|
||||
cssparser = "0.24.0"
|
||||
cssparser = "0.25"
|
||||
new_debug_unreachable = "1.0"
|
||||
encoding_rs = {version = "0.7", optional = true}
|
||||
euclid = "0.19"
|
||||
|
|
|
@ -392,6 +392,12 @@ mod bindings {
|
|||
.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("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| {
|
||||
let generic = item["generic"].as_bool().unwrap();
|
||||
let gecko = item["gecko"].as_str().unwrap();
|
||||
|
|
|
@ -38,19 +38,19 @@ derive_helper_methods = true
|
|||
[export]
|
||||
prefix = "Style"
|
||||
include = [
|
||||
"StyleAppearance",
|
||||
"StyleComputedFontStretchRange",
|
||||
"StyleComputedFontStyleDescriptor",
|
||||
"StyleComputedFontWeightRange",
|
||||
"StyleComputedTimingFunction",
|
||||
"StyleDisplay",
|
||||
"StyleDisplayMode",
|
||||
"StyleFillRule",
|
||||
"StyleFontDisplay",
|
||||
"StyleFontFaceSourceListComponent",
|
||||
"StyleFontLanguageOverride",
|
||||
"StyleTimingFunction",
|
||||
"StylePathCommand",
|
||||
"StyleUnicodeRange",
|
||||
"Appearance",
|
||||
"ComputedFontStretchRange",
|
||||
"ComputedFontStyleDescriptor",
|
||||
"ComputedFontWeightRange",
|
||||
"ComputedTimingFunction",
|
||||
"Display",
|
||||
"DisplayMode",
|
||||
"FillRule",
|
||||
"FontDisplay",
|
||||
"FontFaceSourceListComponent",
|
||||
"FontLanguageOverride",
|
||||
"TimingFunction",
|
||||
"PathCommand",
|
||||
"UnicodeRange",
|
||||
]
|
||||
item_types = ["enums", "structs", "typedefs"]
|
||||
|
|
|
@ -21,6 +21,60 @@ use std::fmt::{self, Write};
|
|||
use std::hash::Hash;
|
||||
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`.
|
||||
///
|
||||
/// Note that this does not include the `--` prefix
|
||||
|
@ -48,6 +102,12 @@ pub struct VariableValue {
|
|||
first_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.
|
||||
references: PrecomputedHashSet<Name>,
|
||||
}
|
||||
|
@ -171,18 +231,6 @@ where
|
|||
self.index.remove(index);
|
||||
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.
|
||||
|
@ -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 {
|
||||
fn empty() -> Self {
|
||||
Self {
|
||||
|
@ -223,6 +279,7 @@ impl VariableValue {
|
|||
last_token_type: TokenSerializationType::nothing(),
|
||||
first_token_type: TokenSerializationType::nothing(),
|
||||
references: PrecomputedHashSet::default(),
|
||||
references_environment: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -273,7 +330,7 @@ impl VariableValue {
|
|||
|
||||
/// Parse a custom property value.
|
||||
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) =
|
||||
parse_self_contained_declaration_value(input, Some(&mut references))?;
|
||||
|
@ -282,7 +339,8 @@ impl VariableValue {
|
|||
css: css.into_owned(),
|
||||
first_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>(
|
||||
input: &mut Parser<'i, 't>,
|
||||
references: Option<&mut PrecomputedHashSet<Name>>,
|
||||
references: Option<&mut VarOrEnvReferences>,
|
||||
) -> Result<(TokenSerializationType, Cow<'i, str>, TokenSerializationType), ParseError<'i>> {
|
||||
let start_position = input.position();
|
||||
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>
|
||||
fn parse_declaration_value<'i, 't>(
|
||||
input: &mut Parser<'i, 't>,
|
||||
references: Option<&mut PrecomputedHashSet<Name>>,
|
||||
references: Option<&mut VarOrEnvReferences>,
|
||||
missing_closing_characters: &mut String,
|
||||
) -> Result<(TokenSerializationType, TokenSerializationType), ParseError<'i>> {
|
||||
input.parse_until_before(Delimiter::Bang | Delimiter::Semicolon, |input| {
|
||||
|
@ -334,7 +392,7 @@ fn parse_declaration_value<'i, 't>(
|
|||
/// invalid at the top level
|
||||
fn parse_declaration_value_block<'i, 't>(
|
||||
input: &mut Parser<'i, 't>,
|
||||
mut references: Option<&mut PrecomputedHashSet<Name>>,
|
||||
mut references: Option<&mut VarOrEnvReferences>,
|
||||
missing_closing_characters: &mut String,
|
||||
) -> Result<(TokenSerializationType, TokenSerializationType), ParseError<'i>> {
|
||||
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))
|
||||
})?;
|
||||
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!();
|
||||
check_closed!(")");
|
||||
|
@ -468,29 +532,48 @@ fn parse_declaration_value_block<'i, 't>(
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_fallback<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(), ParseError<'i>> {
|
||||
// Exclude `!` and `;` at the top level
|
||||
// https://drafts.csswg.org/css-syntax/#typedef-declaration-value
|
||||
input.parse_until_before(Delimiter::Bang | Delimiter::Semicolon, |input| {
|
||||
// At least one non-comment token.
|
||||
input.next_including_whitespace()?;
|
||||
// Skip until the end.
|
||||
while let Ok(_) = input.next_including_whitespace_and_comments() {}
|
||||
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 PrecomputedHashSet<Name>>,
|
||||
references: Option<&mut VarOrEnvReferences>,
|
||||
) -> Result<(), ParseError<'i>> {
|
||||
let name = input.expect_ident_cloned()?;
|
||||
let name: Result<_, ParseError> = parse_name(&name).map_err(|()| {
|
||||
let name = 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
|
||||
// https://drafts.csswg.org/css-syntax/#typedef-declaration-value
|
||||
input.parse_until_before(Delimiter::Bang | Delimiter::Semicolon, |input| {
|
||||
// At least one non-comment token.
|
||||
input.next_including_whitespace()?;
|
||||
// Skip until the end.
|
||||
while let Ok(_) = input.next_including_whitespace_and_comments() {}
|
||||
Ok(())
|
||||
})?;
|
||||
parse_fallback(input)?;
|
||||
}
|
||||
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(())
|
||||
}
|
||||
|
@ -502,25 +585,26 @@ pub struct CustomPropertiesBuilder<'a> {
|
|||
may_have_cycles: bool,
|
||||
custom_properties: Option<CustomPropertiesMap>,
|
||||
inherited: Option<&'a Arc<CustomPropertiesMap>>,
|
||||
environment: &'a CssEnvironment,
|
||||
}
|
||||
|
||||
impl<'a> CustomPropertiesBuilder<'a> {
|
||||
/// 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 {
|
||||
seen: PrecomputedHashSet::default(),
|
||||
may_have_cycles: false,
|
||||
custom_properties: None,
|
||||
inherited,
|
||||
environment,
|
||||
}
|
||||
}
|
||||
|
||||
/// Cascade a given custom property declaration.
|
||||
pub fn cascade(
|
||||
&mut self,
|
||||
name: &'a Name,
|
||||
specified_value: &CustomDeclarationValue,
|
||||
) {
|
||||
pub fn cascade(&mut self, name: &'a Name, specified_value: &CustomDeclarationValue) {
|
||||
let was_already_present = !self.seen.insert(name);
|
||||
if was_already_present {
|
||||
return;
|
||||
|
@ -540,8 +624,29 @@ impl<'a> CustomPropertiesBuilder<'a> {
|
|||
let map = self.custom_properties.as_mut().unwrap();
|
||||
match *specified_value {
|
||||
CustomDeclarationValue::Value(ref unparsed_value) => {
|
||||
self.may_have_cycles |= !unparsed_value.references.is_empty();
|
||||
map.insert(name.clone(), (*unparsed_value).clone());
|
||||
let has_references = !unparsed_value.references.is_empty();
|
||||
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 {
|
||||
CSSWideKeyword::Initial => {
|
||||
|
@ -553,11 +658,7 @@ impl<'a> CustomPropertiesBuilder<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn value_may_affect_style(
|
||||
&self,
|
||||
name: &Name,
|
||||
value: &CustomDeclarationValue,
|
||||
) -> bool {
|
||||
fn value_may_affect_style(&self, name: &Name, value: &CustomDeclarationValue) -> bool {
|
||||
match *value {
|
||||
CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Unset) |
|
||||
CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Inherit) => {
|
||||
|
@ -596,8 +697,8 @@ impl<'a> CustomPropertiesBuilder<'a> {
|
|||
|
||||
/// Returns the final map of applicable custom properties.
|
||||
///
|
||||
/// If there was any specified property, we've created a new map and now we need
|
||||
/// to remove any potential cycles, and wrap it in an arc.
|
||||
/// If there was any specified property, we've created a new map and now we
|
||||
/// need to remove any potential cycles, and wrap it in an arc.
|
||||
///
|
||||
/// Otherwise, just use the inherited custom properties map.
|
||||
pub fn build(mut self) -> Option<Arc<CustomPropertiesMap>> {
|
||||
|
@ -605,9 +706,8 @@ impl<'a> CustomPropertiesBuilder<'a> {
|
|||
Some(m) => m,
|
||||
None => return self.inherited.cloned(),
|
||||
};
|
||||
|
||||
if self.may_have_cycles {
|
||||
substitute_all(&mut map);
|
||||
substitute_all(&mut map, self.environment);
|
||||
}
|
||||
Some(Arc::new(map))
|
||||
}
|
||||
|
@ -616,24 +716,12 @@ impl<'a> CustomPropertiesBuilder<'a> {
|
|||
/// Resolve all custom properties to either substituted or invalid.
|
||||
///
|
||||
/// 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
|
||||
// of Tarjan's algorithm. It is mostly based on the pseudo-code
|
||||
// listed in
|
||||
// https://en.wikipedia.org/w/index.php?
|
||||
// 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 VarInfo {
|
||||
|
@ -662,8 +750,8 @@ fn substitute_all(custom_properties_map: &mut CustomPropertiesMap) {
|
|||
/// all unfinished strong connected components.
|
||||
stack: SmallVec<[usize; 5]>,
|
||||
map: &'a mut CustomPropertiesMap,
|
||||
/// The set of invalid custom properties.
|
||||
invalid: &'a mut PrecomputedHashSet<Name>,
|
||||
/// The environment to substitute `env()` variables.
|
||||
environment: &'a CssEnvironment,
|
||||
}
|
||||
|
||||
/// 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:
|
||||
/// * It is still in context.stack, which means it is part of an
|
||||
/// potentially incomplete dependency circle.
|
||||
/// * It has been added into the invalid set. It can be either that
|
||||
/// the substitution failed, or it is inside a dependency circle.
|
||||
/// When this function put a variable into the invalid set because
|
||||
/// * It has been removed from the map. It can be either that the
|
||||
/// substitution failed, or it is inside a dependency circle.
|
||||
/// When this function removes a variable from the map because
|
||||
/// of dependency circle, it would put all variables in the same
|
||||
/// strong connected component to the set together.
|
||||
/// * 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.
|
||||
fn traverse<'a>(name: Name, context: &mut Context<'a>) -> Option<usize> {
|
||||
// Some shortcut checks.
|
||||
let (name, value) = if let Some(value) = context.map.get(&name) {
|
||||
// This variable has been resolved. Return the signal value.
|
||||
if value.references.is_empty() || context.invalid.contains(&name) {
|
||||
let (name, value) = {
|
||||
let value = context.map.get(&name)?;
|
||||
|
||||
// Nothing to resolve.
|
||||
if value.references.is_empty() {
|
||||
debug_assert!(
|
||||
!value.references_environment,
|
||||
"Should've been handled earlier"
|
||||
);
|
||||
return None;
|
||||
}
|
||||
|
||||
// Whether this variable has been visited in this traversal.
|
||||
let key;
|
||||
match context.index_map.entry(name) {
|
||||
|
@ -702,12 +797,10 @@ fn substitute_all(custom_properties_map: &mut CustomPropertiesMap) {
|
|||
entry.insert(context.count);
|
||||
},
|
||||
}
|
||||
|
||||
// Hold a strong reference to the value so that we don't
|
||||
// need to keep reference to context.map.
|
||||
(key, value.clone())
|
||||
} else {
|
||||
// The variable doesn't exist at all.
|
||||
return None;
|
||||
};
|
||||
|
||||
// Add new entry to the information table.
|
||||
|
@ -750,8 +843,9 @@ fn substitute_all(custom_properties_map: &mut CustomPropertiesMap) {
|
|||
if lowlink != index {
|
||||
// This variable is in a loop, but it is not the root of
|
||||
// this strong connected component. We simply return for
|
||||
// now, and the root would add it into the invalid set.
|
||||
// This cannot be added into the invalid set here, because
|
||||
// now, and the root would remove it from the map.
|
||||
//
|
||||
// This cannot be removed from the map here, because
|
||||
// otherwise the shortcut check at the beginning of this
|
||||
// function would return the wrong value.
|
||||
return Some(index);
|
||||
|
@ -778,44 +872,33 @@ fn substitute_all(custom_properties_map: &mut CustomPropertiesMap) {
|
|||
break;
|
||||
}
|
||||
// Anything here is in a loop which can traverse to the
|
||||
// variable we are handling, so we should add it into
|
||||
// the invalid set. We should never visit the variable
|
||||
// again so it's safe to just take the name away.
|
||||
context.invalid.insert(var_name);
|
||||
// variable we are handling, so remove it from the map, it's invalid
|
||||
// at computed-value time.
|
||||
context.map.remove(&var_name);
|
||||
in_loop = true;
|
||||
}
|
||||
if in_loop {
|
||||
// This variable is in loop. Resolve to invalid.
|
||||
context.invalid.insert(name);
|
||||
context.map.remove(&name);
|
||||
return None;
|
||||
}
|
||||
|
||||
// Now we have shown that this variable is not in a loop, and
|
||||
// all of its dependencies should have been resolved. We can
|
||||
// start substitution now.
|
||||
let mut computed_value = ComputedValue::empty();
|
||||
let mut input = ParserInput::new(&value.css);
|
||||
let mut input = Parser::new(&mut input);
|
||||
let mut position = (input.position(), value.first_token_type);
|
||||
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(())
|
||||
},
|
||||
let result = substitute_references_in_value(
|
||||
&value,
|
||||
&context.map,
|
||||
&context.environment,
|
||||
);
|
||||
if let Ok(last_token_type) = result {
|
||||
computed_value.push_from(position, &input, last_token_type);
|
||||
context.map.insert(name, Arc::new(computed_value));
|
||||
} else {
|
||||
context.invalid.insert(name);
|
||||
|
||||
match result {
|
||||
Ok(computed_value) => {
|
||||
context.map.insert(name, Arc::new(computed_value));
|
||||
},
|
||||
Err(..) => {
|
||||
context.map.remove(&name);
|
||||
},
|
||||
}
|
||||
|
||||
// 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
|
||||
// in the context we create for traversal.
|
||||
let names = custom_properties_map.index.clone();
|
||||
let mut invalid = PrecomputedHashSet::default();
|
||||
for name in names.into_iter() {
|
||||
let mut context = Context {
|
||||
count: 0,
|
||||
|
@ -833,33 +915,54 @@ fn substitute_all(custom_properties_map: &mut CustomPropertiesMap) {
|
|||
stack: SmallVec::new(),
|
||||
var_info: SmallVec::new(),
|
||||
map: custom_properties_map,
|
||||
invalid: &mut invalid,
|
||||
environment,
|
||||
};
|
||||
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.
|
||||
///
|
||||
/// The `substitute_one` callback is called for each `var()` function in `input`.
|
||||
/// If the variable has its initial value,
|
||||
/// the callback should return `Err(())` and leave `partial_computed_value` unchanged.
|
||||
/// If the variable has its initial value, 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)
|
||||
/// to `partial_computed_value` and return `Ok(last_token_type of what was pushed)`
|
||||
///
|
||||
/// Return `Err(())` if `input` is invalid at computed-value time.
|
||||
/// 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>,
|
||||
position: &mut (SourcePosition, TokenSerializationType),
|
||||
partial_computed_value: &mut ComputedValue,
|
||||
substitute_one: &mut F,
|
||||
) -> Result<TokenSerializationType, ParseError<'i>>
|
||||
where
|
||||
F: FnMut(&Name, &mut ComputedValue) -> Result<TokenSerializationType, ()>,
|
||||
{
|
||||
custom_properties: &CustomPropertiesMap,
|
||||
env: &CssEnvironment,
|
||||
) -> Result<TokenSerializationType, ParseError<'i>> {
|
||||
let mut last_token_type = TokenSerializationType::nothing();
|
||||
let mut set_position_at_next_iteration = false;
|
||||
loop {
|
||||
|
@ -883,27 +986,45 @@ where
|
|||
Err(..) => break,
|
||||
};
|
||||
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(
|
||||
input.slice(position.0..before_this_token),
|
||||
position.1,
|
||||
last_token_type,
|
||||
);
|
||||
input.parse_nested_block(|input| {
|
||||
// parse_var_function() ensures neither .unwrap() will fail.
|
||||
let name = input.expect_ident_cloned().unwrap();
|
||||
let name = Atom::from(parse_name(&name).unwrap());
|
||||
// parse_var_function() / parse_env_function() ensure neither .unwrap() will fail.
|
||||
let name = {
|
||||
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) {
|
||||
last_token_type = last;
|
||||
let value = if is_env {
|
||||
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`
|
||||
// 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.
|
||||
while let Ok(_) = input.next() {}
|
||||
while input.next().is_ok() {}
|
||||
} else {
|
||||
input.expect_comma()?;
|
||||
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.
|
||||
.unwrap()
|
||||
.serialization_type();
|
||||
|
@ -913,23 +1034,29 @@ where
|
|||
input,
|
||||
&mut position,
|
||||
partial_computed_value,
|
||||
substitute_one,
|
||||
custom_properties,
|
||||
env,
|
||||
)?;
|
||||
partial_computed_value.push_from(position, input, last_token_type);
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
set_position_at_next_iteration = true
|
||||
},
|
||||
|
||||
}
|
||||
Token::Function(_) |
|
||||
Token::ParenthesisBlock |
|
||||
Token::CurlyBracketBlock |
|
||||
Token::SquareBracketBlock => {
|
||||
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();
|
||||
},
|
||||
|
||||
|
@ -945,29 +1072,30 @@ where
|
|||
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.
|
||||
pub fn substitute<'i>(
|
||||
input: &'i str,
|
||||
first_token_type: TokenSerializationType,
|
||||
computed_values_map: Option<&Arc<CustomPropertiesMap>>,
|
||||
env: &CssEnvironment,
|
||||
) -> Result<String, ParseError<'i>> {
|
||||
let mut substituted = ComputedValue::empty();
|
||||
let mut input = ParserInput::new(input);
|
||||
let mut input = Parser::new(&mut input);
|
||||
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(
|
||||
&mut input,
|
||||
&mut position,
|
||||
&mut substituted,
|
||||
&mut |name, substituted| {
|
||||
if let Some(value) = computed_values_map.and_then(|map| map.get(name)) {
|
||||
substituted.push_variable(value);
|
||||
Ok(value.last_token_type)
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
},
|
||||
&custom_properties,
|
||||
env,
|
||||
)?;
|
||||
substituted.push_from(position, &input, last_token_type);
|
||||
Ok(substituted.css)
|
||||
|
|
|
@ -13,7 +13,7 @@ use invalidation::element::invalidator::{InvalidationProcessor, InvalidationVect
|
|||
use selectors::{Element, NthIndexCache, SelectorList};
|
||||
use selectors::attr::CaseSensitivity;
|
||||
use selectors::matching::{self, MatchingContext, MatchingMode};
|
||||
use selectors::parser::{Combinator, Component, LocalName};
|
||||
use selectors::parser::{Combinator, Component, LocalName, SelectorImpl};
|
||||
use smallvec::SmallVec;
|
||||
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.
|
||||
fn query_selector_single_query<E, Q>(
|
||||
root: E::ConcreteNode,
|
||||
|
@ -357,16 +370,11 @@ where
|
|||
element.has_class(class, case_sensitivity)
|
||||
})
|
||||
},
|
||||
Component::LocalName(LocalName {
|
||||
ref name,
|
||||
ref lower_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()
|
||||
}
|
||||
}),
|
||||
Component::LocalName(ref local_name) => {
|
||||
collect_all_elements::<E, Q, _>(root, results, |element| {
|
||||
local_name_matches(element, local_name)
|
||||
})
|
||||
},
|
||||
// TODO(emilio): More fast paths?
|
||||
_ => return Err(()),
|
||||
}
|
||||
|
@ -374,8 +382,18 @@ where
|
|||
Ok(())
|
||||
}
|
||||
|
||||
enum SimpleFilter<'a, Impl: SelectorImpl> {
|
||||
Class(&'a Atom),
|
||||
LocalName(&'a LocalName<Impl>),
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// replaced by HolyJit :)
|
||||
fn query_selector_fast<E, Q>(
|
||||
|
@ -410,7 +428,12 @@ where
|
|||
let mut iter = selector.iter();
|
||||
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()));
|
||||
|
||||
'component_loop: for component in &mut iter {
|
||||
|
@ -469,13 +492,28 @@ where
|
|||
|
||||
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 {
|
||||
let next_combinator = match iter.next_sequence() {
|
||||
None => return Err(()),
|
||||
None => break 'selector_loop,
|
||||
Some(c) => c,
|
||||
};
|
||||
|
||||
|
@ -492,6 +530,39 @@ where
|
|||
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.
|
||||
|
|
|
@ -151,10 +151,20 @@ impl_range!(FontWeightRange, AbsoluteFontWeight);
|
|||
#[allow(missing_docs)]
|
||||
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 {
|
||||
/// Returns a computed font-stretch range.
|
||||
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::Italic => ComputedFontStyleDescriptor::Italic,
|
||||
FontStyle::Oblique(ref first, ref second) => {
|
||||
ComputedFontStyleDescriptor::Oblique(
|
||||
let (min, max) = sort_range(
|
||||
SpecifiedFontStyle::compute_angle_degrees(first),
|
||||
SpecifiedFontStyle::compute_angle_degrees(second),
|
||||
)
|
||||
);
|
||||
ComputedFontStyleDescriptor::Oblique(min, max)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -649,7 +649,7 @@ pub mod basic_shape {
|
|||
|
||||
use gecko::values::GeckoStyleCoordConvertible;
|
||||
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::{nsStyleCoord, nsStyleCorners};
|
||||
use gecko_bindings::sugar::ns_style_coord::{CoordDataMut, CoordDataValue};
|
||||
|
@ -662,7 +662,7 @@ pub mod basic_shape {
|
|||
use values::computed::position;
|
||||
use values::computed::url::ComputedUrl;
|
||||
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::border::BorderRadius as GenericBorderRadius;
|
||||
use values::generics::rect::Rect;
|
||||
|
@ -693,12 +693,7 @@ pub mod basic_shape {
|
|||
StyleShapeSourceType::URL | StyleShapeSourceType::Image => None,
|
||||
StyleShapeSourceType::Path => {
|
||||
let path = self.to_svg_path().expect("expect an SVGPathData");
|
||||
let gecko_path = unsafe { &*self.__bindgen_anon_1.mSVGPath.as_ref().mPtr };
|
||||
let fill = if gecko_path.mFillRule == StyleFillRule::Evenodd {
|
||||
FillRule::Evenodd
|
||||
} else {
|
||||
FillRule::Nonzero
|
||||
};
|
||||
let fill = unsafe { &*self.__bindgen_anon_1.mSVGPath.as_ref().mPtr }.mFillRule;
|
||||
Some(ShapeSource::Path(Path { fill, path }))
|
||||
},
|
||||
}
|
||||
|
@ -706,18 +701,11 @@ pub mod basic_shape {
|
|||
|
||||
/// Generate a SVGPathData from StyleShapeSource if possible.
|
||||
fn to_svg_path(&self) -> Option<SVGPathData> {
|
||||
use gecko_bindings::structs::StylePathCommand;
|
||||
use values::specified::svg_path::PathCommand;
|
||||
match self.mType {
|
||||
StyleShapeSourceType::Path => {
|
||||
let gecko_path = unsafe { &*self.__bindgen_anon_1.mSVGPath.as_ref().mPtr };
|
||||
let result: Vec<PathCommand> = gecko_path
|
||||
.mPath
|
||||
.iter()
|
||||
.map(|gecko: &StylePathCommand| {
|
||||
// unsafe: cbindgen ensures the representation is the same.
|
||||
unsafe { ::std::mem::transmute(*gecko) }
|
||||
}).collect();
|
||||
let result: Vec<PathCommand> = gecko_path.mPath.iter().cloned().collect();
|
||||
Some(SVGPathData::new(result.into_boxed_slice()))
|
||||
},
|
||||
_ => None,
|
||||
|
@ -730,7 +718,7 @@ pub mod basic_shape {
|
|||
match other.mType {
|
||||
StyleShapeSourceType::URL => unsafe {
|
||||
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);
|
||||
ShapeSource::ImageOrUrl(url)
|
||||
},
|
||||
|
@ -805,11 +793,6 @@ pub mod basic_shape {
|
|||
position: (&other.mPosition).into(),
|
||||
}),
|
||||
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);
|
||||
for i in 0..(other.mCoordinates.len() / 2) {
|
||||
let x = 2 * i;
|
||||
|
@ -828,7 +811,7 @@ pub mod basic_shape {
|
|||
))
|
||||
}
|
||||
GenericBasicShape::Polygon(Polygon {
|
||||
fill: fill_rule,
|
||||
fill: other.mFillRule,
|
||||
coordinates: coords,
|
||||
})
|
||||
},
|
||||
|
|
|
@ -179,7 +179,7 @@ fn eval_device_orientation(device: &Device, value: Option<Orientation>) -> bool
|
|||
}
|
||||
|
||||
/// Values for the display-mode media feature.
|
||||
#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
|
||||
#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss)]
|
||||
#[repr(u8)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum DisplayMode {
|
||||
|
@ -191,16 +191,10 @@ pub enum DisplayMode {
|
|||
|
||||
/// https://w3c.github.io/manifest/#the-display-mode-media-feature
|
||||
fn eval_display_mode(device: &Device, query_value: Option<DisplayMode>) -> bool {
|
||||
let query_value = match query_value {
|
||||
Some(v) => v,
|
||||
None => return 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
|
||||
match query_value {
|
||||
Some(v) => v == unsafe { bindings::Gecko_MediaFeatures_GetDisplayMode(device.document()) },
|
||||
None => true,
|
||||
}
|
||||
}
|
||||
|
||||
/// https://drafts.csswg.org/mediaqueries-4/#grid
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
use app_units::AU_PER_PX;
|
||||
use app_units::Au;
|
||||
use cssparser::RGBA;
|
||||
use custom_properties::CssEnvironment;
|
||||
use euclid::Size2D;
|
||||
use euclid::TypedScale;
|
||||
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
|
||||
/// by using vw/vh/vmin/vmax units.
|
||||
used_viewport_size: AtomicBool,
|
||||
/// The CssEnvironment object responsible of getting CSS environment
|
||||
/// variables.
|
||||
environment: CssEnvironment,
|
||||
}
|
||||
|
||||
impl fmt::Debug for Device {
|
||||
|
@ -87,9 +91,16 @@ impl Device {
|
|||
body_text_color: AtomicUsize::new(unsafe { &*pres_context }.mDefaultColor as usize),
|
||||
used_root_font_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
|
||||
/// relevant viewport constraints.
|
||||
pub fn account_for_viewport_rule(&mut self, _constraints: &ViewportConstraints) {
|
||||
|
|
|
@ -27,6 +27,14 @@ include!(concat!(
|
|||
impl ::selectors::parser::PseudoElement for PseudoElement {
|
||||
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 {
|
||||
if !self.supports_user_action_state() {
|
||||
return false;
|
||||
|
|
|
@ -243,7 +243,10 @@ where
|
|||
|
||||
let mut declarations = SmallVec::<[(&_, CascadeLevel); 32]>::new();
|
||||
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() {
|
||||
declarations.push((declaration, cascade_level));
|
||||
|
@ -420,6 +423,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
|||
declaration.id,
|
||||
self.context.builder.custom_properties.as_ref(),
|
||||
self.context.quirks_mode,
|
||||
self.context.device().environment(),
|
||||
))
|
||||
}
|
||||
|
||||
|
|
|
@ -298,6 +298,7 @@ class Longhand(object):
|
|||
"ColumnCount",
|
||||
"Contain",
|
||||
"Display",
|
||||
"FillRule",
|
||||
"Float",
|
||||
"FontSizeAdjust",
|
||||
"FontStretch",
|
||||
|
@ -431,6 +432,8 @@ class Alias(object):
|
|||
self.original = original
|
||||
self.enabled_in = original.enabled_in
|
||||
self.servo_pref = original.servo_pref
|
||||
self.animatable = original.animatable
|
||||
self.transitionable = original.transitionable
|
||||
self.gecko_pref = gecko_pref
|
||||
self.allowed_in_page_rule = original.allowed_in_page_rule
|
||||
self.allowed_in_keyframe_block = original.allowed_in_keyframe_block
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
use context::QuirksMode;
|
||||
use cssparser::{DeclarationListParser, parse_important, ParserInput, CowRcStr};
|
||||
use cssparser::{Parser, AtRuleParser, DeclarationParser, Delimiter, ParseErrorKind};
|
||||
use custom_properties::CustomPropertiesBuilder;
|
||||
use custom_properties::{CustomPropertiesBuilder, CssEnvironment};
|
||||
use error_reporting::{ParseErrorReporter, ContextualParseError};
|
||||
use itertools::Itertools;
|
||||
use parser::ParserContext;
|
||||
|
@ -760,13 +760,19 @@ impl PropertyDeclarationBlock {
|
|||
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 {
|
||||
// If there are extra custom properties for this declaration block,
|
||||
// factor them in too.
|
||||
if let Some(block) = custom_properties_block {
|
||||
// FIXME(emilio): This is not super-efficient here, and all this
|
||||
// feels like a hack anyway...
|
||||
block.cascade_custom_properties(cv.custom_properties())
|
||||
block.cascade_custom_properties(cv.custom_properties(), &env)
|
||||
} else {
|
||||
cv.custom_properties().cloned()
|
||||
}
|
||||
|
@ -790,6 +796,7 @@ impl PropertyDeclarationBlock {
|
|||
declaration.id,
|
||||
custom_properties.as_ref(),
|
||||
QuirksMode::NoQuirks,
|
||||
&env,
|
||||
).to_css(dest)
|
||||
},
|
||||
(ref d, _) => d.to_css(dest),
|
||||
|
@ -835,17 +842,24 @@ impl PropertyDeclarationBlock {
|
|||
&self,
|
||||
context: &Context,
|
||||
) -> 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
|
||||
/// properties in this declaration block along with the given custom
|
||||
/// properties.
|
||||
pub fn cascade_custom_properties(
|
||||
fn cascade_custom_properties(
|
||||
&self,
|
||||
inherited_custom_properties: Option<&Arc<::custom_properties::CustomPropertiesMap>>,
|
||||
environment: &CssEnvironment,
|
||||
) -> 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() {
|
||||
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)
|
||||
%>
|
||||
|
||||
<%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)">
|
||||
#[allow(non_snake_case)]
|
||||
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;
|
||||
}
|
||||
let length = match self.gecko.${gecko_ffi_name}.as_value() {
|
||||
CoordDataValue::Factor(number) =>
|
||||
SvgLengthOrPercentageOrNumber::Number(number),
|
||||
CoordDataValue::Coord(coord) =>
|
||||
CoordDataValue::Factor(number) => {
|
||||
SvgLengthOrPercentageOrNumber::Number(number)
|
||||
},
|
||||
CoordDataValue::Coord(coord) => {
|
||||
SvgLengthOrPercentageOrNumber::LengthOrPercentage(
|
||||
LengthOrPercentage::Length(Au(coord).into())),
|
||||
CoordDataValue::Percent(p) =>
|
||||
LengthOrPercentage::Length(Au(coord).into())
|
||||
)
|
||||
},
|
||||
CoordDataValue::Percent(p) => {
|
||||
SvgLengthOrPercentageOrNumber::LengthOrPercentage(
|
||||
LengthOrPercentage::Percentage(Percentage(p))),
|
||||
CoordDataValue::Calc(calc) =>
|
||||
LengthOrPercentage::Percentage(Percentage(p))
|
||||
)
|
||||
},
|
||||
CoordDataValue::Calc(calc) => {
|
||||
SvgLengthOrPercentageOrNumber::LengthOrPercentage(
|
||||
LengthOrPercentage::Calc(calc.into())),
|
||||
LengthOrPercentage::Calc(calc.into())
|
||||
)
|
||||
},
|
||||
_ => unreachable!("Unexpected coordinate in ${ident}"),
|
||||
};
|
||||
SVGLength::Length(length.into())
|
||||
|
@ -1425,6 +1404,7 @@ impl Clone for ${style_struct.gecko_struct_name} {
|
|||
"length::LengthOrNormal": impl_style_coord,
|
||||
"length::NonNegativeLengthOrAuto": impl_style_coord,
|
||||
"length::NonNegativeLengthOrPercentageOrNormal": impl_style_coord,
|
||||
"FillRule": impl_simple,
|
||||
"FlexBasis": impl_style_coord,
|
||||
"Length": impl_absolute_length,
|
||||
"LengthOrNormal": impl_style_coord,
|
||||
|
@ -3059,9 +3039,8 @@ fn static_assert() {
|
|||
<%self:impl_trait style_struct_name="Box" skip_longhands="${skip_box_longhands}">
|
||||
#[inline]
|
||||
pub fn set_display(&mut self, v: longhands::display::computed_value::T) {
|
||||
// unsafe: cbindgen ensures the representation is the same.
|
||||
self.gecko.mDisplay = unsafe { transmute(v) };
|
||||
self.gecko.mOriginalDisplay = unsafe { transmute(v) };
|
||||
self.gecko.mDisplay = v;
|
||||
self.gecko.mOriginalDisplay = v;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -3081,17 +3060,15 @@ fn static_assert() {
|
|||
v: longhands::display::computed_value::T,
|
||||
_is_item_or_root: bool
|
||||
) {
|
||||
// unsafe: cbindgen ensures the representation is the same.
|
||||
self.gecko.mDisplay = unsafe { transmute(v) };
|
||||
self.gecko.mDisplay = v;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clone_display(&self) -> longhands::display::computed_value::T {
|
||||
// unsafe: cbindgen ensures the representation is the same.
|
||||
unsafe { transmute(self.gecko.mDisplay) }
|
||||
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") %>
|
||||
${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()
|
||||
};
|
||||
|
||||
let iter = servo_path.commands().iter().map(|command| {
|
||||
// unsafe: cbindgen ensures the representation is the same.
|
||||
unsafe { transmute(*command) }
|
||||
});
|
||||
gecko_path.mPath.assign_from_iter_pod(iter);
|
||||
gecko_path.mPath.assign_from_iter_pod(servo_path.commands().iter().cloned());
|
||||
|
||||
// Setup fill-rule.
|
||||
// unsafe: cbindgen ensures the representation is the same.
|
||||
gecko_path.mFillRule = unsafe { transmute(fill) };
|
||||
gecko_path.mFillRule = fill;
|
||||
}
|
||||
|
||||
<%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.1.to_gecko_style_coord(&mut shape.mCoordinates[2 * i + 1]);
|
||||
}
|
||||
// unsafe: cbindgen ensures the representation is the same.
|
||||
shape.mFillRule = unsafe { transmute(poly.fill) };
|
||||
shape.mFillRule = poly.fill;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,10 +17,8 @@ use itertools::{EitherOrBoth, Itertools};
|
|||
use num_traits::Zero;
|
||||
use properties::{CSSWideKeyword, PropertyDeclaration};
|
||||
use properties::longhands;
|
||||
use properties::longhands::font_weight::computed_value::T as FontWeight;
|
||||
use properties::longhands::visibility::computed_value::T as Visibility;
|
||||
use properties::PropertyId;
|
||||
use properties::{LonghandId, ShorthandId};
|
||||
use properties::LonghandId;
|
||||
use servo_arc::Arc;
|
||||
use smallvec::SmallVec;
|
||||
use std::{cmp, ptr};
|
||||
|
@ -29,15 +27,12 @@ use hash::FxHashMap;
|
|||
use super::ComputedValues;
|
||||
use values::CSSFloat;
|
||||
use values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero};
|
||||
use values::animated::color::Color as AnimatedColor;
|
||||
use values::animated::effects::Filter as AnimatedFilter;
|
||||
#[cfg(feature = "gecko")] use values::computed::TransitionProperty;
|
||||
use values::computed::{Angle, CalcLengthOrPercentage};
|
||||
use values::computed::Angle;
|
||||
use values::computed::{ClipRect, Context};
|
||||
use values::computed::{Length, LengthOrPercentage, LengthOrPercentageOrAuto};
|
||||
use values::computed::LengthOrPercentageOrNone;
|
||||
use values::computed::{NonNegativeNumber, Number, NumberOrPercentage, Percentage};
|
||||
use values::computed::length::NonNegativeLengthOrPercentage;
|
||||
use values::computed::{Length, LengthOrPercentage};
|
||||
use values::computed::{Number, Percentage};
|
||||
use values::computed::ToComputedValue;
|
||||
use values::computed::transform::{DirectionVector, Matrix, Matrix3D};
|
||||
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::Translate as ComputedTranslate;
|
||||
use values::computed::transform::Scale as ComputedScale;
|
||||
use values::computed::url::ComputedUrl;
|
||||
use values::generics::transform::{self, Rotate, Translate, Scale, Transform, TransformOperation};
|
||||
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::svg::{SVGLength, SvgLengthOrPercentageOrNumber, SVGPaint};
|
||||
use values::generics::svg::{SVGPaintKind, SVGStrokeDashArray, SVGOpacity};
|
||||
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
|
||||
#[cfg(feature = "gecko")]
|
||||
#[allow(non_upper_case_globals)]
|
||||
impl From<nsCSSPropertyID> for TransitionProperty {
|
||||
fn from(property: nsCSSPropertyID) -> TransitionProperty {
|
||||
use properties::ShorthandId;
|
||||
match property {
|
||||
% for prop in data.longhands:
|
||||
${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
|
||||
/// property.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
|
@ -556,6 +520,7 @@ impl AnimationValue {
|
|||
declaration.id,
|
||||
custom_properties,
|
||||
context.quirks_mode,
|
||||
context.device().environment(),
|
||||
)
|
||||
};
|
||||
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>
|
||||
impl Animate for ClipRect {
|
||||
#[inline]
|
||||
|
@ -2351,10 +2125,21 @@ impl Animate for ComputedRotate {
|
|||
let from = ComputedRotate::resolve(self);
|
||||
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);
|
||||
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);
|
||||
|
||||
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) {
|
||||
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',
|
||||
'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",
|
||||
flags="APPLIES_TO_FIRST_LETTER",
|
||||
boxed=product == "servo",
|
||||
ignored_when_colors_disabled=True
|
||||
)}
|
||||
|
||||
${helpers.predefined_type(
|
||||
|
|
|
@ -59,16 +59,14 @@ ${helpers.single_keyword(
|
|||
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`
|
||||
// And, firefox doesn't support `pixelated` yet (https://bugzilla.mozilla.org/show_bug.cgi?id=856337)
|
||||
${helpers.single_keyword(
|
||||
"image-rendering",
|
||||
"auto",
|
||||
extra_gecko_values="optimizespeed optimizequality -moz-crisp-edges",
|
||||
extra_servo_values="pixelated crisp-edges",
|
||||
custom_consts=image_rendering_custom_consts,
|
||||
"auto crisp-edges",
|
||||
extra_gecko_values="optimizespeed optimizequality",
|
||||
extra_servo_values="pixelated",
|
||||
extra_gecko_aliases="-moz-crisp-edges=crisp-edges",
|
||||
animation_value_type="discrete",
|
||||
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",
|
||||
)}
|
||||
|
||||
${helpers.single_keyword(
|
||||
${helpers.predefined_type(
|
||||
"fill-rule",
|
||||
"nonzero evenodd",
|
||||
gecko_enum_prefix="StyleFillRule",
|
||||
"FillRule",
|
||||
"Default::default()",
|
||||
needs_context=False,
|
||||
products="gecko",
|
||||
animation_value_type="discrete",
|
||||
spec="https://www.w3.org/TR/SVG11/painting.html#FillRuleProperty",
|
||||
|
@ -84,7 +85,7 @@ ${helpers.predefined_type(
|
|||
|
||||
${helpers.predefined_type(
|
||||
"stroke-width", "SVGWidth",
|
||||
"::values::computed::NonNegativeLength::new(1.).into()",
|
||||
"computed::SVGWidth::one()",
|
||||
products="gecko",
|
||||
animation_value_type="::values::computed::SVGWidth",
|
||||
spec="https://www.w3.org/TR/SVG2/painting.html#StrokeWidth",
|
||||
|
@ -134,19 +135,21 @@ ${helpers.predefined_type(
|
|||
)}
|
||||
|
||||
${helpers.predefined_type(
|
||||
"stroke-dashoffset", "SVGLength",
|
||||
"Au(0).into()",
|
||||
"stroke-dashoffset",
|
||||
"SVGLength",
|
||||
"computed::SVGLength::zero()",
|
||||
products="gecko",
|
||||
animation_value_type="ComputedValue",
|
||||
spec="https://www.w3.org/TR/SVG2/painting.html#StrokeDashing",
|
||||
)}
|
||||
|
||||
// Section 14 - Clipping, Masking and Compositing
|
||||
${helpers.single_keyword(
|
||||
${helpers.predefined_type(
|
||||
"clip-rule",
|
||||
"nonzero evenodd",
|
||||
"FillRule",
|
||||
"Default::default()",
|
||||
needs_context=False,
|
||||
products="gecko",
|
||||
gecko_enum_prefix="StyleFillRule",
|
||||
animation_value_type="discrete",
|
||||
spec="https://www.w3.org/TR/SVG11/masking.html#ClipRuleProperty",
|
||||
)}
|
||||
|
|
|
@ -471,6 +471,20 @@ impl NonCustomPropertyId {
|
|||
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]
|
||||
fn enabled_for_all_content(self) -> bool {
|
||||
${static_non_custom_property_id_set(
|
||||
|
@ -758,6 +772,41 @@ impl<'a> Iterator for LonghandIdSetIterator<'a> {
|
|||
}
|
||||
|
||||
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.
|
||||
pub fn iter(&self) -> LonghandIdSetIterator {
|
||||
LonghandIdSetIterator { longhands: self, cur: 0, }
|
||||
|
@ -784,6 +833,14 @@ impl LonghandIdSet {
|
|||
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
|
||||
#[inline]
|
||||
pub fn new() -> LonghandIdSet {
|
||||
|
@ -800,8 +857,7 @@ impl LonghandIdSet {
|
|||
/// Return whether this set contains any reset longhand.
|
||||
#[inline]
|
||||
pub fn contains_any_reset(&self) -> bool {
|
||||
${static_longhand_id_set("RESET", lambda p: not p.style_struct.inherited)}
|
||||
self.contains_any(&RESET)
|
||||
self.contains_any(Self::reset())
|
||||
}
|
||||
|
||||
/// Add the given property to the set
|
||||
|
@ -961,9 +1017,8 @@ impl LonghandId {
|
|||
|
||||
/// Returns whether the longhand property is inherited by default.
|
||||
#[inline]
|
||||
pub fn inherited(&self) -> bool {
|
||||
${static_longhand_id_set("INHERITED", lambda p: p.style_struct.inherited)}
|
||||
INHERITED.contains(*self)
|
||||
pub fn inherited(self) -> bool {
|
||||
!LonghandIdSet::reset().contains(self)
|
||||
}
|
||||
|
||||
fn shorthands(&self) -> NonCustomPropertyIterator<ShorthandId> {
|
||||
|
@ -1034,15 +1089,13 @@ impl LonghandId {
|
|||
/// Returns whether this property is animatable.
|
||||
#[inline]
|
||||
pub fn is_animatable(self) -> bool {
|
||||
${static_longhand_id_set("ANIMATABLE", lambda p: p.animatable)}
|
||||
ANIMATABLE.contains(self)
|
||||
LonghandIdSet::animatable().contains(self)
|
||||
}
|
||||
|
||||
/// Returns whether this property is animatable in a discrete way.
|
||||
#[inline]
|
||||
pub fn is_discrete_animatable(self) -> bool {
|
||||
${static_longhand_id_set("DISCRETE_ANIMATABLE", lambda p: p.animation_value_type == "discrete")}
|
||||
DISCRETE_ANIMATABLE.contains(self)
|
||||
LonghandIdSet::discrete_animatable().contains(self)
|
||||
}
|
||||
|
||||
/// Converts from a LonghandId to an adequate nsCSSPropertyID.
|
||||
|
@ -1065,9 +1118,8 @@ impl LonghandId {
|
|||
|
||||
/// Return whether this property is logical.
|
||||
#[inline]
|
||||
pub fn is_logical(&self) -> bool {
|
||||
${static_longhand_id_set("LOGICAL", lambda p: p.logical)}
|
||||
LOGICAL.contains(*self)
|
||||
pub fn is_logical(self) -> bool {
|
||||
LonghandIdSet::logical().contains(self)
|
||||
}
|
||||
|
||||
/// If this is a logical property, return the corresponding physical one in
|
||||
|
@ -1157,12 +1209,7 @@ impl LonghandId {
|
|||
/// colors are disabled.
|
||||
#[inline]
|
||||
fn ignored_when_document_colors_disabled(self) -> bool {
|
||||
${static_longhand_id_set(
|
||||
"IGNORED_WHEN_COLORS_DISABLED",
|
||||
lambda p: p.ignored_when_colors_disabled
|
||||
)}
|
||||
|
||||
IGNORED_WHEN_COLORS_DISABLED.contains(self)
|
||||
LonghandIdSet::ignored_when_colors_disabled().contains(self)
|
||||
}
|
||||
|
||||
/// 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>(
|
||||
&self,
|
||||
declarations: &mut SourcePropertyDeclaration,
|
||||
|
@ -1470,10 +1536,14 @@ impl UnparsedValue {
|
|||
longhand_id: LonghandId,
|
||||
custom_properties: Option<<&Arc<::custom_properties::CustomPropertiesMap>>,
|
||||
quirks_mode: QuirksMode,
|
||||
environment: &::custom_properties::CssEnvironment,
|
||||
) -> PropertyDeclaration {
|
||||
::custom_properties::substitute(&self.css, self.first_token_type, custom_properties)
|
||||
.ok()
|
||||
.and_then(|css| {
|
||||
::custom_properties::substitute(
|
||||
&self.css,
|
||||
self.first_token_type,
|
||||
custom_properties,
|
||||
environment,
|
||||
).ok().and_then(|css| {
|
||||
// As of this writing, only the base URL is used for property
|
||||
// values.
|
||||
//
|
||||
|
@ -2148,34 +2218,33 @@ impl PropertyDeclaration {
|
|||
WideKeywordDeclaration { id, keyword },
|
||||
)
|
||||
}).or_else(|()| {
|
||||
input.look_for_var_functions();
|
||||
input.look_for_var_or_env_functions();
|
||||
input.parse_entirely(|input| id.parse_value(context, input))
|
||||
.or_else(|err| {
|
||||
while let Ok(_) = input.next() {} // Look for var() after the error.
|
||||
if input.seen_var_functions() {
|
||||
input.reset(&start);
|
||||
let (first_token_type, css) =
|
||||
::custom_properties::parse_non_custom_with_var(input).map_err(|e| {
|
||||
StyleParseErrorKind::new_invalid(
|
||||
non_custom_id.unwrap().name(),
|
||||
e,
|
||||
)
|
||||
})?;
|
||||
Ok(PropertyDeclaration::WithVariables(VariableDeclaration {
|
||||
id,
|
||||
value: Arc::new(UnparsedValue {
|
||||
if !input.seen_var_or_env_functions() {
|
||||
return Err(StyleParseErrorKind::new_invalid(
|
||||
non_custom_id.unwrap().name(),
|
||||
err,
|
||||
));
|
||||
}
|
||||
input.reset(&start);
|
||||
let (first_token_type, css) =
|
||||
::custom_properties::parse_non_custom_with_var(input).map_err(|e| {
|
||||
StyleParseErrorKind::new_invalid(
|
||||
non_custom_id.unwrap().name(),
|
||||
e,
|
||||
)
|
||||
})?;
|
||||
Ok(PropertyDeclaration::WithVariables(VariableDeclaration {
|
||||
id,
|
||||
value: Arc::new(UnparsedValue {
|
||||
css: css.into_owned(),
|
||||
first_token_type: first_token_type,
|
||||
url_data: context.url_data.clone(),
|
||||
from_shorthand: None,
|
||||
}),
|
||||
}))
|
||||
} else {
|
||||
Err(StyleParseErrorKind::new_invalid(
|
||||
non_custom_id.unwrap().name(),
|
||||
err,
|
||||
))
|
||||
}
|
||||
}),
|
||||
}))
|
||||
})
|
||||
}).map(|declaration| {
|
||||
declarations.push(declaration)
|
||||
|
@ -2198,12 +2267,13 @@ impl PropertyDeclaration {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
input.look_for_var_functions();
|
||||
// Not using parse_entirely here: each ${shorthand.ident}::parse_into function
|
||||
// needs to do so *before* pushing to `declarations`.
|
||||
input.look_for_var_or_env_functions();
|
||||
// Not using parse_entirely here: each
|
||||
// ${shorthand.ident}::parse_into function needs to do so
|
||||
// *before* pushing to `declarations`.
|
||||
id.parse_into(declarations, context, input).or_else(|err| {
|
||||
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,
|
||||
|
|
|
@ -1288,24 +1288,12 @@ impl StrongRuleNode {
|
|||
}
|
||||
}
|
||||
|
||||
// If author colors are not allowed, only claim to have author-specified
|
||||
// rules if we're looking at a non-color property or if we're looking at
|
||||
// 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 are not allowed, don't look at those properties
|
||||
// (except for background-color which is special and we handle below).
|
||||
if !author_colors_allowed {
|
||||
for id in IGNORED_WHEN_COLORS_DISABLED {
|
||||
properties.remove(*id);
|
||||
properties.remove_all(LonghandIdSet::ignored_when_colors_disabled());
|
||||
if rule_type_mask & NS_AUTHOR_SPECIFIED_BACKGROUND != 0 {
|
||||
properties.insert(LonghandId::BackgroundColor);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
use app_units::Au;
|
||||
use cssparser::RGBA;
|
||||
use custom_properties::CssEnvironment;
|
||||
use euclid::{Size2D, TypedScale, TypedSize2D};
|
||||
use media_queries::MediaType;
|
||||
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.
|
||||
#[ignore_malloc_size_of = "Pure stack type"]
|
||||
used_viewport_units: AtomicBool,
|
||||
/// The CssEnvironment object responsible of getting CSS environment
|
||||
/// variables.
|
||||
environment: CssEnvironment,
|
||||
}
|
||||
|
||||
impl Device {
|
||||
|
@ -66,9 +70,16 @@ impl Device {
|
|||
root_font_size: AtomicIsize::new(FontSize::medium().size().0 as isize),
|
||||
used_root_font_size: 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.
|
||||
pub fn default_computed_values(&self) -> &ComputedValues {
|
||||
// FIXME(bz): This isn't really right, but it's no more wrong
|
||||
|
|
|
@ -605,11 +605,11 @@ impl Stylist {
|
|||
maybe = maybe || f(&*data);
|
||||
});
|
||||
|
||||
if maybe || !doc_author_rules_apply {
|
||||
return maybe;
|
||||
if maybe || f(&self.cascade_data.user) {
|
||||
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
|
||||
|
@ -1491,7 +1491,33 @@ impl Stylist {
|
|||
// the lookups, which means that the bitvecs are comparable. We verify
|
||||
// this in the caller by asserting that the bitvecs are same-length.
|
||||
let mut results = SmallBitVec::new();
|
||||
for (data, _) in self.cascade_data.iter_origins() {
|
||||
|
||||
let matches_document_rules =
|
||||
element.each_applicable_non_document_style_rule_data(|data, quirks_mode, host| {
|
||||
matching_context.with_shadow_host(host, |matching_context| {
|
||||
data.selectors_for_cache_revalidation.lookup(
|
||||
element,
|
||||
quirks_mode,
|
||||
|selector_and_hashes| {
|
||||
results.push(matches_selector(
|
||||
&selector_and_hashes.selector,
|
||||
selector_and_hashes.selector_offset,
|
||||
Some(&selector_and_hashes.hashes),
|
||||
&element,
|
||||
matching_context,
|
||||
flags_setter,
|
||||
));
|
||||
true
|
||||
},
|
||||
);
|
||||
})
|
||||
});
|
||||
|
||||
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,
|
||||
|
@ -1509,25 +1535,6 @@ impl Stylist {
|
|||
);
|
||||
}
|
||||
|
||||
element.each_applicable_non_document_style_rule_data(|data, quirks_mode, host| {
|
||||
matching_context.with_shadow_host(host, |matching_context| {
|
||||
data.selectors_for_cache_revalidation.lookup(
|
||||
element,
|
||||
quirks_mode,
|
||||
|selector_and_hashes| {
|
||||
results.push(matches_selector(
|
||||
&selector_and_hashes.selector,
|
||||
selector_and_hashes.selector_offset,
|
||||
Some(&selector_and_hashes.hashes),
|
||||
&element,
|
||||
matching_context,
|
||||
flags_setter,
|
||||
));
|
||||
true
|
||||
},
|
||||
);
|
||||
})
|
||||
});
|
||||
|
||||
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 euclid::{Point2D, Size2D};
|
||||
use properties::PropertyId;
|
||||
use smallvec::SmallVec;
|
||||
use std::cmp;
|
||||
use values::computed::Angle as ComputedAngle;
|
||||
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::url::ComputedUrl;
|
||||
|
||||
pub mod color;
|
||||
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.
|
||||
///
|
||||
|
@ -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 {
|
||||
#[inline]
|
||||
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::generics::basic_shape as generic;
|
||||
|
||||
/// A computed alias for FillRule.
|
||||
pub use values::generics::basic_shape::FillRule;
|
||||
|
||||
/// A computed clipping shape.
|
||||
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::angle::Angle;
|
||||
pub use self::background::{BackgroundRepeat, BackgroundSize};
|
||||
pub use self::basic_shape::FillRule;
|
||||
pub use self::border::{BorderImageRepeat, BorderImageSideWidth, BorderImageSlice, BorderImageWidth};
|
||||
pub use self::border::{BorderCornerRadius, BorderRadius, BorderSpacing};
|
||||
pub use self::font::{FontSize, FontSizeAdjust, FontStretch, FontSynthesis, FontVariantAlternates, FontWeight};
|
||||
|
|
|
@ -4,11 +4,8 @@
|
|||
|
||||
//! Computed types for SVG properties.
|
||||
|
||||
use app_units::Au;
|
||||
use values::RGBA;
|
||||
use values::computed::{LengthOrPercentage, NonNegativeLength};
|
||||
use values::computed::{NonNegativeLengthOrPercentage, NonNegativeNumber, Number};
|
||||
use values::computed::Opacity;
|
||||
use values::computed::{NonNegativeLengthOrPercentage, NonNegativeNumber, Number, LengthOrPercentage, Opacity};
|
||||
use values::computed::color::Color;
|
||||
use values::computed::url::ComputedUrl;
|
||||
use values::generics::svg as generic;
|
||||
|
@ -50,10 +47,11 @@ pub type SvgLengthOrPercentageOrNumber =
|
|||
/// <length> | <percentage> | <number> | context-value
|
||||
pub type SVGLength = generic::SVGLength<SvgLengthOrPercentageOrNumber>;
|
||||
|
||||
impl From<Au> for SVGLength {
|
||||
fn from(length: Au) -> Self {
|
||||
impl SVGLength {
|
||||
/// `0px`
|
||||
pub fn zero() -> Self {
|
||||
generic::SVGLength::Length(generic::SvgLengthOrPercentageOrNumber::LengthOrPercentage(
|
||||
length.into(),
|
||||
LengthOrPercentage::zero()
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -63,6 +61,8 @@ impl From<Au> for SVGLength {
|
|||
pub type NonNegativeSvgLengthOrPercentageOrNumber =
|
||||
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 {
|
||||
fn into(self) -> NonNegativeSvgLengthOrPercentageOrNumber {
|
||||
match self {
|
||||
|
@ -79,10 +79,12 @@ impl Into<NonNegativeSvgLengthOrPercentageOrNumber> for SvgLengthOrPercentageOrN
|
|||
/// An non-negative wrapper of SVGLength.
|
||||
pub type SVGWidth = generic::SVGLength<NonNegativeSvgLengthOrPercentageOrNumber>;
|
||||
|
||||
impl From<NonNegativeLength> for SVGWidth {
|
||||
fn from(length: NonNegativeLength) -> Self {
|
||||
impl SVGWidth {
|
||||
/// `1px`.
|
||||
pub fn one() -> Self {
|
||||
use values::generics::NonNegative;
|
||||
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() {
|
||||
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 {
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -8,9 +8,6 @@ use cssparser::Parser;
|
|||
use parser::{Parse, ParserContext};
|
||||
use style_traits::{ParseError, StyleParseErrorKind};
|
||||
use values::{Either, None_};
|
||||
use values::computed::NumberOrPercentage;
|
||||
use values::computed::length::LengthOrPercentage;
|
||||
use values::distance::{ComputeSquaredDistance, SquaredDistance};
|
||||
|
||||
/// An SVG paint value
|
||||
///
|
||||
|
@ -152,54 +149,6 @@ pub enum SvgLengthOrPercentageOrNumber<LengthOrPercentage, 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
|
||||
/// since prevent converting to the length.
|
||||
impl<LengthOrPercentageType: Parse, NumberType: Parse> Parse
|
||||
|
@ -213,10 +162,8 @@ impl<LengthOrPercentageType: Parse, NumberType: Parse> Parse
|
|||
return Ok(SvgLengthOrPercentageOrNumber::Number(num));
|
||||
}
|
||||
|
||||
if let Ok(lop) = input.try(|i| LengthOrPercentageType::parse(context, i)) {
|
||||
return Ok(SvgLengthOrPercentageOrNumber::LengthOrPercentage(lop));
|
||||
}
|
||||
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
let lop = LengthOrPercentageType::parse(context, input)?;
|
||||
Ok(SvgLengthOrPercentageOrNumber::LengthOrPercentage(lop))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ use std::fmt::{self, Write};
|
|||
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
|
||||
use values::computed::Percentage;
|
||||
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::rect::Rect;
|
||||
use values::specified::LengthOrPercentage;
|
||||
|
@ -25,6 +25,9 @@ use values::specified::position::{HorizontalPosition, Position, PositionComponen
|
|||
use values::specified::position::{Side, VerticalPosition};
|
||||
use values::specified::url::SpecifiedUrl;
|
||||
|
||||
/// A specified alias for FillRule.
|
||||
pub use values::generics::basic_shape::FillRule;
|
||||
|
||||
/// A specified clipping shape.
|
||||
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<()>> {
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ pub use self::align::{AlignContent, AlignItems, AlignSelf, ContentDistribution};
|
|||
#[cfg(feature = "gecko")]
|
||||
pub use self::align::{JustifyContent, JustifyItems, JustifySelf, SelfAlignment};
|
||||
pub use self::background::{BackgroundRepeat, BackgroundSize};
|
||||
pub use self::basic_shape::FillRule;
|
||||
pub use self::border::{BorderCornerRadius, BorderImageSlice, BorderImageWidth};
|
||||
pub use self::border::{BorderImageRepeat, BorderImageSideWidth};
|
||||
pub use self::border::{BorderRadius, BorderSideWidth, BorderSpacing};
|
||||
|
|
|
@ -488,12 +488,7 @@ impl TextAlign {
|
|||
/// Convert an enumerated value coming from Gecko to a `TextAlign`.
|
||||
#[cfg(feature = "gecko")]
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ gecko = []
|
|||
|
||||
[dependencies]
|
||||
app_units = "0.7"
|
||||
cssparser = "0.24.0"
|
||||
cssparser = "0.25"
|
||||
bitflags = "1.0"
|
||||
euclid = "0.19"
|
||||
malloc_size_of = { path = "../malloc_size_of" }
|
||||
|
|
|
@ -12,7 +12,7 @@ doctest = false
|
|||
[dependencies]
|
||||
byteorder = "1.0"
|
||||
app_units = "0.7"
|
||||
cssparser = "0.24.0"
|
||||
cssparser = "0.25"
|
||||
euclid = "0.19"
|
||||
html5ever = "0.22"
|
||||
parking_lot = "0.6"
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
use cssparser::{Parser, ParserInput};
|
||||
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 test::{self, Bencher};
|
||||
|
||||
|
@ -18,7 +18,8 @@ fn cascade(
|
|||
(Name::from(name), SpecifiedValue::parse(&mut parser).unwrap())
|
||||
}).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 {
|
||||
builder.cascade(name, &CustomDeclarationValue::Value(val.clone()));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue