summaryrefslogtreecommitdiffstats
path: root/servo/components/style/custom_properties.rs
diff options
context:
space:
mode:
Diffstat (limited to 'servo/components/style/custom_properties.rs')
-rw-r--r--servo/components/style/custom_properties.rs317
1 files changed, 211 insertions, 106 deletions
diff --git a/servo/components/style/custom_properties.rs b/servo/components/style/custom_properties.rs
index cb3b9685ae..b6523dd489 100644
--- a/servo/components/style/custom_properties.rs
+++ b/servo/components/style/custom_properties.rs
@@ -15,7 +15,10 @@ use crate::properties::{
};
use crate::properties_and_values::{
registry::PropertyRegistrationData,
- value::{AllowComputationallyDependent, SpecifiedValue as SpecifiedRegisteredValue},
+ value::{
+ AllowComputationallyDependent, ComputedValue as ComputedRegisteredValue,
+ SpecifiedValue as SpecifiedRegisteredValue,
+ },
};
use crate::selector_map::{PrecomputedHashMap, PrecomputedHashSet};
use crate::stylesheets::UrlExtraData;
@@ -122,7 +125,12 @@ macro_rules! lnf_int_variable {
}};
}
-static CHROME_ENVIRONMENT_VARIABLES: [EnvironmentVariable; 7] = [
+static CHROME_ENVIRONMENT_VARIABLES: [EnvironmentVariable; 8] = [
+ lnf_int_variable!(
+ atom!("-moz-gtk-csd-titlebar-button-spacing"),
+ TitlebarButtonSpacing,
+ int_pixels
+ ),
lnf_int_variable!(
atom!("-moz-gtk-csd-titlebar-radius"),
TitlebarRadius,
@@ -245,7 +253,7 @@ impl ComputedCustomProperties {
}
/// Return the name and value of the property at specified index, if any.
- pub fn property_at(&self, index: usize) -> Option<(&Name, &Option<Arc<VariableValue>>)> {
+ pub fn property_at(&self, index: usize) -> Option<(&Name, &Option<ComputedRegisteredValue>)> {
// Just expose the custom property items from custom_properties.inherited, followed
// by custom property items from custom_properties.non_inherited.
self.inherited
@@ -259,7 +267,7 @@ impl ComputedCustomProperties {
&mut self,
registration: &PropertyRegistrationData,
name: &Name,
- value: Arc<VariableValue>,
+ value: ComputedRegisteredValue,
) {
self.map_mut(registration).insert(name, value)
}
@@ -288,7 +296,7 @@ impl ComputedCustomProperties {
&self,
registration: &PropertyRegistrationData,
name: &Name,
- ) -> Option<&Arc<VariableValue>> {
+ ) -> Option<&ComputedRegisteredValue> {
if registration.inherits() {
self.inherited.get(name)
} else {
@@ -669,7 +677,9 @@ fn parse_declaration_value_block<'i, 't>(
let mut prev_reference_index: Option<usize> = None;
loop {
let token_start = input.position();
- let Ok(token) = input.next_including_whitespace_and_comments() else { break };
+ let Ok(token) = input.next_including_whitespace_and_comments() else {
+ break;
+ };
let prev_token_type = last_token_type;
let serialization_type = token.serialization_type();
@@ -809,7 +819,8 @@ fn parse_declaration_value_block<'i, 't>(
check_closed!(")");
prev_reference_index = Some(our_ref_index);
let reference = &mut references.refs[our_ref_index];
- reference.end = input.position().byte_index() - input_start.byte_index() + missing_closing_characters.len();
+ reference.end = input.position().byte_index() - input_start.byte_index() +
+ missing_closing_characters.len();
reference.fallback = fallback;
if is_var {
references.any_var = true;
@@ -885,7 +896,11 @@ impl<'a, 'b: 'a> CustomPropertiesBuilder<'a, 'b> {
/// Create a new builder, inheriting from a given custom properties map.
///
/// We expose this publicly mostly for @keyframe blocks.
- pub fn new_with_properties(stylist: &'a Stylist, custom_properties: ComputedCustomProperties, computed_context: &'a mut computed::Context<'b>) -> Self {
+ pub fn new_with_properties(
+ stylist: &'a Stylist,
+ custom_properties: ComputedCustomProperties,
+ computed_context: &'a mut computed::Context<'b>,
+ ) -> Self {
Self {
seen: PrecomputedHashSet::default(),
reverted: Default::default(),
@@ -915,7 +930,9 @@ impl<'a, 'b: 'a> CustomPropertiesBuilder<'a, 'b> {
// Reuse flags from computing registered custom properties initial values, such as
// whether they depend on viewport units.
- context.style().add_flags(stylist.get_custom_property_initial_values_flags());
+ context
+ .style()
+ .add_flags(stylist.get_custom_property_initial_values_flags());
Self::new_with_properties(stylist, properties, context)
}
@@ -943,8 +960,8 @@ impl<'a, 'b: 'a> CustomPropertiesBuilder<'a, 'b> {
let map = &mut self.custom_properties;
let registration = self.stylist.get_custom_property_registration(&name);
- match *value {
- CustomDeclarationValue::Value(ref unparsed_value) => {
+ match value {
+ CustomDeclarationValue::Value(unparsed_value) => {
let has_custom_property_references = unparsed_value.references.any_var;
let registered_length_property =
registration.syntax.may_reference_font_relative_length();
@@ -970,21 +987,19 @@ impl<'a, 'b: 'a> CustomPropertiesBuilder<'a, 'b> {
self.computed_context,
);
}
- map.insert(registration, name, Arc::clone(unparsed_value));
+ let value = ComputedRegisteredValue::universal(Arc::clone(unparsed_value));
+ map.insert(registration, name, value);
},
CustomDeclarationValue::CSSWideKeyword(keyword) => match keyword {
CSSWideKeyword::RevertLayer | CSSWideKeyword::Revert => {
- let origin_revert = keyword == CSSWideKeyword::Revert;
+ let origin_revert = matches!(keyword, CSSWideKeyword::Revert);
self.seen.remove(name);
self.reverted.insert(name, (priority, origin_revert));
},
CSSWideKeyword::Initial => {
// For non-inherited custom properties, 'initial' was handled in value_may_affect_style.
debug_assert!(registration.inherits(), "Should've been handled earlier");
- map.remove(registration, name);
- if let Some(ref initial_value) = registration.initial_value {
- map.insert(registration, name, initial_value.clone());
- }
+ remove_and_insert_initial_value(name, registration, map);
},
CSSWideKeyword::Inherit => {
// For inherited custom properties, 'inherit' was handled in value_may_affect_style.
@@ -1006,7 +1021,11 @@ impl<'a, 'b: 'a> CustomPropertiesBuilder<'a, 'b> {
/// Note a non-custom property with variable reference that may in turn depend on that property.
/// e.g. `font-size` depending on a custom property that may be a registered property using `em`.
- pub fn note_potentially_cyclic_non_custom_dependency(&mut self, id: LonghandId, decl: &VariableDeclaration) {
+ pub fn note_potentially_cyclic_non_custom_dependency(
+ &mut self,
+ id: LonghandId,
+ decl: &VariableDeclaration,
+ ) {
// With unit algebra in `calc()`, references aren't limited to `font-size`.
// For example, `--foo: 100ex; font-weight: calc(var(--foo) / 1ex);`,
// or `--foo: 1em; zoom: calc(var(--foo) * 30px / 2em);`
@@ -1020,8 +1039,7 @@ impl<'a, 'b: 'a> CustomPropertiesBuilder<'a, 'b> {
},
LonghandId::LineHeight => {
if self.computed_context.is_root_element() {
- NonCustomReferences::ROOT_LH_UNITS |
- NonCustomReferences::ROOT_FONT_UNITS
+ NonCustomReferences::ROOT_LH_UNITS | NonCustomReferences::ROOT_FONT_UNITS
} else {
NonCustomReferences::LH_UNITS | NonCustomReferences::FONT_UNITS
}
@@ -1033,15 +1051,24 @@ impl<'a, 'b: 'a> CustomPropertiesBuilder<'a, 'b> {
return;
}
- let variables: Vec<Atom> = refs.refs.iter().filter_map(|reference| {
- if !reference.is_var {
- return None;
- }
- if !self.stylist.get_custom_property_registration(&reference.name).syntax.may_compute_length() {
- return None;
- }
- Some(reference.name.clone())
- }).collect();
+ let variables: Vec<Atom> = refs
+ .refs
+ .iter()
+ .filter_map(|reference| {
+ if !reference.is_var {
+ return None;
+ }
+ if !self
+ .stylist
+ .get_custom_property_registration(&reference.name)
+ .syntax
+ .may_compute_length()
+ {
+ return None;
+ }
+ Some(reference.name.clone())
+ })
+ .collect();
references.for_each(|idx| {
let entry = &mut self.references_from_non_custom_properties[idx];
let was_none = entry.is_none();
@@ -1098,8 +1125,12 @@ impl<'a, 'b: 'a> CustomPropertiesBuilder<'a, 'b> {
debug_assert!(registration.inherits(), "Should've been handled earlier");
// Don't bother overwriting an existing value with the initial value specified in
// the registration.
- if Some(existing_value) == registration.initial_value.as_ref() {
- return false;
+ if let Some(initial_value) = self
+ .stylist
+ .get_custom_property_initial_values()
+ .get(registration, name)
+ {
+ return existing_value != initial_value;
}
},
(Some(_), &CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Inherit)) => {
@@ -1120,8 +1151,16 @@ impl<'a, 'b: 'a> CustomPropertiesBuilder<'a, 'b> {
(Some(existing_value), &CustomDeclarationValue::Value(ref value)) => {
// Don't bother overwriting an existing value with the same
// specified value.
- if existing_value == value {
- return false;
+ if let Some(existing_value) = existing_value.as_universal() {
+ return existing_value != value;
+ }
+ if let Ok(value) = compute_value(
+ &value.css,
+ &value.url_data,
+ registration,
+ self.computed_context,
+ ) {
+ return existing_value.v != value.v;
}
},
_ => {},
@@ -1164,7 +1203,8 @@ impl<'a, 'b: 'a> CustomPropertiesBuilder<'a, 'b> {
self.stylist,
self.computed_context,
);
- self.computed_context.builder.invalid_non_custom_properties = invalid_non_custom_properties;
+ self.computed_context.builder.invalid_non_custom_properties =
+ invalid_non_custom_properties;
}
self.custom_properties.shrink_to_fit();
@@ -1178,7 +1218,8 @@ impl<'a, 'b: 'a> CustomPropertiesBuilder<'a, 'b> {
inherited: if self
.computed_context
.inherited_custom_properties()
- .inherited == self.custom_properties.inherited
+ .inherited ==
+ self.custom_properties.inherited
{
self.computed_context
.inherited_custom_properties()
@@ -1217,6 +1258,9 @@ impl<'a, 'b: 'a> CustomPropertiesBuilder<'a, 'b> {
// have to worry about resolving in a wrong order.
for (k, v) in deferred.iter() {
let Some(v) = v else { continue };
+ let Some(v) = v.as_universal() else {
+ unreachable!("Computing should have been deferred!")
+ };
substitute_references_if_needed_and_apply(
k,
v,
@@ -1340,6 +1384,7 @@ fn substitute_all(
VarType::Custom(ref name) => {
let registration = context.stylist.get_custom_property_registration(name);
let value = context.map.get(registration, name)?;
+ let value = value.as_universal()?;
let non_custom_references = value
.references
@@ -1476,22 +1521,16 @@ fn substitute_all(
.invalid_non_custom_properties
.insert(LonghandId::FontSize);
}
- if context.non_custom_references.intersects(
- NonCustomReferences::LH_UNITS |
- NonCustomReferences::ROOT_LH_UNITS,
- ) {
+ if context
+ .non_custom_references
+ .intersects(NonCustomReferences::LH_UNITS | NonCustomReferences::ROOT_LH_UNITS)
+ {
context
.invalid_non_custom_properties
.insert(LonghandId::LineHeight);
}
// This variable is in loop. Resolve to invalid.
- handle_invalid_at_computed_value_time(
- name,
- context.map,
- context.computed_context.inherited_custom_properties(),
- context.stylist,
- context.computed_context.is_root_element(),
- );
+ handle_invalid_at_computed_value_time(name, context.map, context.computed_context);
};
loop {
let var_index = context
@@ -1534,7 +1573,7 @@ fn substitute_all(
return None;
}
- if let Some(ref v) = value.as_ref() {
+ if let Some(ref v) = value {
let registration = context.stylist.get_custom_property_registration(&name);
let registered_length_property =
registration.syntax.may_reference_font_relative_length();
@@ -1542,7 +1581,8 @@ fn substitute_all(
if !context.non_custom_references.is_empty() && registered_length_property {
if let Some(deferred) = &mut context.deferred_properties {
// This property directly depends on a non-custom property, defer resolving it.
- deferred.insert(registration, &name, (*v).clone());
+ let deferred_property = ComputedRegisteredValue::universal(Arc::clone(v));
+ deferred.insert(registration, &name, deferred_property);
context.map.remove(registration, &name);
defer = true;
}
@@ -1553,11 +1593,14 @@ fn substitute_all(
continue;
}
if let Some(deferred) = &mut context.deferred_properties {
- let registration =
- context.stylist.get_custom_property_registration(&reference.name);
+ let registration = context
+ .stylist
+ .get_custom_property_registration(&reference.name);
if deferred.get(registration, &reference.name).is_some() {
// This property depends on a custom property that depends on a non-custom property, defer.
- deferred.insert(registration, &name, Arc::clone(v));
+ let deferred_property =
+ ComputedRegisteredValue::universal(Arc::clone(v));
+ deferred.insert(registration, &name, deferred_property);
context.map.remove(registration, &name);
defer = true;
break;
@@ -1610,22 +1653,27 @@ fn substitute_all(
fn handle_invalid_at_computed_value_time(
name: &Name,
custom_properties: &mut ComputedCustomProperties,
- inherited: &ComputedCustomProperties,
- stylist: &Stylist,
- is_root_element: bool,
+ computed_context: &computed::Context,
) {
+ let stylist = computed_context.style().stylist.unwrap();
let registration = stylist.get_custom_property_registration(&name);
if !registration.syntax.is_universal() {
// For the root element, inherited maps are empty. We should just
// use the initial value if any, rather than removing the name.
- if registration.inherits() && !is_root_element {
+ if registration.inherits() && !computed_context.is_root_element() {
+ let inherited = computed_context.inherited_custom_properties();
if let Some(value) = inherited.get(registration, name) {
- custom_properties.insert(registration, name, Arc::clone(value));
+ custom_properties.insert(registration, name, value.clone());
return;
}
- } else {
- if let Some(ref initial_value) = registration.initial_value {
- custom_properties.insert(registration, name, Arc::clone(initial_value));
+ } else if let Some(ref initial_value) = registration.initial_value {
+ if let Ok(initial_value) = compute_value(
+ &initial_value.css,
+ &initial_value.url_data,
+ registration,
+ computed_context,
+ ) {
+ custom_properties.insert(registration, name, initial_value);
return;
}
}
@@ -1644,35 +1692,43 @@ fn substitute_references_if_needed_and_apply(
let registration = stylist.get_custom_property_registration(&name);
if !value.has_references() && registration.syntax.is_universal() {
// Trivial path: no references and no need to compute the value, just apply it directly.
- custom_properties.insert(registration, name, Arc::clone(value));
+ let computed_value = ComputedRegisteredValue::universal(Arc::clone(value));
+ custom_properties.insert(registration, name, computed_value);
return;
}
let inherited = computed_context.inherited_custom_properties();
- let value = match substitute_internal(value, custom_properties, stylist, registration, computed_context) {
+ let url_data = &value.url_data;
+ let value = match substitute_internal(
+ value,
+ custom_properties,
+ stylist,
+ registration,
+ computed_context,
+ ) {
Ok(v) => v,
Err(..) => {
- handle_invalid_at_computed_value_time(
- name,
- custom_properties,
- inherited,
- stylist,
- computed_context.is_root_element(),
- );
+ handle_invalid_at_computed_value_time(name, custom_properties, computed_context);
return;
},
- }.into_value(&value.url_data);
+ }
+ .into_value(url_data);
// If variable fallback results in a wide keyword, deal with it now.
{
- let mut input = ParserInput::new(&value.css);
+ let css = value.to_variable_value().css;
+ let mut input = ParserInput::new(&css);
let mut input = Parser::new(&mut input);
if let Ok(kw) = input.try_parse(CSSWideKeyword::parse) {
// TODO: It's unclear what this should do for revert / revert-layer, see
// https://github.com/w3c/csswg-drafts/issues/9131. For now treating as unset
// seems fine?
- match (kw, registration.inherits(), computed_context.is_root_element()) {
+ match (
+ kw,
+ registration.inherits(),
+ computed_context.is_root_element(),
+ ) {
(CSSWideKeyword::Initial, _, _) |
(CSSWideKeyword::Revert, false, _) |
(CSSWideKeyword::RevertLayer, false, _) |
@@ -1681,10 +1737,7 @@ fn substitute_references_if_needed_and_apply(
(CSSWideKeyword::RevertLayer, true, true) |
(CSSWideKeyword::Unset, true, true) |
(CSSWideKeyword::Inherit, _, true) => {
- custom_properties.remove(registration, name);
- if let Some(ref initial_value) = registration.initial_value {
- custom_properties.insert(registration, name, Arc::clone(initial_value));
- }
+ remove_and_insert_initial_value(name, registration, custom_properties);
},
(CSSWideKeyword::Revert, true, false) |
(CSSWideKeyword::RevertLayer, true, false) |
@@ -1692,7 +1745,7 @@ fn substitute_references_if_needed_and_apply(
(CSSWideKeyword::Unset, true, false) => {
match inherited.get(registration, name) {
Some(value) => {
- custom_properties.insert(registration, name, Arc::clone(value));
+ custom_properties.insert(registration, name, value.clone());
},
None => {
custom_properties.remove(registration, name);
@@ -1704,45 +1757,81 @@ fn substitute_references_if_needed_and_apply(
}
}
- custom_properties.insert(registration, name, Arc::new(value));
+ custom_properties.insert(registration, name, value);
+}
+
+enum Substitution<'a> {
+ Universal(UniversalSubstitution<'a>),
+ Computed(ComputedRegisteredValue),
+}
+
+impl<'a> Default for Substitution<'a> {
+ fn default() -> Self {
+ Self::Universal(UniversalSubstitution::default())
+ }
}
#[derive(Default)]
-struct Substitution<'a> {
+struct UniversalSubstitution<'a> {
css: Cow<'a, str>,
first_token_type: TokenSerializationType,
last_token_type: TokenSerializationType,
}
+impl<'a> UniversalSubstitution<'a> {
+ fn from_value(v: VariableValue) -> Self {
+ UniversalSubstitution {
+ css: Cow::from(v.css),
+ first_token_type: v.first_token_type,
+ last_token_type: v.last_token_type,
+ }
+ }
+}
+
impl<'a> Substitution<'a> {
fn new(
css: &'a str,
first_token_type: TokenSerializationType,
last_token_type: TokenSerializationType,
) -> Self {
- Self {
+ Self::Universal(UniversalSubstitution {
css: Cow::Borrowed(css),
first_token_type,
last_token_type,
- }
+ })
}
- fn from_value(v: VariableValue) -> Substitution<'static> {
- debug_assert!(!v.has_references(), "Computed values shouldn't have references");
- Substitution {
- css: Cow::from(v.css),
- first_token_type: v.first_token_type,
- last_token_type: v.last_token_type,
+ fn into_universal(self) -> UniversalSubstitution<'a> {
+ match self {
+ Substitution::Universal(substitution) => substitution,
+ Substitution::Computed(computed) => {
+ UniversalSubstitution::from_value(computed.to_variable_value())
+ },
}
}
- fn into_value(self, url_data: &UrlExtraData) -> VariableValue {
- VariableValue {
- css: self.css.into_owned(),
- first_token_type: self.first_token_type,
- last_token_type: self.last_token_type,
- url_data: url_data.clone(),
- references: Default::default(),
+ fn from_value(v: VariableValue) -> Self {
+ debug_assert!(
+ !v.has_references(),
+ "Computed values shouldn't have references"
+ );
+ let substitution = UniversalSubstitution::from_value(v);
+ Self::Universal(substitution)
+ }
+
+ fn into_value(self, url_data: &UrlExtraData) -> ComputedRegisteredValue {
+ match self {
+ Substitution::Universal(substitution) => {
+ let value = Arc::new(VariableValue {
+ css: substitution.css.into_owned(),
+ first_token_type: substitution.first_token_type,
+ last_token_type: substitution.last_token_type,
+ url_data: url_data.clone(),
+ references: Default::default(),
+ });
+ ComputedRegisteredValue::universal(value)
+ },
+ Substitution::Computed(computed) => computed,
}
}
}
@@ -1752,20 +1841,32 @@ fn compute_value(
url_data: &UrlExtraData,
registration: &PropertyRegistrationData,
computed_context: &computed::Context,
-) -> Result<Substitution<'static>, ()> {
+) -> Result<ComputedRegisteredValue, ()> {
debug_assert!(!registration.syntax.is_universal());
let mut input = ParserInput::new(&css);
let mut input = Parser::new(&mut input);
- let value = SpecifiedRegisteredValue::compute(
+ SpecifiedRegisteredValue::compute(
&mut input,
registration,
url_data,
computed_context,
AllowComputationallyDependent::Yes,
- )?;
- Ok(Substitution::from_value(value))
+ )
+}
+
+/// Removes the named registered custom property and inserts its uncomputed initial value.
+fn remove_and_insert_initial_value(
+ name: &Name,
+ registration: &PropertyRegistrationData,
+ custom_properties: &mut ComputedCustomProperties,
+) {
+ custom_properties.remove(registration, name);
+ if let Some(ref initial_value) = registration.initial_value {
+ let value = ComputedRegisteredValue::universal(Arc::clone(initial_value));
+ custom_properties.insert(registration, name, value);
+ }
}
fn do_substitute_chunk<'a>(
@@ -1792,7 +1893,8 @@ fn do_substitute_chunk<'a>(
{
let result = &css[start..end];
if !registration.syntax.is_universal() {
- return compute_value(result, url_data, registration, computed_context);
+ let computed_value = compute_value(result, url_data, registration, computed_context)?;
+ return Ok(Substitution::Computed(computed_value));
}
return Ok(Substitution::new(result, first_token_type, last_token_type));
}
@@ -1824,6 +1926,7 @@ fn do_substitute_chunk<'a>(
return Ok(substitution);
}
+ let substitution = substitution.into_universal();
substituted.push(
&substitution.css,
substitution.first_token_type,
@@ -1837,7 +1940,9 @@ fn do_substitute_chunk<'a>(
substituted.push(&css[cur_pos..end], next_token_type, last_token_type)?;
}
if !registration.syntax.is_universal() {
- return compute_value(&substituted.css, url_data, registration, computed_context);
+ let computed_value =
+ compute_value(&substituted.css, url_data, registration, computed_context)?;
+ return Ok(Substitution::Computed(computed_value));
}
Ok(Substitution::from_value(substituted))
}
@@ -1855,7 +1960,8 @@ fn substitute_one_reference<'a>(
if reference.is_var {
registration = stylist.get_custom_property_registration(&reference.name);
if let Some(v) = custom_properties.get(registration, &reference.name) {
- debug_assert!(!v.has_references(), "Should be already computed");
+ #[cfg(debug_assertions)]
+ debug_assert!(v.is_parsed(registration), "Should be already computed");
if registration.syntax.is_universal() {
// Skip references that are inside the outer variable (in fallback for example).
while references
@@ -1881,11 +1987,7 @@ fn substitute_one_reference<'a>(
)?;
}
}
- return Ok(Substitution {
- css: Cow::from(&v.css),
- first_token_type: v.first_token_type,
- last_token_type: v.last_token_type,
- });
+ return Ok(Substitution::Computed(v.clone()));
}
} else {
registration = PropertyRegistrationData::unregistered();
@@ -1899,7 +2001,9 @@ fn substitute_one_reference<'a>(
}
}
- let Some(ref fallback) = reference.fallback else { return Err(()) };
+ let Some(ref fallback) = reference.fallback else {
+ return Err(());
+ };
do_substitute_chunk(
css,
@@ -1955,5 +2059,6 @@ pub fn substitute<'a>(
PropertyRegistrationData::unregistered(),
computed_context,
)?;
+ let v = v.into_universal();
Ok(v.css)
}