Bug 1462233 - Implement the env() function with hardcoded zeros for safe-area-inset. r=heycam,firefox-style-system-reviewers

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

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Emilio Cobos Álvarez 2018-11-05 10:39:46 +00:00
Родитель 4f5c3e9078
Коммит bc8766b7fc
28 изменённых файлов: 386 добавлений и 239 удалений

16
Cargo.lock сгенерированный
Просмотреть файл

@ -581,7 +581,7 @@ dependencies = [
[[package]]
name = "cssparser"
version = "0.24.1"
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)",
@ -984,7 +984,7 @@ name = "geckoservo"
version = "0.0.1"
dependencies = [
"atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.24.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cstr 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1412,7 +1412,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.1 (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",
"selectors 0.20.0",
@ -2184,7 +2184,7 @@ name = "selectors"
version = "0.20.0"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.24.1 (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.6 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2362,7 +2362,7 @@ dependencies = [
"bitflags 1.0.4 (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.1 (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)",
"fallible 0.0.1",
"fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2418,7 +2418,7 @@ version = "0.0.1"
dependencies = [
"app_units 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.24.1 (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",
@ -2431,7 +2431,7 @@ name = "stylo_tests"
version = "0.0.1"
dependencies = [
"atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.24.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cstr 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
"geckoservo 0.0.1",
@ -3175,7 +3175,7 @@ dependencies = [
"checksum crossbeam-epoch 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2af0e75710d6181e234c8ecc79f14a97907850a541b13b0be1dd10992f2e4620"
"checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9"
"checksum crossbeam-utils 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d636a8b3bcc1b409d7ffd3facef8f21dcb4009626adbd0c5e6c4305c07253c7b"
"checksum cssparser 0.24.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b200a7193703a615c8d2751fed1ede39b9c4b3905e09d1ec7064a24688c190fc"
"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 cstr 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b6557bdb1dc9647eae1cf7f5601b14cd45fc3c7ccf2df618387416fe542da6ea"
"checksum cstr-macros 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0472c17c83d3ec1af32fb6ee2b3ad56ae0b6e69355d63d1d30602055c34324a8"

Просмотреть файл

@ -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.10", optional = true }

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -21,6 +21,59 @@ 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.
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 +101,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>,
}
@ -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 {
fn empty() -> Self {
Self {
@ -223,6 +290,7 @@ impl VariableValue {
last_token_type: TokenSerializationType::nothing(),
first_token_type: TokenSerializationType::nothing(),
references: PrecomputedHashSet::default(),
references_environment: false,
}
}
@ -273,7 +341,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 +350,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 +366,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 +386,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 +403,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 +476,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 +543,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 +596,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 +635,31 @@ 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 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 {
CSSWideKeyword::Initial => {
@ -553,11 +671,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 +710,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 +719,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,7 +729,7 @@ 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
@ -664,6 +777,8 @@ fn substitute_all(custom_properties_map: &mut CustomPropertiesMap) {
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
@ -686,11 +801,23 @@ 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;
}
// This variable has already been resolved.
if context.invalid.contains(&name) {
return None;
}
// Whether this variable has been visited in this traversal.
let key;
match context.index_map.entry(name) {
@ -702,12 +829,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.
@ -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
// 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.invalid,
&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.invalid.insert(name);
},
}
// All resolved, so return the signal value.
@ -834,6 +950,7 @@ fn substitute_all(custom_properties_map: &mut CustomPropertiesMap) {
var_info: SmallVec::new(),
map: custom_properties_map,
invalid: &mut invalid,
environment,
};
traverse(name, &mut context);
}
@ -841,25 +958,51 @@ fn substitute_all(custom_properties_map: &mut CustomPropertiesMap) {
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.
///
/// 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,
invalid_custom_properties: &PrecomputedHashSet<Name>,
env: &CssEnvironment,
) -> Result<TokenSerializationType, ParseError<'i>> {
let mut last_token_type = TokenSerializationType::nothing();
let mut set_position_at_next_iteration = false;
loop {
@ -883,27 +1026,49 @@ 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 {
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`
// 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 +1078,31 @@ where
input,
&mut position,
partial_computed_value,
substitute_one,
custom_properties,
invalid_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,
invalid_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 +1118,32 @@ 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 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(
&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,
&invalid,
env,
)?;
substituted.push_from(position, &input, last_token_type);
Ok(substituted.css)

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -519,6 +519,7 @@ impl AnimationValue {
declaration.id,
custom_properties,
context.quirks_mode,
context.device().environment(),
)
};
return AnimationValue::from_declaration(

Просмотреть файл

@ -1536,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.
//
@ -2214,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)
@ -2264,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,

Просмотреть файл

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

Просмотреть файл

@ -14,7 +14,7 @@ gecko_debug = ["style/gecko_debug", "nsstring/gecko_debug"]
[dependencies]
atomic_refcell = "0.1"
cssparser = "0.24.0"
cssparser = "0.25"
cstr = "0.1.2"
libc = "0.2"
log = {version = "0.4", features = ["release_max_level_info"]}

Просмотреть файл

@ -13,7 +13,7 @@ doctest = false
[dependencies]
atomic_refcell = "0.1"
cssparser = "0.24.0"
cssparser = "0.25"
cstr = "0.1.2"
env_logger = { version = "0.5", default-features = false }
geckoservo = {path = "../../../ports/geckolib"}

Просмотреть файл

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

Просмотреть файл

@ -1,4 +0,0 @@
[at-supports.tentative.html]
[Test that CSS env vars work with @support]
expected: FAIL

Просмотреть файл

@ -1,4 +0,0 @@
[env-in-custom-properties.tentative.html]
[Test env() will work in custom properties]
expected: FAIL

Просмотреть файл

@ -1,4 +0,0 @@
[fallback-nested-var.tentative.html]
[Test that nested var() fallback values work with CSS env vars]
expected: FAIL

Просмотреть файл

@ -1,4 +0,0 @@
[seralization-round-tripping.tentative.html]
[Test style seralization round tripping with CSS env vars]
expected: FAIL

Просмотреть файл

@ -1,4 +0,0 @@
[supports-script.tentative.html]
[Test that CSS env vars work with CSS.supports]
expected: FAIL

Просмотреть файл

@ -1,43 +0,0 @@
[syntax.tentative.html]
[background-color: env(test) rgba(0, 0, 0, 0)]
expected: FAIL
[background-color: ENV(test) rgba(0, 0, 0, 0)]
expected: FAIL
[background-color: env(test) !important rgba(0, 0, 0, 0)]
expected: FAIL
[background-color: env(test, 10px) rgba(0, 0, 0, 0)]
expected: FAIL
[background-color: env(test, blue) rgb(0, 0, 255)]
expected: FAIL
[background-color: env(test, env(another)) rgba(0, 0, 0, 0)]
expected: FAIL
[background-color: env(test, env(another, blue)) rgb(0, 0, 255)]
expected: FAIL
[background-color: env(-test) rgba(0, 0, 0, 0)]
expected: FAIL
[background-color: env(--test) rgba(0, 0, 0, 0)]
expected: FAIL
[background-color: env( test) rgba(0, 0, 0, 0)]
expected: FAIL
[background-color: env(test ) rgba(0, 0, 0, 0)]
expected: FAIL
[background-color: env( test ) rgba(0, 0, 0, 0)]
expected: FAIL
[background-color: env(test /**/, blue) rgb(0, 0, 255)]
expected: FAIL
[background-color: env(test, {}) rgba(0, 0, 0, 0)]
expected: FAIL

Просмотреть файл

@ -1,4 +0,0 @@
[unknown-env-names-override-previous.tentative.html]
[Test unknown env() names will override previous values]
expected: FAIL

Просмотреть файл

@ -1 +1 @@
{"files":{"Cargo.toml":"8329b9e0771dc3e34acca27dbb047f397e3bb30e6d0a69444ce9a2ad0b0fc2cd","LICENSE":"fab3dd6bdab226f1c08630b1dd917e11fcb4ec5e1e020e2c16f83a0a13863e85","README.md":"c5781e673335f37ed3d7acb119f8ed33efdf6eb75a7094b7da2abe0c3230adb8","build.rs":"ce686e87cccb6aa85a8cd34688d809398c5a624f179fd9a172d1049892da3f4c","build/match_byte.rs":"31905ae3dba69fa82c1f13069df4cd056bb340d59ee5d177679425f105f203cf","docs/404.html":"025861f76f8d1f6d67c20ab624c6e418f4f824385e2dd8ad8732c4ea563c6a2e","docs/index.html":"025861f76f8d1f6d67c20ab624c6e418f4f824385e2dd8ad8732c4ea563c6a2e","src/color.rs":"c60f1b0ab7a2a6213e434604ee33f78e7ef74347f325d86d0b9192d8225ae1cc","src/cow_rc_str.rs":"541216f8ef74ee3cc5cbbc1347e5f32ed66588c401851c9a7d68b867aede1de0","src/from_bytes.rs":"331fe63af2123ae3675b61928a69461b5ac77799fff3ce9978c55cf2c558f4ff","src/lib.rs":"a474ee88ef8f73fcb7b7272d426e5eafb4ad10d104797a5a188d1676c8180972","src/macros.rs":"adb9773c157890381556ea83d7942dcc676f99eea71abbb6afeffee1e3f28960","src/nth.rs":"5c70fb542d1376cddab69922eeb4c05e4fcf8f413f27563a2af50f72a47c8f8c","src/parser.rs":"9ed4aec998221eb2d2ba99db2f9f82a02399fb0c3b8500627f68f5aab872adde","src/rules_and_declarations.rs":"622ce07c117a511d40ce595602d4f4730659a59273388f28553d1a2b0fac92ce","src/serializer.rs":"3e2dfc60613f885cb6f99abfc854fde2a1e00de507431bd2e51178b61abfd69b","src/size_of_tests.rs":"e5f63c8c18721cc3ff7a5407e84f9889ffa10e66da96e8510a696c3e00ad72d5","src/tests.rs":"4a9223b9d2dc982144499aee497515553fc3d9ec86ca7b2e62b6caa5d4a11570","src/tokenizer.rs":"429b2cba419cf8b923fbcc32d3bd34c0b39284ebfcb9fc29b8eb8643d8d5f312","src/unicode_range.rs":"ae159d2ebe4123a6666e18dc0362f89b475240a6b7ed5fb6fe21b9e7a4139da8"},"package":"b200a7193703a615c8d2751fed1ede39b9c4b3905e09d1ec7064a24688c190fc"}
{"files":{"Cargo.toml":"47497bde56f31c8a24665d840fbe5b03f14324dd06a68f907e419f8e7a855186","LICENSE":"fab3dd6bdab226f1c08630b1dd917e11fcb4ec5e1e020e2c16f83a0a13863e85","README.md":"c5781e673335f37ed3d7acb119f8ed33efdf6eb75a7094b7da2abe0c3230adb8","build.rs":"310d6d7b1931ff783a8aa1a4c6baee87b4c9130c858e4694ef69cc96df5e38dc","build/match_byte.rs":"31905ae3dba69fa82c1f13069df4cd056bb340d59ee5d177679425f105f203cf","docs/404.html":"025861f76f8d1f6d67c20ab624c6e418f4f824385e2dd8ad8732c4ea563c6a2e","docs/index.html":"025861f76f8d1f6d67c20ab624c6e418f4f824385e2dd8ad8732c4ea563c6a2e","src/color.rs":"c60f1b0ab7a2a6213e434604ee33f78e7ef74347f325d86d0b9192d8225ae1cc","src/cow_rc_str.rs":"541216f8ef74ee3cc5cbbc1347e5f32ed66588c401851c9a7d68b867aede1de0","src/from_bytes.rs":"331fe63af2123ae3675b61928a69461b5ac77799fff3ce9978c55cf2c558f4ff","src/lib.rs":"a474ee88ef8f73fcb7b7272d426e5eafb4ad10d104797a5a188d1676c8180972","src/macros.rs":"adb9773c157890381556ea83d7942dcc676f99eea71abbb6afeffee1e3f28960","src/nth.rs":"5c70fb542d1376cddab69922eeb4c05e4fcf8f413f27563a2af50f72a47c8f8c","src/parser.rs":"a4ec0bd1b5eab6632cf1985701a7ea641fe7f7bbcc0a2bd33f924ae6228591a5","src/rules_and_declarations.rs":"622ce07c117a511d40ce595602d4f4730659a59273388f28553d1a2b0fac92ce","src/serializer.rs":"3e2dfc60613f885cb6f99abfc854fde2a1e00de507431bd2e51178b61abfd69b","src/size_of_tests.rs":"e5f63c8c18721cc3ff7a5407e84f9889ffa10e66da96e8510a696c3e00ad72d5","src/tests.rs":"9d08b3943d453664e01d58e307f79345e240f9f9ce6f8d36a842eff37155563e","src/tokenizer.rs":"adcf5811955e8df57a519e3d1e44fe3afeb5afeb1076daeb8d36fed1abcf1327","src/unicode_range.rs":"ae159d2ebe4123a6666e18dc0362f89b475240a6b7ed5fb6fe21b9e7a4139da8"},"package":"730363a45c4e248d4f21d3e5c1156d1a9cdec0855056c0d9539e814bc59865c3"}

2
third_party/rust/cssparser/Cargo.toml поставляемый
Просмотреть файл

@ -12,7 +12,7 @@
[package]
name = "cssparser"
version = "0.24.1"
version = "0.25.0"
authors = ["Simon Sapin <simon.sapin@exyr.org>"]
build = "build.rs"
exclude = ["src/css-parsing-tests/**", "src/big-data-url.css"]

4
third_party/rust/cssparser/build.rs поставляемый
Просмотреть файл

@ -8,10 +8,6 @@ extern crate quote;
extern crate syn;
extern crate proc_macro2;
use std::env;
use std::path::Path;
#[cfg(feature = "dummy_match_byte")]
mod codegen {
use std::path::Path;

16
third_party/rust/cssparser/src/parser.rs поставляемый
Просмотреть файл

@ -470,17 +470,19 @@ impl<'i: 't, 't> Parser<'i, 't> {
self.at_start_of = state.at_start_of;
}
/// Start looking for `var()` functions. (See the `.seen_var_functions()` method.)
/// Start looking for `var()` / `env()` functions. (See the
/// `.seen_var_or_env_functions()` method.)
#[inline]
pub fn look_for_var_functions(&mut self) {
self.input.tokenizer.look_for_var_functions()
pub fn look_for_var_or_env_functions(&mut self) {
self.input.tokenizer.look_for_var_or_env_functions()
}
/// Return whether a `var()` function has been seen by the tokenizer since
/// either `look_for_var_functions` was called, and stop looking.
/// Return whether a `var()` or `env()` function has been seen by the
/// tokenizer since either `look_for_var_or_env_functions` was called, and
/// stop looking.
#[inline]
pub fn seen_var_functions(&mut self) -> bool {
self.input.tokenizer.seen_var_functions()
pub fn seen_var_or_env_functions(&mut self) -> bool {
self.input.tokenizer.seen_var_or_env_functions()
}
/// Execute the given closure, passing it the parser.

6
third_party/rust/cssparser/src/tests.rs поставляемый
Просмотреть файл

@ -685,14 +685,14 @@ fn unquoted_url(b: &mut Bencher) {
b.iter(|| {
let mut input = ParserInput::new(BACKGROUND_IMAGE);
let mut input = Parser::new(&mut input);
input.look_for_var_functions();
input.look_for_var_or_env_functions();
let result = input.try(|input| input.expect_url());
assert!(result.is_ok());
input.seen_var_functions();
(result.is_ok(), input.seen_var_functions())
input.seen_var_or_env_functions();
(result.is_ok(), input.seen_var_or_env_functions())
})
}

22
third_party/rust/cssparser/src/tokenizer.rs поставляемый
Просмотреть файл

@ -208,7 +208,7 @@ pub struct Tokenizer<'a> {
/// of UTF-16 characters.
current_line_start_position: usize,
current_line_number: u32,
var_functions: SeenStatus,
var_or_env_functions: SeenStatus,
source_map_url: Option<&'a str>,
source_url: Option<&'a str>,
}
@ -234,29 +234,31 @@ impl<'a> Tokenizer<'a> {
position: 0,
current_line_start_position: 0,
current_line_number: first_line_number,
var_functions: SeenStatus::DontCare,
var_or_env_functions: SeenStatus::DontCare,
source_map_url: None,
source_url: None,
}
}
#[inline]
pub fn look_for_var_functions(&mut self) {
self.var_functions = SeenStatus::LookingForThem;
pub fn look_for_var_or_env_functions(&mut self) {
self.var_or_env_functions = SeenStatus::LookingForThem;
}
#[inline]
pub fn seen_var_functions(&mut self) -> bool {
let seen = self.var_functions == SeenStatus::SeenAtLeastOne;
self.var_functions = SeenStatus::DontCare;
pub fn seen_var_or_env_functions(&mut self) -> bool {
let seen = self.var_or_env_functions == SeenStatus::SeenAtLeastOne;
self.var_or_env_functions = SeenStatus::DontCare;
seen
}
#[inline]
pub fn see_function(&mut self, name: &str) {
if self.var_functions == SeenStatus::LookingForThem {
if name.eq_ignore_ascii_case("var") {
self.var_functions = SeenStatus::SeenAtLeastOne;
if self.var_or_env_functions == SeenStatus::LookingForThem {
if name.eq_ignore_ascii_case("var") ||
name.eq_ignore_ascii_case("env")
{
self.var_or_env_functions = SeenStatus::SeenAtLeastOne;
}
}
}

Просмотреть файл

@ -996,6 +996,10 @@ STATIC_ATOMS = [
Atom("rubyTextContainer", "ruby-text-container"),
Atom("rules", "rules"),
Atom("s", "s"),
Atom("safe_area_inset_top", "safe-area-inset-top"),
Atom("safe_area_inset_bottom", "safe-area-inset-bottom"),
Atom("safe_area_inset_left", "safe-area-inset-left"),
Atom("safe_area_inset_right", "safe-area-inset-right"),
Atom("samp", "samp"),
Atom("sandbox", "sandbox"),
Atom("sbattr", "sbattr"),