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:
bors-servo 2018-11-05 06:46:56 -05:00 committed by GitHub
commit ff7c6953c8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
47 changed files with 1330 additions and 1091 deletions

22
Cargo.lock generated
View file

@ -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"

View file

@ -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

View file

@ -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"

View file

@ -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"

View file

@ -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 }

View file

@ -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"}

View file

@ -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"

View file

@ -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"

View file

@ -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());
}

View file

@ -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"

View file

@ -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();

View file

@ -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"]

View file

@ -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 dont 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,
)
})?;
// Its 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)

View file

@ -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.

View file

@ -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)
}
}
}

View file

@ -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,
})
},

View file

@ -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

View file

@ -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) {

View file

@ -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;

View file

@ -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(),
))
}

View file

@ -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

View file

@ -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 {

View file

@ -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;
}
}

View file

@ -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
}
}

View file

@ -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(

View file

@ -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",
)}

View file

@ -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",
)}

View file

@ -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,

View file

@ -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);
}
}

View file

@ -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

View file

@ -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
}

View 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.
}
}
}

View 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,
}
}
}

View file

@ -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, ()> {

View 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(()),
}
}
}

View file

@ -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>;

View file

@ -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};

View file

@ -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())
))
}
}

View file

@ -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;
}

View file

@ -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))
}
}

View file

@ -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>;

View file

@ -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);
}

View file

@ -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};

View file

@ -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))
}
}

View file

@ -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" }

View file

@ -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"

View file

@ -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()));