From 93f0de789991fbcfdb62abd1fb749703efb845ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Jim=C3=A9nez=20Moreno?= Date: Wed, 28 Jun 2017 12:27:29 -0700 Subject: [PATCH] stylo: implement indexed and count getters for custom properties --- components/style/custom_properties.rs | 95 +++++++++++++++---- components/style/gecko/generated/bindings.rs | 16 ++++ .../style/properties/properties.mako.rs | 20 ++-- ports/geckolib/glue.rs | 35 ++++++- 4 files changed, 135 insertions(+), 31 deletions(-) diff --git a/components/style/custom_properties.rs b/components/style/custom_properties.rs index e6dbabdbe00..d5920a6533f 100644 --- a/components/style/custom_properties.rs +++ b/components/style/custom_properties.rs @@ -13,7 +13,7 @@ use properties::{CSSWideKeyword, DeclaredValue}; use selectors::parser::SelectorParseError; use std::ascii::AsciiExt; use std::borrow::Cow; -use std::collections::{HashMap, HashSet}; +use std::collections::{HashMap, hash_map, HashSet}; use std::fmt; use style_traits::{HasViewportPercentage, ToCss, StyleParseError, ParseError}; use stylearc::Arc; @@ -92,7 +92,65 @@ impl ToCss for ComputedValue { /// A map from CSS variable names to CSS variable computed values, used for /// resolving. -pub type ComputedValuesMap = HashMap; +/// +/// A consistent ordering is required for CSSDeclaration objects in the +/// DOM. CSSDeclarations expose property names as indexed properties, which +/// need to be stable. So we keep an array of property names which order is +/// determined on the order that they are added to the name-value map. +#[derive(Clone, Debug)] +pub struct CustomPropertiesMap { + /// Custom property name index. + index: Vec, + /// Computed values indexed by custom property name. + values: HashMap, +} + +impl CustomPropertiesMap { + /// Creates a new custom properties map. + pub fn new() -> Self { + CustomPropertiesMap { + index: Vec::new(), + values: HashMap::new(), + } + } + + /// Insert a computed value if it has not previously been inserted. + pub fn insert(&mut self, name: &Name, value: ComputedValue) { + debug_assert!(!self.index.contains(name)); + self.index.push(name.clone()); + self.values.insert(name.clone(), value); + } + + /// Custom property computed value getter by name. + pub fn get_computed_value(&self, name: &Name) -> Option<&ComputedValue> { + let value = self.values.get(name); + debug_assert_eq!(value.is_some(), self.index.contains(name)); + value + } + + /// Get the name of a custom property given its list index. + pub fn get_name_at(&self, index: u32) -> Option<&Name> { + self.index.get(index as usize) + } + + /// Get an iterator for custom properties computed values. + pub fn iter(&self) -> hash_map::Iter { + self.values.iter() + } + + /// Get the count of custom properties computed values. + pub fn len(&self) -> usize { + debug_assert_eq!(self.values.len(), self.index.len()); + self.values.len() + } +} + +impl Eq for CustomPropertiesMap {} +impl PartialEq for CustomPropertiesMap { + fn eq(&self, other: &CustomPropertiesMap) -> bool { + self.values == other.values && self.index == other.index + } +} impl ComputedValue { fn empty() -> ComputedValue { @@ -335,7 +393,7 @@ fn parse_var_function<'i, 't>(input: &mut Parser<'i, 't>, /// Add one custom property declaration to a map, unless another with the same /// name was already there. pub fn cascade<'a>(custom_properties: &mut Option>>, - inherited: &'a Option>>, + inherited: &'a Option>, seen: &mut HashSet<&'a Name>, name: &'a Name, specified_value: DeclaredValue<'a, Box>) { @@ -388,8 +446,8 @@ pub fn cascade<'a>(custom_properties: &mut Option>, - inherited: &Option>>) - -> Option>> { + inherited: &Option>) + -> Option> { if let Some(mut map) = specified_values_map { remove_cycles(&mut map); Some(Arc::new(substitute_all(map, inherited))) @@ -445,18 +503,19 @@ fn remove_cycles(map: &mut HashMap<&Name, BorrowedSpecifiedValue>) { /// Replace `var()` functions for all custom properties. fn substitute_all(specified_values_map: HashMap<&Name, BorrowedSpecifiedValue>, - inherited: &Option>>) - -> HashMap { - let mut computed_values_map = HashMap::new(); + inherited: &Option>) + -> CustomPropertiesMap { + let mut custom_properties_map = CustomPropertiesMap::new(); let mut invalid = HashSet::new(); for (&name, value) in &specified_values_map { // If this value is invalid at computed-time it won’t be inserted in computed_values_map. // Nothing else to do. let _ = substitute_one( name, value, &specified_values_map, inherited, None, - &mut computed_values_map, &mut invalid); + &mut custom_properties_map, &mut invalid); } - computed_values_map + + custom_properties_map } /// Replace `var()` functions for one custom property. @@ -466,12 +525,12 @@ fn substitute_all(specified_values_map: HashMap<&Name, BorrowedSpecifiedValue>, fn substitute_one(name: &Name, specified_value: &BorrowedSpecifiedValue, specified_values_map: &HashMap<&Name, BorrowedSpecifiedValue>, - inherited: &Option>>, + inherited: &Option>, partial_computed_value: Option<&mut ComputedValue>, - computed_values_map: &mut HashMap, + custom_properties_map: &mut CustomPropertiesMap, invalid: &mut HashSet) -> Result { - if let Some(computed_value) = computed_values_map.get(name) { + if let Some(computed_value) = custom_properties_map.get_computed_value(&name) { if let Some(partial_computed_value) = partial_computed_value { partial_computed_value.push_variable(computed_value) } @@ -491,7 +550,7 @@ fn substitute_one(name: &Name, &mut |name, partial_computed_value| { if let Some(other_specified_value) = specified_values_map.get(name) { substitute_one(name, other_specified_value, specified_values_map, inherited, - Some(partial_computed_value), computed_values_map, invalid) + Some(partial_computed_value), custom_properties_map, invalid) } else { Err(()) } @@ -502,7 +561,7 @@ fn substitute_one(name: &Name, partial_computed_value } else { // Invalid at computed-value time. Use the inherited value. - if let Some(inherited_value) = inherited.as_ref().and_then(|i| i.get(name)) { + if let Some(inherited_value) = inherited.as_ref().and_then(|i| i.values.get(name)) { inherited_value.clone() } else { invalid.insert(name.clone()); @@ -521,7 +580,7 @@ fn substitute_one(name: &Name, partial_computed_value.push_variable(&computed_value) } let last_token_type = computed_value.last_token_type; - computed_values_map.insert(name.clone(), computed_value); + custom_properties_map.insert(name, computed_value); Ok(last_token_type) } @@ -616,7 +675,7 @@ fn substitute_block<'i, 't, F>(input: &mut Parser<'i, 't>, /// Replace `var()` 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>>) + computed_values_map: &Option>) -> Result> { let mut substituted = ComputedValue::empty(); let mut input = ParserInput::new(input); @@ -624,7 +683,7 @@ pub fn substitute<'i>(input: &'i str, first_token_type: TokenSerializationType, let mut position = (input.position(), first_token_type); let last_token_type = substitute_block( &mut input, &mut position, &mut substituted, &mut |name, substituted| { - if let Some(value) = computed_values_map.as_ref().and_then(|map| map.get(name)) { + if let Some(value) = computed_values_map.as_ref().and_then(|map| map.get_computed_value(name)) { substituted.push_variable(value); Ok(value.last_token_type) } else { diff --git a/components/style/gecko/generated/bindings.rs b/components/style/gecko/generated/bindings.rs index e94e1cd96eb..bb95e101c6a 100644 --- a/components/style/gecko/generated/bindings.rs +++ b/components/style/gecko/generated/bindings.rs @@ -2709,6 +2709,22 @@ extern "C" { RawServoDeclarationBlockBorrowed, buffer: *mut nsAString); } +extern "C" { + pub fn Servo_GetCustomPropertyValue(computed_values: + ServoComputedValuesBorrowed, + name: *const nsAString, + value: *mut nsAString) -> bool; +} +extern "C" { + pub fn Servo_GetCustomPropertiesCount(computed_values: + ServoComputedValuesBorrowed) + -> u32; +} +extern "C" { + pub fn Servo_GetCustomPropertyNameAt(arg1: ServoComputedValuesBorrowed, + index: u32, name: *mut nsAString) + -> bool; +} extern "C" { pub fn Servo_GetStyleFont(computed_values: ServoComputedValuesBorrowedOrNull) diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index 02629ec6f74..19d9cc54b0e 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -375,7 +375,7 @@ impl PropertyDeclarationIdSet { % else: value: &DeclaredValue, % endif - custom_properties: &Option>, + custom_properties: &Option>, f: &mut F, error_reporter: &ParseErrorReporter, quirks_mode: QuirksMode) @@ -406,7 +406,7 @@ impl PropertyDeclarationIdSet { first_token_type: TokenSerializationType, url_data: &UrlExtraData, from_shorthand: Option, - custom_properties: &Option>, + custom_properties: &Option>, f: &mut F, error_reporter: &ParseErrorReporter, quirks_mode: QuirksMode) @@ -1816,7 +1816,7 @@ pub struct ComputedValues { % for style_struct in data.active_style_structs(): ${style_struct.ident}: Arc, % endfor - custom_properties: Option>, + custom_properties: Option>, /// The writing mode of this computed values struct. pub writing_mode: WritingMode, /// The keyword behind the current font-size property, if any @@ -1839,7 +1839,7 @@ pub struct ComputedValues { impl ComputedValues { /// Construct a `ComputedValues` instance. pub fn new( - custom_properties: Option>, + custom_properties: Option>, writing_mode: WritingMode, font_size_keyword: Option<(longhands::font_size::KeywordSize, f32)>, flags: ComputedValueFlags, @@ -1937,7 +1937,7 @@ impl ComputedValues { // Aah! The << in the return type below is not valid syntax, but we must // escape < that way for Mako. /// Gets a reference to the custom properties map (if one exists). - pub fn get_custom_properties(&self) -> Option<<&::custom_properties::ComputedValuesMap> { + pub fn get_custom_properties(&self) -> Option<<&::custom_properties::CustomPropertiesMap> { self.custom_properties.as_ref().map(|x| &**x) } @@ -1945,7 +1945,7 @@ impl ComputedValues { /// /// Cloning the Arc here is fine because it only happens in the case where /// we have custom properties, and those are both rare and expensive. - pub fn custom_properties(&self) -> Option> { + pub fn custom_properties(&self) -> Option> { self.custom_properties.clone() } @@ -2241,7 +2241,7 @@ impl ComputedValues { PropertyDeclarationId::Custom(name) => { self.custom_properties .as_ref() - .and_then(|map| map.get(name)) + .and_then(|map| map.get_computed_value(name)) .map(|value| value.to_css_string()) .unwrap_or(String::new()) } @@ -2401,7 +2401,7 @@ pub struct StyleBuilder<'a> { /// The rule node representing the ordered list of rules matched for this /// node. rules: Option, - custom_properties: Option>, + custom_properties: Option>, /// The writing mode flags. /// /// TODO(emilio): Make private. @@ -2423,7 +2423,7 @@ impl<'a> StyleBuilder<'a> { inherited_style: &'a ComputedValues, reset_style: &'a ComputedValues, rules: Option, - custom_properties: Option>, + custom_properties: Option>, writing_mode: WritingMode, font_size_keyword: Option<(longhands::font_size::KeywordSize, f32)>, visited_style: Option>, @@ -2544,7 +2544,7 @@ impl<'a> StyleBuilder<'a> { /// /// Cloning the Arc here is fine because it only happens in the case where /// we have custom properties, and those are both rare and expensive. - fn custom_properties(&self) -> Option> { + fn custom_properties(&self) -> Option> { self.custom_properties.clone() } } diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index f00fb4a6c0a..c3bc1913444 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -3185,15 +3185,16 @@ pub extern "C" fn Servo_StyleSet_HasStateDependency(raw_data: RawServoStyleSetBo } #[no_mangle] -pub extern "C" fn Servo_GetCustomProperty(computed_values: ServoComputedValuesBorrowed, - name: *const nsAString, value: *mut nsAString) -> bool { +pub extern "C" fn Servo_GetCustomPropertyValue(computed_values: ServoComputedValuesBorrowed, + name: *const nsAString, + value: *mut nsAString) -> bool { let custom_properties = match ComputedValues::as_arc(&computed_values).custom_properties() { Some(p) => p, None => return false, }; let name = unsafe { Atom::from((&*name)) }; - let computed_value = match custom_properties.get(&name) { + let computed_value = match custom_properties.get_computed_value(&name) { Some(v) => v, None => return false, }; @@ -3201,3 +3202,31 @@ pub extern "C" fn Servo_GetCustomProperty(computed_values: ServoComputedValuesBo computed_value.to_css(unsafe { value.as_mut().unwrap() }).unwrap(); true } + +#[no_mangle] +pub extern "C" fn Servo_GetCustomPropertiesCount(computed_values: ServoComputedValuesBorrowed) -> u32 { + match ComputedValues::as_arc(&computed_values).custom_properties() { + Some(p) => p.len() as u32, + None => 0, + } +} + +#[no_mangle] +pub extern "C" fn Servo_GetCustomPropertyNameAt(computed_values: ServoComputedValuesBorrowed, + index: u32, + name: *mut nsAString) -> bool { + let custom_properties = match ComputedValues::as_arc(&computed_values).custom_properties() { + Some(p) => p, + None => return false, + }; + + let property_name = match custom_properties.get_name_at(index) { + Some(n) => n, + None => return false, + }; + + let name = unsafe { name.as_mut().unwrap() }; + name.assign(&*property_name.as_slice()); + + true +}