mirror of
https://github.com/servo/servo.git
synced 2025-08-05 05:30:08 +01:00
style: Implement the env() function with hardcoded zeros for safe-area-inset.
Intent to Implement and Ship: https://groups.google.com/d/msg/mozilla.dev.platform/EVKyR1B87T0/_l-_qK8SAAAJ Differential Revision: https://phabricator.services.mozilla.com/D9609
This commit is contained in:
parent
5af6abfb78
commit
b7da1bac88
11 changed files with 346 additions and 136 deletions
|
@ -26,7 +26,7 @@ servo = [
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
app_units = "0.7"
|
app_units = "0.7"
|
||||||
cssparser = "0.24.0"
|
cssparser = "0.25"
|
||||||
euclid = "0.19"
|
euclid = "0.19"
|
||||||
hashglobe = { path = "../hashglobe" }
|
hashglobe = { path = "../hashglobe" }
|
||||||
hyper = { version = "0.12", optional = true }
|
hyper = { version = "0.12", optional = true }
|
||||||
|
|
|
@ -22,7 +22,7 @@ bench = []
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitflags = "1.0"
|
bitflags = "1.0"
|
||||||
matches = "0.1"
|
matches = "0.1"
|
||||||
cssparser = "0.24.0"
|
cssparser = "0.25"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
fxhash = "0.2"
|
fxhash = "0.2"
|
||||||
phf = "0.7.18"
|
phf = "0.7.18"
|
||||||
|
|
|
@ -31,7 +31,7 @@ atomic_refcell = "0.1"
|
||||||
bitflags = "1.0"
|
bitflags = "1.0"
|
||||||
byteorder = "1.0"
|
byteorder = "1.0"
|
||||||
cfg-if = "0.1.0"
|
cfg-if = "0.1.0"
|
||||||
cssparser = "0.24.0"
|
cssparser = "0.25"
|
||||||
new_debug_unreachable = "1.0"
|
new_debug_unreachable = "1.0"
|
||||||
encoding_rs = {version = "0.7", optional = true}
|
encoding_rs = {version = "0.7", optional = true}
|
||||||
euclid = "0.19"
|
euclid = "0.19"
|
||||||
|
|
|
@ -21,6 +21,59 @@ use std::fmt::{self, Write};
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
|
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
|
||||||
|
|
||||||
|
/// The environment from which to get `env` function values.
|
||||||
|
///
|
||||||
|
/// TODO(emilio): If this becomes a bit more complex we should probably move it
|
||||||
|
/// to the `media_queries` module, or something.
|
||||||
|
pub struct CssEnvironment;
|
||||||
|
|
||||||
|
struct EnvironmentVariable {
|
||||||
|
name: Atom,
|
||||||
|
value: VariableValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! make_variable {
|
||||||
|
($name:expr, $value:expr) => {{
|
||||||
|
EnvironmentVariable {
|
||||||
|
name: $name,
|
||||||
|
value: {
|
||||||
|
// TODO(emilio): We could make this be more efficient (though a
|
||||||
|
// bit less convenient).
|
||||||
|
let mut input = ParserInput::new($value);
|
||||||
|
let mut input = Parser::new(&mut input);
|
||||||
|
|
||||||
|
let (first_token_type, css, last_token_type) =
|
||||||
|
parse_self_contained_declaration_value(&mut input, None).unwrap();
|
||||||
|
|
||||||
|
VariableValue {
|
||||||
|
css: css.into_owned(),
|
||||||
|
first_token_type,
|
||||||
|
last_token_type,
|
||||||
|
references: Default::default(),
|
||||||
|
references_environment: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref ENVIRONMENT_VARIABLES: [EnvironmentVariable; 4] = [
|
||||||
|
make_variable!(atom!("safe-area-inset-top"), "0px"),
|
||||||
|
make_variable!(atom!("safe-area-inset-bottom"), "0px"),
|
||||||
|
make_variable!(atom!("safe-area-inset-left"), "0px"),
|
||||||
|
make_variable!(atom!("safe-area-inset-right"), "0px"),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CssEnvironment {
|
||||||
|
#[inline]
|
||||||
|
fn get(&self, name: &Atom) -> Option<&VariableValue> {
|
||||||
|
let var = ENVIRONMENT_VARIABLES.iter().find(|var| var.name == *name)?;
|
||||||
|
Some(&var.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A custom property name is just an `Atom`.
|
/// A custom property name is just an `Atom`.
|
||||||
///
|
///
|
||||||
/// Note that this does not include the `--` prefix
|
/// Note that this does not include the `--` prefix
|
||||||
|
@ -48,6 +101,12 @@ pub struct VariableValue {
|
||||||
first_token_type: TokenSerializationType,
|
first_token_type: TokenSerializationType,
|
||||||
last_token_type: TokenSerializationType,
|
last_token_type: TokenSerializationType,
|
||||||
|
|
||||||
|
/// Whether a variable value has a reference to an environment variable.
|
||||||
|
///
|
||||||
|
/// If this is the case, we need to perform variable substitution on the
|
||||||
|
/// value.
|
||||||
|
references_environment: bool,
|
||||||
|
|
||||||
/// Custom property names in var() functions.
|
/// Custom property names in var() functions.
|
||||||
references: PrecomputedHashSet<Name>,
|
references: PrecomputedHashSet<Name>,
|
||||||
}
|
}
|
||||||
|
@ -216,6 +275,14 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A struct holding information about the external references to that a custom
|
||||||
|
/// property value may have.
|
||||||
|
#[derive(Default)]
|
||||||
|
struct VarOrEnvReferences {
|
||||||
|
custom_property_references: PrecomputedHashSet<Name>,
|
||||||
|
references_environment: bool,
|
||||||
|
}
|
||||||
|
|
||||||
impl VariableValue {
|
impl VariableValue {
|
||||||
fn empty() -> Self {
|
fn empty() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -223,6 +290,7 @@ impl VariableValue {
|
||||||
last_token_type: TokenSerializationType::nothing(),
|
last_token_type: TokenSerializationType::nothing(),
|
||||||
first_token_type: TokenSerializationType::nothing(),
|
first_token_type: TokenSerializationType::nothing(),
|
||||||
references: PrecomputedHashSet::default(),
|
references: PrecomputedHashSet::default(),
|
||||||
|
references_environment: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,7 +341,7 @@ impl VariableValue {
|
||||||
|
|
||||||
/// Parse a custom property value.
|
/// Parse a custom property value.
|
||||||
pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Arc<Self>, ParseError<'i>> {
|
pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Arc<Self>, ParseError<'i>> {
|
||||||
let mut references = PrecomputedHashSet::default();
|
let mut references = VarOrEnvReferences::default();
|
||||||
|
|
||||||
let (first_token_type, css, last_token_type) =
|
let (first_token_type, css, last_token_type) =
|
||||||
parse_self_contained_declaration_value(input, Some(&mut references))?;
|
parse_self_contained_declaration_value(input, Some(&mut references))?;
|
||||||
|
@ -282,7 +350,8 @@ impl VariableValue {
|
||||||
css: css.into_owned(),
|
css: css.into_owned(),
|
||||||
first_token_type,
|
first_token_type,
|
||||||
last_token_type,
|
last_token_type,
|
||||||
references,
|
references: references.custom_property_references,
|
||||||
|
references_environment: references.references_environment,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -297,7 +366,7 @@ pub fn parse_non_custom_with_var<'i, 't>(
|
||||||
|
|
||||||
fn parse_self_contained_declaration_value<'i, 't>(
|
fn parse_self_contained_declaration_value<'i, 't>(
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
references: Option<&mut PrecomputedHashSet<Name>>,
|
references: Option<&mut VarOrEnvReferences>,
|
||||||
) -> Result<(TokenSerializationType, Cow<'i, str>, TokenSerializationType), ParseError<'i>> {
|
) -> Result<(TokenSerializationType, Cow<'i, str>, TokenSerializationType), ParseError<'i>> {
|
||||||
let start_position = input.position();
|
let start_position = input.position();
|
||||||
let mut missing_closing_characters = String::new();
|
let mut missing_closing_characters = String::new();
|
||||||
|
@ -317,7 +386,7 @@ fn parse_self_contained_declaration_value<'i, 't>(
|
||||||
/// <https://drafts.csswg.org/css-syntax-3/#typedef-declaration-value>
|
/// <https://drafts.csswg.org/css-syntax-3/#typedef-declaration-value>
|
||||||
fn parse_declaration_value<'i, 't>(
|
fn parse_declaration_value<'i, 't>(
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
references: Option<&mut PrecomputedHashSet<Name>>,
|
references: Option<&mut VarOrEnvReferences>,
|
||||||
missing_closing_characters: &mut String,
|
missing_closing_characters: &mut String,
|
||||||
) -> Result<(TokenSerializationType, TokenSerializationType), ParseError<'i>> {
|
) -> Result<(TokenSerializationType, TokenSerializationType), ParseError<'i>> {
|
||||||
input.parse_until_before(Delimiter::Bang | Delimiter::Semicolon, |input| {
|
input.parse_until_before(Delimiter::Bang | Delimiter::Semicolon, |input| {
|
||||||
|
@ -334,7 +403,7 @@ fn parse_declaration_value<'i, 't>(
|
||||||
/// invalid at the top level
|
/// invalid at the top level
|
||||||
fn parse_declaration_value_block<'i, 't>(
|
fn parse_declaration_value_block<'i, 't>(
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
mut references: Option<&mut PrecomputedHashSet<Name>>,
|
mut references: Option<&mut VarOrEnvReferences>,
|
||||||
missing_closing_characters: &mut String,
|
missing_closing_characters: &mut String,
|
||||||
) -> Result<(TokenSerializationType, TokenSerializationType), ParseError<'i>> {
|
) -> Result<(TokenSerializationType, TokenSerializationType), ParseError<'i>> {
|
||||||
let mut token_start = input.position();
|
let mut token_start = input.position();
|
||||||
|
@ -407,6 +476,12 @@ fn parse_declaration_value_block<'i, 't>(
|
||||||
parse_var_function(input, references.as_mut().map(|r| &mut **r))
|
parse_var_function(input, references.as_mut().map(|r| &mut **r))
|
||||||
})?;
|
})?;
|
||||||
input.reset(&args_start);
|
input.reset(&args_start);
|
||||||
|
} else if name.eq_ignore_ascii_case("env") {
|
||||||
|
let args_start = input.state();
|
||||||
|
input.parse_nested_block(|input| {
|
||||||
|
parse_env_function(input, references.as_mut().map(|r| &mut **r))
|
||||||
|
})?;
|
||||||
|
input.reset(&args_start);
|
||||||
}
|
}
|
||||||
nested!();
|
nested!();
|
||||||
check_closed!(")");
|
check_closed!(")");
|
||||||
|
@ -468,17 +543,7 @@ fn parse_declaration_value_block<'i, 't>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the var function is valid, return Ok((custom_property_name, fallback))
|
fn parse_fallback<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(), ParseError<'i>> {
|
||||||
fn parse_var_function<'i, 't>(
|
|
||||||
input: &mut Parser<'i, 't>,
|
|
||||||
references: Option<&mut PrecomputedHashSet<Name>>,
|
|
||||||
) -> Result<(), ParseError<'i>> {
|
|
||||||
let name = input.expect_ident_cloned()?;
|
|
||||||
let name: Result<_, ParseError> = parse_name(&name).map_err(|()| {
|
|
||||||
input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))
|
|
||||||
});
|
|
||||||
let name = name?;
|
|
||||||
if input.try(|input| input.expect_comma()).is_ok() {
|
|
||||||
// Exclude `!` and `;` at the top level
|
// Exclude `!` and `;` at the top level
|
||||||
// https://drafts.csswg.org/css-syntax/#typedef-declaration-value
|
// https://drafts.csswg.org/css-syntax/#typedef-declaration-value
|
||||||
input.parse_until_before(Delimiter::Bang | Delimiter::Semicolon, |input| {
|
input.parse_until_before(Delimiter::Bang | Delimiter::Semicolon, |input| {
|
||||||
|
@ -487,10 +552,39 @@ fn parse_var_function<'i, 't>(
|
||||||
// Skip until the end.
|
// Skip until the end.
|
||||||
while let Ok(_) = input.next_including_whitespace_and_comments() {}
|
while let Ok(_) = input.next_including_whitespace_and_comments() {}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the var function is valid, return Ok((custom_property_name, fallback))
|
||||||
|
fn parse_var_function<'i, 't>(
|
||||||
|
input: &mut Parser<'i, 't>,
|
||||||
|
references: Option<&mut VarOrEnvReferences>,
|
||||||
|
) -> Result<(), ParseError<'i>> {
|
||||||
|
let name = input.expect_ident_cloned()?;
|
||||||
|
let name = parse_name(&name).map_err(|()| {
|
||||||
|
input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))
|
||||||
})?;
|
})?;
|
||||||
|
if input.try(|input| input.expect_comma()).is_ok() {
|
||||||
|
parse_fallback(input)?;
|
||||||
}
|
}
|
||||||
if let Some(refs) = references {
|
if let Some(refs) = references {
|
||||||
refs.insert(Atom::from(name));
|
refs.custom_property_references.insert(Atom::from(name));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_env_function<'i, 't>(
|
||||||
|
input: &mut Parser<'i, 't>,
|
||||||
|
references: Option<&mut VarOrEnvReferences>,
|
||||||
|
) -> Result<(), ParseError<'i>> {
|
||||||
|
// TODO(emilio): This should be <custom-ident> per spec, but no other
|
||||||
|
// browser does that, see https://github.com/w3c/csswg-drafts/issues/3262.
|
||||||
|
input.expect_ident()?;
|
||||||
|
if input.try(|input| input.expect_comma()).is_ok() {
|
||||||
|
parse_fallback(input)?;
|
||||||
|
}
|
||||||
|
if let Some(references) = references {
|
||||||
|
references.references_environment = true;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -502,25 +596,26 @@ pub struct CustomPropertiesBuilder<'a> {
|
||||||
may_have_cycles: bool,
|
may_have_cycles: bool,
|
||||||
custom_properties: Option<CustomPropertiesMap>,
|
custom_properties: Option<CustomPropertiesMap>,
|
||||||
inherited: Option<&'a Arc<CustomPropertiesMap>>,
|
inherited: Option<&'a Arc<CustomPropertiesMap>>,
|
||||||
|
environment: &'a CssEnvironment,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> CustomPropertiesBuilder<'a> {
|
impl<'a> CustomPropertiesBuilder<'a> {
|
||||||
/// Create a new builder, inheriting from a given custom properties map.
|
/// Create a new builder, inheriting from a given custom properties map.
|
||||||
pub fn new(inherited: Option<&'a Arc<CustomPropertiesMap>>) -> Self {
|
pub fn new(
|
||||||
|
inherited: Option<&'a Arc<CustomPropertiesMap>>,
|
||||||
|
environment: &'a CssEnvironment,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
seen: PrecomputedHashSet::default(),
|
seen: PrecomputedHashSet::default(),
|
||||||
may_have_cycles: false,
|
may_have_cycles: false,
|
||||||
custom_properties: None,
|
custom_properties: None,
|
||||||
inherited,
|
inherited,
|
||||||
|
environment,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cascade a given custom property declaration.
|
/// Cascade a given custom property declaration.
|
||||||
pub fn cascade(
|
pub fn cascade(&mut self, name: &'a Name, specified_value: &CustomDeclarationValue) {
|
||||||
&mut self,
|
|
||||||
name: &'a Name,
|
|
||||||
specified_value: &CustomDeclarationValue,
|
|
||||||
) {
|
|
||||||
let was_already_present = !self.seen.insert(name);
|
let was_already_present = !self.seen.insert(name);
|
||||||
if was_already_present {
|
if was_already_present {
|
||||||
return;
|
return;
|
||||||
|
@ -540,8 +635,31 @@ impl<'a> CustomPropertiesBuilder<'a> {
|
||||||
let map = self.custom_properties.as_mut().unwrap();
|
let map = self.custom_properties.as_mut().unwrap();
|
||||||
match *specified_value {
|
match *specified_value {
|
||||||
CustomDeclarationValue::Value(ref unparsed_value) => {
|
CustomDeclarationValue::Value(ref unparsed_value) => {
|
||||||
self.may_have_cycles |= !unparsed_value.references.is_empty();
|
let has_references = !unparsed_value.references.is_empty();
|
||||||
map.insert(name.clone(), (*unparsed_value).clone());
|
self.may_have_cycles |= has_references;
|
||||||
|
|
||||||
|
// If the variable value has no references and it has an
|
||||||
|
// environment variable here, perform substitution here instead
|
||||||
|
// of forcing a full traversal in `substitute_all` afterwards.
|
||||||
|
let value = if !has_references && unparsed_value.references_environment {
|
||||||
|
let invalid = Default::default(); // Irrelevant, since there are no references.
|
||||||
|
let result = substitute_references_in_value(
|
||||||
|
unparsed_value,
|
||||||
|
&map,
|
||||||
|
&invalid,
|
||||||
|
&self.environment,
|
||||||
|
);
|
||||||
|
match result {
|
||||||
|
Ok(new_value) => Arc::new(new_value),
|
||||||
|
Err(..) => {
|
||||||
|
map.remove(name);
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(*unparsed_value).clone()
|
||||||
|
};
|
||||||
|
map.insert(name.clone(), value);
|
||||||
},
|
},
|
||||||
CustomDeclarationValue::CSSWideKeyword(keyword) => match keyword {
|
CustomDeclarationValue::CSSWideKeyword(keyword) => match keyword {
|
||||||
CSSWideKeyword::Initial => {
|
CSSWideKeyword::Initial => {
|
||||||
|
@ -553,11 +671,7 @@ impl<'a> CustomPropertiesBuilder<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn value_may_affect_style(
|
fn value_may_affect_style(&self, name: &Name, value: &CustomDeclarationValue) -> bool {
|
||||||
&self,
|
|
||||||
name: &Name,
|
|
||||||
value: &CustomDeclarationValue,
|
|
||||||
) -> bool {
|
|
||||||
match *value {
|
match *value {
|
||||||
CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Unset) |
|
CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Unset) |
|
||||||
CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Inherit) => {
|
CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Inherit) => {
|
||||||
|
@ -596,8 +710,8 @@ impl<'a> CustomPropertiesBuilder<'a> {
|
||||||
|
|
||||||
/// Returns the final map of applicable custom properties.
|
/// Returns the final map of applicable custom properties.
|
||||||
///
|
///
|
||||||
/// If there was any specified property, we've created a new map and now we need
|
/// If there was any specified property, we've created a new map and now we
|
||||||
/// to remove any potential cycles, and wrap it in an arc.
|
/// need to remove any potential cycles, and wrap it in an arc.
|
||||||
///
|
///
|
||||||
/// Otherwise, just use the inherited custom properties map.
|
/// Otherwise, just use the inherited custom properties map.
|
||||||
pub fn build(mut self) -> Option<Arc<CustomPropertiesMap>> {
|
pub fn build(mut self) -> Option<Arc<CustomPropertiesMap>> {
|
||||||
|
@ -605,9 +719,8 @@ impl<'a> CustomPropertiesBuilder<'a> {
|
||||||
Some(m) => m,
|
Some(m) => m,
|
||||||
None => return self.inherited.cloned(),
|
None => return self.inherited.cloned(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.may_have_cycles {
|
if self.may_have_cycles {
|
||||||
substitute_all(&mut map);
|
substitute_all(&mut map, self.environment);
|
||||||
}
|
}
|
||||||
Some(Arc::new(map))
|
Some(Arc::new(map))
|
||||||
}
|
}
|
||||||
|
@ -616,7 +729,7 @@ impl<'a> CustomPropertiesBuilder<'a> {
|
||||||
/// Resolve all custom properties to either substituted or invalid.
|
/// Resolve all custom properties to either substituted or invalid.
|
||||||
///
|
///
|
||||||
/// It does cycle dependencies removal at the same time as substitution.
|
/// It does cycle dependencies removal at the same time as substitution.
|
||||||
fn substitute_all(custom_properties_map: &mut CustomPropertiesMap) {
|
fn substitute_all(custom_properties_map: &mut CustomPropertiesMap, environment: &CssEnvironment) {
|
||||||
// The cycle dependencies removal in this function is a variant
|
// The cycle dependencies removal in this function is a variant
|
||||||
// of Tarjan's algorithm. It is mostly based on the pseudo-code
|
// of Tarjan's algorithm. It is mostly based on the pseudo-code
|
||||||
// listed in
|
// listed in
|
||||||
|
@ -664,6 +777,8 @@ fn substitute_all(custom_properties_map: &mut CustomPropertiesMap) {
|
||||||
map: &'a mut CustomPropertiesMap,
|
map: &'a mut CustomPropertiesMap,
|
||||||
/// The set of invalid custom properties.
|
/// The set of invalid custom properties.
|
||||||
invalid: &'a mut PrecomputedHashSet<Name>,
|
invalid: &'a mut PrecomputedHashSet<Name>,
|
||||||
|
/// The environment to substitute `env()` variables.
|
||||||
|
environment: &'a CssEnvironment,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function combines the traversal for cycle removal and value
|
/// This function combines the traversal for cycle removal and value
|
||||||
|
@ -686,11 +801,23 @@ fn substitute_all(custom_properties_map: &mut CustomPropertiesMap) {
|
||||||
/// * There is no such variable at all.
|
/// * There is no such variable at all.
|
||||||
fn traverse<'a>(name: Name, context: &mut Context<'a>) -> Option<usize> {
|
fn traverse<'a>(name: Name, context: &mut Context<'a>) -> Option<usize> {
|
||||||
// Some shortcut checks.
|
// Some shortcut checks.
|
||||||
let (name, value) = if let Some(value) = context.map.get(&name) {
|
let (name, value) = {
|
||||||
// This variable has been resolved. Return the signal value.
|
let value = context.map.get(&name)?;
|
||||||
if value.references.is_empty() || context.invalid.contains(&name) {
|
|
||||||
|
// Nothing to resolve.
|
||||||
|
if value.references.is_empty() {
|
||||||
|
debug_assert!(
|
||||||
|
!value.references_environment,
|
||||||
|
"Should've been handled earlier"
|
||||||
|
);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This variable has already been resolved.
|
||||||
|
if context.invalid.contains(&name) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
// Whether this variable has been visited in this traversal.
|
// Whether this variable has been visited in this traversal.
|
||||||
let key;
|
let key;
|
||||||
match context.index_map.entry(name) {
|
match context.index_map.entry(name) {
|
||||||
|
@ -702,12 +829,10 @@ fn substitute_all(custom_properties_map: &mut CustomPropertiesMap) {
|
||||||
entry.insert(context.count);
|
entry.insert(context.count);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hold a strong reference to the value so that we don't
|
// Hold a strong reference to the value so that we don't
|
||||||
// need to keep reference to context.map.
|
// need to keep reference to context.map.
|
||||||
(key, value.clone())
|
(key, value.clone())
|
||||||
} else {
|
|
||||||
// The variable doesn't exist at all.
|
|
||||||
return None;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add new entry to the information table.
|
// Add new entry to the information table.
|
||||||
|
@ -793,29 +918,20 @@ fn substitute_all(custom_properties_map: &mut CustomPropertiesMap) {
|
||||||
// Now we have shown that this variable is not in a loop, and
|
// Now we have shown that this variable is not in a loop, and
|
||||||
// all of its dependencies should have been resolved. We can
|
// all of its dependencies should have been resolved. We can
|
||||||
// start substitution now.
|
// start substitution now.
|
||||||
let mut computed_value = ComputedValue::empty();
|
let result = substitute_references_in_value(
|
||||||
let mut input = ParserInput::new(&value.css);
|
&value,
|
||||||
let mut input = Parser::new(&mut input);
|
&context.map,
|
||||||
let mut position = (input.position(), value.first_token_type);
|
&context.invalid,
|
||||||
let result = substitute_block(
|
&context.environment,
|
||||||
&mut input,
|
|
||||||
&mut position,
|
|
||||||
&mut computed_value,
|
|
||||||
&mut |name, partial_computed_value| {
|
|
||||||
if let Some(value) = context.map.get(name) {
|
|
||||||
if !context.invalid.contains(name) {
|
|
||||||
partial_computed_value.push_variable(value);
|
|
||||||
return Ok(value.last_token_type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(())
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
if let Ok(last_token_type) = result {
|
|
||||||
computed_value.push_from(position, &input, last_token_type);
|
match result {
|
||||||
|
Ok(computed_value) => {
|
||||||
context.map.insert(name, Arc::new(computed_value));
|
context.map.insert(name, Arc::new(computed_value));
|
||||||
} else {
|
},
|
||||||
|
Err(..) => {
|
||||||
context.invalid.insert(name);
|
context.invalid.insert(name);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// All resolved, so return the signal value.
|
// All resolved, so return the signal value.
|
||||||
|
@ -834,6 +950,7 @@ fn substitute_all(custom_properties_map: &mut CustomPropertiesMap) {
|
||||||
var_info: SmallVec::new(),
|
var_info: SmallVec::new(),
|
||||||
map: custom_properties_map,
|
map: custom_properties_map,
|
||||||
invalid: &mut invalid,
|
invalid: &mut invalid,
|
||||||
|
environment,
|
||||||
};
|
};
|
||||||
traverse(name, &mut context);
|
traverse(name, &mut context);
|
||||||
}
|
}
|
||||||
|
@ -841,25 +958,51 @@ fn substitute_all(custom_properties_map: &mut CustomPropertiesMap) {
|
||||||
custom_properties_map.remove_set(&invalid);
|
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,
|
||||||
|
invalid_custom_properties: &PrecomputedHashSet<Name>,
|
||||||
|
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,
|
||||||
|
invalid_custom_properties,
|
||||||
|
environment,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
computed_value.push_from(position, &input, last_token_type);
|
||||||
|
Ok(computed_value)
|
||||||
|
}
|
||||||
|
|
||||||
/// Replace `var()` functions in an arbitrary bit of input.
|
/// Replace `var()` functions in an arbitrary bit of input.
|
||||||
///
|
///
|
||||||
/// The `substitute_one` callback is called for each `var()` function in `input`.
|
/// If the variable has its initial value, the callback should return `Err(())`
|
||||||
/// If the variable has its initial value,
|
/// and leave `partial_computed_value` unchanged.
|
||||||
/// the callback should return `Err(())` and leave `partial_computed_value` unchanged.
|
///
|
||||||
/// Otherwise, it should push the value of the variable (with its own `var()` functions replaced)
|
/// Otherwise, it should push the value of the variable (with its own `var()` functions replaced)
|
||||||
/// to `partial_computed_value` and return `Ok(last_token_type of what was pushed)`
|
/// to `partial_computed_value` and return `Ok(last_token_type of what was pushed)`
|
||||||
///
|
///
|
||||||
/// Return `Err(())` if `input` is invalid at computed-value time.
|
/// Return `Err(())` if `input` is invalid at computed-value time.
|
||||||
/// or `Ok(last_token_type that was pushed to partial_computed_value)` otherwise.
|
/// or `Ok(last_token_type that was pushed to partial_computed_value)` otherwise.
|
||||||
fn substitute_block<'i, 't, F>(
|
fn substitute_block<'i, 't>(
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
position: &mut (SourcePosition, TokenSerializationType),
|
position: &mut (SourcePosition, TokenSerializationType),
|
||||||
partial_computed_value: &mut ComputedValue,
|
partial_computed_value: &mut ComputedValue,
|
||||||
substitute_one: &mut F,
|
custom_properties: &CustomPropertiesMap,
|
||||||
) -> Result<TokenSerializationType, ParseError<'i>>
|
invalid_custom_properties: &PrecomputedHashSet<Name>,
|
||||||
where
|
env: &CssEnvironment,
|
||||||
F: FnMut(&Name, &mut ComputedValue) -> Result<TokenSerializationType, ()>,
|
) -> Result<TokenSerializationType, ParseError<'i>> {
|
||||||
{
|
|
||||||
let mut last_token_type = TokenSerializationType::nothing();
|
let mut last_token_type = TokenSerializationType::nothing();
|
||||||
let mut set_position_at_next_iteration = false;
|
let mut set_position_at_next_iteration = false;
|
||||||
loop {
|
loop {
|
||||||
|
@ -883,27 +1026,49 @@ where
|
||||||
Err(..) => break,
|
Err(..) => break,
|
||||||
};
|
};
|
||||||
match token {
|
match token {
|
||||||
Token::Function(ref name) if name.eq_ignore_ascii_case("var") => {
|
Token::Function(ref name)
|
||||||
|
if name.eq_ignore_ascii_case("var") || name.eq_ignore_ascii_case("env") =>
|
||||||
|
{
|
||||||
|
let is_env = name.eq_ignore_ascii_case("env");
|
||||||
|
|
||||||
partial_computed_value.push(
|
partial_computed_value.push(
|
||||||
input.slice(position.0..before_this_token),
|
input.slice(position.0..before_this_token),
|
||||||
position.1,
|
position.1,
|
||||||
last_token_type,
|
last_token_type,
|
||||||
);
|
);
|
||||||
input.parse_nested_block(|input| {
|
input.parse_nested_block(|input| {
|
||||||
// parse_var_function() ensures neither .unwrap() will fail.
|
// parse_var_function() / parse_env_function() ensure neither .unwrap() will fail.
|
||||||
let name = input.expect_ident_cloned().unwrap();
|
let name = {
|
||||||
let name = Atom::from(parse_name(&name).unwrap());
|
let name = input.expect_ident().unwrap();
|
||||||
|
if is_env {
|
||||||
|
Atom::from(&**name)
|
||||||
|
} else {
|
||||||
|
Atom::from(parse_name(&name).unwrap())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if let Ok(last) = substitute_one(&name, partial_computed_value) {
|
let value = if is_env {
|
||||||
last_token_type = last;
|
env.get(&name)
|
||||||
|
} else {
|
||||||
|
if invalid_custom_properties.contains(&name) {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
custom_properties.get(&name).map(|v| &**v)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(v) = value {
|
||||||
|
last_token_type = v.last_token_type;
|
||||||
|
partial_computed_value.push_variable(v);
|
||||||
// Skip over the fallback, as `parse_nested_block` would return `Err`
|
// Skip over the fallback, as `parse_nested_block` would return `Err`
|
||||||
// if we don’t consume all of `input`.
|
// if we don't consume all of `input`.
|
||||||
// FIXME: Add a specialized method to cssparser to do this with less work.
|
// FIXME: Add a specialized method to cssparser to do this with less work.
|
||||||
while let Ok(_) = input.next() {}
|
while input.next().is_ok() {}
|
||||||
} else {
|
} else {
|
||||||
input.expect_comma()?;
|
input.expect_comma()?;
|
||||||
let after_comma = input.state();
|
let after_comma = input.state();
|
||||||
let first_token_type = input.next_including_whitespace_and_comments()
|
let first_token_type = input
|
||||||
|
.next_including_whitespace_and_comments()
|
||||||
// parse_var_function() ensures that .unwrap() will not fail.
|
// parse_var_function() ensures that .unwrap() will not fail.
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.serialization_type();
|
.serialization_type();
|
||||||
|
@ -913,23 +1078,31 @@ where
|
||||||
input,
|
input,
|
||||||
&mut position,
|
&mut position,
|
||||||
partial_computed_value,
|
partial_computed_value,
|
||||||
substitute_one,
|
custom_properties,
|
||||||
|
invalid_custom_properties,
|
||||||
|
env,
|
||||||
)?;
|
)?;
|
||||||
partial_computed_value.push_from(position, input, last_token_type);
|
partial_computed_value.push_from(position, input, last_token_type);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
set_position_at_next_iteration = true
|
set_position_at_next_iteration = true
|
||||||
},
|
}
|
||||||
|
|
||||||
Token::Function(_) |
|
Token::Function(_) |
|
||||||
Token::ParenthesisBlock |
|
Token::ParenthesisBlock |
|
||||||
Token::CurlyBracketBlock |
|
Token::CurlyBracketBlock |
|
||||||
Token::SquareBracketBlock => {
|
Token::SquareBracketBlock => {
|
||||||
input.parse_nested_block(|input| {
|
input.parse_nested_block(|input| {
|
||||||
substitute_block(input, position, partial_computed_value, substitute_one)
|
substitute_block(
|
||||||
|
input,
|
||||||
|
position,
|
||||||
|
partial_computed_value,
|
||||||
|
custom_properties,
|
||||||
|
invalid_custom_properties,
|
||||||
|
env,
|
||||||
|
)
|
||||||
})?;
|
})?;
|
||||||
// It’s the same type for CloseCurlyBracket and CloseSquareBracket.
|
// It's the same type for CloseCurlyBracket and CloseSquareBracket.
|
||||||
last_token_type = Token::CloseParenthesis.serialization_type();
|
last_token_type = Token::CloseParenthesis.serialization_type();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -945,29 +1118,32 @@ where
|
||||||
Ok(last_token_type)
|
Ok(last_token_type)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Replace `var()` functions for a non-custom property.
|
/// Replace `var()` and `env()` functions for a non-custom property.
|
||||||
|
///
|
||||||
/// Return `Err(())` for invalid at computed time.
|
/// Return `Err(())` for invalid at computed time.
|
||||||
pub fn substitute<'i>(
|
pub fn substitute<'i>(
|
||||||
input: &'i str,
|
input: &'i str,
|
||||||
first_token_type: TokenSerializationType,
|
first_token_type: TokenSerializationType,
|
||||||
computed_values_map: Option<&Arc<CustomPropertiesMap>>,
|
computed_values_map: Option<&Arc<CustomPropertiesMap>>,
|
||||||
|
env: &CssEnvironment,
|
||||||
) -> Result<String, ParseError<'i>> {
|
) -> Result<String, ParseError<'i>> {
|
||||||
let mut substituted = ComputedValue::empty();
|
let mut substituted = ComputedValue::empty();
|
||||||
let mut input = ParserInput::new(input);
|
let mut input = ParserInput::new(input);
|
||||||
let mut input = Parser::new(&mut input);
|
let mut input = Parser::new(&mut input);
|
||||||
let mut position = (input.position(), first_token_type);
|
let mut position = (input.position(), first_token_type);
|
||||||
|
let invalid = PrecomputedHashSet::default();
|
||||||
|
let empty_map = CustomPropertiesMap::new();
|
||||||
|
let custom_properties = match computed_values_map {
|
||||||
|
Some(m) => &**m,
|
||||||
|
None => &empty_map,
|
||||||
|
};
|
||||||
let last_token_type = substitute_block(
|
let last_token_type = substitute_block(
|
||||||
&mut input,
|
&mut input,
|
||||||
&mut position,
|
&mut position,
|
||||||
&mut substituted,
|
&mut substituted,
|
||||||
&mut |name, substituted| {
|
&custom_properties,
|
||||||
if let Some(value) = computed_values_map.and_then(|map| map.get(name)) {
|
&invalid,
|
||||||
substituted.push_variable(value);
|
env,
|
||||||
Ok(value.last_token_type)
|
|
||||||
} else {
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)?;
|
)?;
|
||||||
substituted.push_from(position, &input, last_token_type);
|
substituted.push_from(position, &input, last_token_type);
|
||||||
Ok(substituted.css)
|
Ok(substituted.css)
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
use app_units::AU_PER_PX;
|
use app_units::AU_PER_PX;
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use cssparser::RGBA;
|
use cssparser::RGBA;
|
||||||
|
use custom_properties::CssEnvironment;
|
||||||
use euclid::Size2D;
|
use euclid::Size2D;
|
||||||
use euclid::TypedScale;
|
use euclid::TypedScale;
|
||||||
use gecko::values::{convert_nscolor_to_rgba, convert_rgba_to_nscolor};
|
use gecko::values::{convert_nscolor_to_rgba, convert_rgba_to_nscolor};
|
||||||
|
@ -52,6 +53,9 @@ pub struct Device {
|
||||||
/// Whether any styles computed in the document relied on the viewport size
|
/// Whether any styles computed in the document relied on the viewport size
|
||||||
/// by using vw/vh/vmin/vmax units.
|
/// by using vw/vh/vmin/vmax units.
|
||||||
used_viewport_size: AtomicBool,
|
used_viewport_size: AtomicBool,
|
||||||
|
/// The CssEnvironment object responsible of getting CSS environment
|
||||||
|
/// variables.
|
||||||
|
environment: CssEnvironment,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Device {
|
impl fmt::Debug for Device {
|
||||||
|
@ -87,9 +91,16 @@ impl Device {
|
||||||
body_text_color: AtomicUsize::new(unsafe { &*pres_context }.mDefaultColor as usize),
|
body_text_color: AtomicUsize::new(unsafe { &*pres_context }.mDefaultColor as usize),
|
||||||
used_root_font_size: AtomicBool::new(false),
|
used_root_font_size: AtomicBool::new(false),
|
||||||
used_viewport_size: AtomicBool::new(false),
|
used_viewport_size: AtomicBool::new(false),
|
||||||
|
environment: CssEnvironment,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the relevant environment to resolve `env()` functions.
|
||||||
|
#[inline]
|
||||||
|
pub fn environment(&self) -> &CssEnvironment {
|
||||||
|
&self.environment
|
||||||
|
}
|
||||||
|
|
||||||
/// Tells the device that a new viewport rule has been found, and stores the
|
/// Tells the device that a new viewport rule has been found, and stores the
|
||||||
/// relevant viewport constraints.
|
/// relevant viewport constraints.
|
||||||
pub fn account_for_viewport_rule(&mut self, _constraints: &ViewportConstraints) {
|
pub fn account_for_viewport_rule(&mut self, _constraints: &ViewportConstraints) {
|
||||||
|
|
|
@ -243,7 +243,10 @@ where
|
||||||
|
|
||||||
let mut declarations = SmallVec::<[(&_, CascadeLevel); 32]>::new();
|
let mut declarations = SmallVec::<[(&_, CascadeLevel); 32]>::new();
|
||||||
let custom_properties = {
|
let custom_properties = {
|
||||||
let mut builder = CustomPropertiesBuilder::new(inherited_style.custom_properties());
|
let mut builder = CustomPropertiesBuilder::new(
|
||||||
|
inherited_style.custom_properties(),
|
||||||
|
device.environment(),
|
||||||
|
);
|
||||||
|
|
||||||
for (declaration, cascade_level) in iter_declarations() {
|
for (declaration, cascade_level) in iter_declarations() {
|
||||||
declarations.push((declaration, cascade_level));
|
declarations.push((declaration, cascade_level));
|
||||||
|
@ -420,6 +423,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
||||||
declaration.id,
|
declaration.id,
|
||||||
self.context.builder.custom_properties.as_ref(),
|
self.context.builder.custom_properties.as_ref(),
|
||||||
self.context.quirks_mode,
|
self.context.quirks_mode,
|
||||||
|
self.context.device().environment(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
use context::QuirksMode;
|
use context::QuirksMode;
|
||||||
use cssparser::{DeclarationListParser, parse_important, ParserInput, CowRcStr};
|
use cssparser::{DeclarationListParser, parse_important, ParserInput, CowRcStr};
|
||||||
use cssparser::{Parser, AtRuleParser, DeclarationParser, Delimiter, ParseErrorKind};
|
use cssparser::{Parser, AtRuleParser, DeclarationParser, Delimiter, ParseErrorKind};
|
||||||
use custom_properties::CustomPropertiesBuilder;
|
use custom_properties::{CustomPropertiesBuilder, CssEnvironment};
|
||||||
use error_reporting::{ParseErrorReporter, ContextualParseError};
|
use error_reporting::{ParseErrorReporter, ContextualParseError};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use parser::ParserContext;
|
use parser::ParserContext;
|
||||||
|
@ -760,13 +760,19 @@ impl PropertyDeclarationBlock {
|
||||||
None => return Err(fmt::Error),
|
None => return Err(fmt::Error),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO(emilio): When we implement any environment variable without
|
||||||
|
// hard-coding the values we're going to need to get something
|
||||||
|
// meaningful out of here... All this code path is so terribly hacky
|
||||||
|
// ;_;.
|
||||||
|
let env = CssEnvironment;
|
||||||
|
|
||||||
let custom_properties = if let Some(cv) = computed_values {
|
let custom_properties = if let Some(cv) = computed_values {
|
||||||
// If there are extra custom properties for this declaration block,
|
// If there are extra custom properties for this declaration block,
|
||||||
// factor them in too.
|
// factor them in too.
|
||||||
if let Some(block) = custom_properties_block {
|
if let Some(block) = custom_properties_block {
|
||||||
// FIXME(emilio): This is not super-efficient here, and all this
|
// FIXME(emilio): This is not super-efficient here, and all this
|
||||||
// feels like a hack anyway...
|
// feels like a hack anyway...
|
||||||
block.cascade_custom_properties(cv.custom_properties())
|
block.cascade_custom_properties(cv.custom_properties(), &env)
|
||||||
} else {
|
} else {
|
||||||
cv.custom_properties().cloned()
|
cv.custom_properties().cloned()
|
||||||
}
|
}
|
||||||
|
@ -790,6 +796,7 @@ impl PropertyDeclarationBlock {
|
||||||
declaration.id,
|
declaration.id,
|
||||||
custom_properties.as_ref(),
|
custom_properties.as_ref(),
|
||||||
QuirksMode::NoQuirks,
|
QuirksMode::NoQuirks,
|
||||||
|
&env,
|
||||||
).to_css(dest)
|
).to_css(dest)
|
||||||
},
|
},
|
||||||
(ref d, _) => d.to_css(dest),
|
(ref d, _) => d.to_css(dest),
|
||||||
|
@ -835,17 +842,24 @@ impl PropertyDeclarationBlock {
|
||||||
&self,
|
&self,
|
||||||
context: &Context,
|
context: &Context,
|
||||||
) -> Option<Arc<::custom_properties::CustomPropertiesMap>> {
|
) -> Option<Arc<::custom_properties::CustomPropertiesMap>> {
|
||||||
self.cascade_custom_properties(context.style().custom_properties())
|
self.cascade_custom_properties(
|
||||||
|
context.style().custom_properties(),
|
||||||
|
context.device().environment(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a custom properties map which is the result of cascading custom
|
/// Returns a custom properties map which is the result of cascading custom
|
||||||
/// properties in this declaration block along with the given custom
|
/// properties in this declaration block along with the given custom
|
||||||
/// properties.
|
/// properties.
|
||||||
pub fn cascade_custom_properties(
|
fn cascade_custom_properties(
|
||||||
&self,
|
&self,
|
||||||
inherited_custom_properties: Option<&Arc<::custom_properties::CustomPropertiesMap>>,
|
inherited_custom_properties: Option<&Arc<::custom_properties::CustomPropertiesMap>>,
|
||||||
|
environment: &CssEnvironment,
|
||||||
) -> Option<Arc<::custom_properties::CustomPropertiesMap>> {
|
) -> Option<Arc<::custom_properties::CustomPropertiesMap>> {
|
||||||
let mut builder = CustomPropertiesBuilder::new(inherited_custom_properties);
|
let mut builder = CustomPropertiesBuilder::new(
|
||||||
|
inherited_custom_properties,
|
||||||
|
environment,
|
||||||
|
);
|
||||||
|
|
||||||
for declaration in self.normal_declaration_iter() {
|
for declaration in self.normal_declaration_iter() {
|
||||||
if let PropertyDeclaration::Custom(ref declaration) = *declaration {
|
if let PropertyDeclaration::Custom(ref declaration) = *declaration {
|
||||||
|
|
|
@ -519,6 +519,7 @@ impl AnimationValue {
|
||||||
declaration.id,
|
declaration.id,
|
||||||
custom_properties,
|
custom_properties,
|
||||||
context.quirks_mode,
|
context.quirks_mode,
|
||||||
|
context.device().environment(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
return AnimationValue::from_declaration(
|
return AnimationValue::from_declaration(
|
||||||
|
|
|
@ -1536,10 +1536,14 @@ impl UnparsedValue {
|
||||||
longhand_id: LonghandId,
|
longhand_id: LonghandId,
|
||||||
custom_properties: Option<<&Arc<::custom_properties::CustomPropertiesMap>>,
|
custom_properties: Option<<&Arc<::custom_properties::CustomPropertiesMap>>,
|
||||||
quirks_mode: QuirksMode,
|
quirks_mode: QuirksMode,
|
||||||
|
environment: &::custom_properties::CssEnvironment,
|
||||||
) -> PropertyDeclaration {
|
) -> PropertyDeclaration {
|
||||||
::custom_properties::substitute(&self.css, self.first_token_type, custom_properties)
|
::custom_properties::substitute(
|
||||||
.ok()
|
&self.css,
|
||||||
.and_then(|css| {
|
self.first_token_type,
|
||||||
|
custom_properties,
|
||||||
|
environment,
|
||||||
|
).ok().and_then(|css| {
|
||||||
// As of this writing, only the base URL is used for property
|
// As of this writing, only the base URL is used for property
|
||||||
// values.
|
// values.
|
||||||
//
|
//
|
||||||
|
@ -2214,11 +2218,16 @@ impl PropertyDeclaration {
|
||||||
WideKeywordDeclaration { id, keyword },
|
WideKeywordDeclaration { id, keyword },
|
||||||
)
|
)
|
||||||
}).or_else(|()| {
|
}).or_else(|()| {
|
||||||
input.look_for_var_functions();
|
input.look_for_var_or_env_functions();
|
||||||
input.parse_entirely(|input| id.parse_value(context, input))
|
input.parse_entirely(|input| id.parse_value(context, input))
|
||||||
.or_else(|err| {
|
.or_else(|err| {
|
||||||
while let Ok(_) = input.next() {} // Look for var() after the error.
|
while let Ok(_) = input.next() {} // Look for var() after the error.
|
||||||
if input.seen_var_functions() {
|
if !input.seen_var_or_env_functions() {
|
||||||
|
return Err(StyleParseErrorKind::new_invalid(
|
||||||
|
non_custom_id.unwrap().name(),
|
||||||
|
err,
|
||||||
|
));
|
||||||
|
}
|
||||||
input.reset(&start);
|
input.reset(&start);
|
||||||
let (first_token_type, css) =
|
let (first_token_type, css) =
|
||||||
::custom_properties::parse_non_custom_with_var(input).map_err(|e| {
|
::custom_properties::parse_non_custom_with_var(input).map_err(|e| {
|
||||||
|
@ -2236,12 +2245,6 @@ impl PropertyDeclaration {
|
||||||
from_shorthand: None,
|
from_shorthand: None,
|
||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
} else {
|
|
||||||
Err(StyleParseErrorKind::new_invalid(
|
|
||||||
non_custom_id.unwrap().name(),
|
|
||||||
err,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}).map(|declaration| {
|
}).map(|declaration| {
|
||||||
declarations.push(declaration)
|
declarations.push(declaration)
|
||||||
|
@ -2264,12 +2267,13 @@ impl PropertyDeclaration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
input.look_for_var_functions();
|
input.look_for_var_or_env_functions();
|
||||||
// Not using parse_entirely here: each ${shorthand.ident}::parse_into function
|
// Not using parse_entirely here: each
|
||||||
// needs to do so *before* pushing to `declarations`.
|
// ${shorthand.ident}::parse_into function needs to do so
|
||||||
|
// *before* pushing to `declarations`.
|
||||||
id.parse_into(declarations, context, input).or_else(|err| {
|
id.parse_into(declarations, context, input).or_else(|err| {
|
||||||
while let Ok(_) = input.next() {} // Look for var() after the error.
|
while let Ok(_) = input.next() {} // Look for var() after the error.
|
||||||
if !input.seen_var_functions() {
|
if !input.seen_var_or_env_functions() {
|
||||||
return Err(StyleParseErrorKind::new_invalid(
|
return Err(StyleParseErrorKind::new_invalid(
|
||||||
non_custom_id.unwrap().name(),
|
non_custom_id.unwrap().name(),
|
||||||
err,
|
err,
|
||||||
|
|
|
@ -15,7 +15,7 @@ gecko = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
app_units = "0.7"
|
app_units = "0.7"
|
||||||
cssparser = "0.24.0"
|
cssparser = "0.25"
|
||||||
bitflags = "1.0"
|
bitflags = "1.0"
|
||||||
euclid = "0.19"
|
euclid = "0.19"
|
||||||
malloc_size_of = { path = "../malloc_size_of" }
|
malloc_size_of = { path = "../malloc_size_of" }
|
||||||
|
|
|
@ -12,7 +12,7 @@ doctest = false
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "1.0"
|
byteorder = "1.0"
|
||||||
app_units = "0.7"
|
app_units = "0.7"
|
||||||
cssparser = "0.24.0"
|
cssparser = "0.25"
|
||||||
euclid = "0.19"
|
euclid = "0.19"
|
||||||
html5ever = "0.22"
|
html5ever = "0.22"
|
||||||
parking_lot = "0.6"
|
parking_lot = "0.6"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue