Skip to content

Commit

Permalink
Auto merge of #17551 - ferjm:bug1375555.stylevariables, r=emilio
Browse files Browse the repository at this point in the history
stylo: implement indexed and count getters for custom properties

From https://bugzilla.mozilla.org/show_bug.cgi?id=1375555

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/17551)
<!-- Reviewable:end -->
  • Loading branch information
bors-servo committed Jun 29, 2017
2 parents c3a202b + 93f0de7 commit b3e2b26
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 31 deletions.
95 changes: 77 additions & 18 deletions components/style/custom_properties.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -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<Name, ComputedValue>;
///
/// 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<Name>,
/// Computed values indexed by custom property name.
values: HashMap<Name, ComputedValue>,
}

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<Name, ComputedValue> {
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 {
Expand Down Expand Up @@ -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<HashMap<&'a Name, BorrowedSpecifiedValue<'a>>>,
inherited: &'a Option<Arc<HashMap<Name, ComputedValue>>>,
inherited: &'a Option<Arc<CustomPropertiesMap>>,
seen: &mut HashSet<&'a Name>,
name: &'a Name,
specified_value: DeclaredValue<'a, Box<SpecifiedValue>>) {
Expand Down Expand Up @@ -388,8 +446,8 @@ pub fn cascade<'a>(custom_properties: &mut Option<HashMap<&'a Name, BorrowedSpec
///
/// Otherwise, just use the inherited custom properties map.
pub fn finish_cascade(specified_values_map: Option<HashMap<&Name, BorrowedSpecifiedValue>>,
inherited: &Option<Arc<HashMap<Name, ComputedValue>>>)
-> Option<Arc<HashMap<Name, ComputedValue>>> {
inherited: &Option<Arc<CustomPropertiesMap>>)
-> Option<Arc<CustomPropertiesMap>> {
if let Some(mut map) = specified_values_map {
remove_cycles(&mut map);
Some(Arc::new(substitute_all(map, inherited)))
Expand Down Expand Up @@ -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<Arc<HashMap<Name, ComputedValue>>>)
-> HashMap<Name, ComputedValue> {
let mut computed_values_map = HashMap::new();
inherited: &Option<Arc<CustomPropertiesMap>>)
-> 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.
Expand All @@ -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<Arc<HashMap<Name, ComputedValue>>>,
inherited: &Option<Arc<CustomPropertiesMap>>,
partial_computed_value: Option<&mut ComputedValue>,
computed_values_map: &mut HashMap<Name, ComputedValue>,
custom_properties_map: &mut CustomPropertiesMap,
invalid: &mut HashSet<Name>)
-> Result<TokenSerializationType, ()> {
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)
}
Expand All @@ -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(())
}
Expand All @@ -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());
Expand All @@ -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)
}

Expand Down Expand Up @@ -616,15 +675,15 @@ 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<Arc<HashMap<Name, ComputedValue>>>)
computed_values_map: &Option<Arc<CustomPropertiesMap>>)
-> 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 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 {
Expand Down
16 changes: 16 additions & 0 deletions components/style/gecko/generated/bindings.rs
Expand Up @@ -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)
Expand Down
20 changes: 10 additions & 10 deletions components/style/properties/properties.mako.rs
Expand Up @@ -375,7 +375,7 @@ impl PropertyDeclarationIdSet {
% else:
value: &DeclaredValue<longhands::${property.ident}::SpecifiedValue>,
% endif
custom_properties: &Option<Arc<::custom_properties::ComputedValuesMap>>,
custom_properties: &Option<Arc<::custom_properties::CustomPropertiesMap>>,
f: &mut F,
error_reporter: &ParseErrorReporter,
quirks_mode: QuirksMode)
Expand Down Expand Up @@ -406,7 +406,7 @@ impl PropertyDeclarationIdSet {
first_token_type: TokenSerializationType,
url_data: &UrlExtraData,
from_shorthand: Option<ShorthandId>,
custom_properties: &Option<Arc<::custom_properties::ComputedValuesMap>>,
custom_properties: &Option<Arc<::custom_properties::CustomPropertiesMap>>,
f: &mut F,
error_reporter: &ParseErrorReporter,
quirks_mode: QuirksMode)
Expand Down Expand Up @@ -1816,7 +1816,7 @@ pub struct ComputedValues {
% for style_struct in data.active_style_structs():
${style_struct.ident}: Arc<style_structs::${style_struct.name}>,
% endfor
custom_properties: Option<Arc<::custom_properties::ComputedValuesMap>>,
custom_properties: Option<Arc<::custom_properties::CustomPropertiesMap>>,
/// The writing mode of this computed values struct.
pub writing_mode: WritingMode,
/// The keyword behind the current font-size property, if any
Expand All @@ -1839,7 +1839,7 @@ pub struct ComputedValues {
impl ComputedValues {
/// Construct a `ComputedValues` instance.
pub fn new(
custom_properties: Option<Arc<::custom_properties::ComputedValuesMap>>,
custom_properties: Option<Arc<::custom_properties::CustomPropertiesMap>>,
writing_mode: WritingMode,
font_size_keyword: Option<(longhands::font_size::KeywordSize, f32)>,
flags: ComputedValueFlags,
Expand Down Expand Up @@ -1937,15 +1937,15 @@ 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)
}

/// Get the custom properties map if necessary.
///
/// 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<Arc<::custom_properties::ComputedValuesMap>> {
pub fn custom_properties(&self) -> Option<Arc<::custom_properties::CustomPropertiesMap>> {
self.custom_properties.clone()
}

Expand Down Expand Up @@ -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())
}
Expand Down Expand Up @@ -2401,7 +2401,7 @@ pub struct StyleBuilder<'a> {
/// The rule node representing the ordered list of rules matched for this
/// node.
rules: Option<StrongRuleNode>,
custom_properties: Option<Arc<::custom_properties::ComputedValuesMap>>,
custom_properties: Option<Arc<::custom_properties::CustomPropertiesMap>>,
/// The writing mode flags.
///
/// TODO(emilio): Make private.
Expand All @@ -2423,7 +2423,7 @@ impl<'a> StyleBuilder<'a> {
inherited_style: &'a ComputedValues,
reset_style: &'a ComputedValues,
rules: Option<StrongRuleNode>,
custom_properties: Option<Arc<::custom_properties::ComputedValuesMap>>,
custom_properties: Option<Arc<::custom_properties::CustomPropertiesMap>>,
writing_mode: WritingMode,
font_size_keyword: Option<(longhands::font_size::KeywordSize, f32)>,
visited_style: Option<Arc<ComputedValues>>,
Expand Down Expand Up @@ -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<Arc<::custom_properties::ComputedValuesMap>> {
fn custom_properties(&self) -> Option<Arc<::custom_properties::CustomPropertiesMap>> {
self.custom_properties.clone()
}
}
Expand Down
35 changes: 32 additions & 3 deletions ports/geckolib/glue.rs
Expand Up @@ -3224,19 +3224,48 @@ pub extern "C" fn Servo_StyleSet_HasStateDependency(
}

#[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,
};

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
}

0 comments on commit b3e2b26

Please sign in to comment.