Skip to content

Commit

Permalink
Iterate through properties in priority order when computing keyframes
Browse files Browse the repository at this point in the history
This is largely just a translation of Gecko's
PropertyPriorityIterator[1] into rust with the exception that IDL sort
order is only defined for shorthands since that's all we currently
require.

[1] http://searchfox.org/mozilla-central/rev/3a3af33f513071ea829debdfbc628caebcdf6996/dom/animation/KeyframeUtils.cpp#151
  • Loading branch information
birtles committed Jul 24, 2017
1 parent 46ffcba commit 8e7011d
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 5 deletions.
5 changes: 5 additions & 0 deletions components/style/properties/data.py
Expand Up @@ -46,6 +46,11 @@ def to_camel_case_lower(ident):
return camel[0].lower() + camel[1:]


# https://drafts.csswg.org/cssom/#css-property-to-idl-attribute
def to_idl_name(ident):
return re.sub("-([a-z])", lambda m: m.group(1).upper(), ident)


def parse_aliases(value):
aliases = {}
for pair in value.split():
Expand Down
58 changes: 56 additions & 2 deletions components/style/properties/helpers/animated_properties.mako.rs
Expand Up @@ -4,7 +4,7 @@

<%namespace name="helpers" file="/helpers.mako.rs" />

<% from data import SYSTEM_FONT_LONGHANDS %>
<% from data import to_idl_name, SYSTEM_FONT_LONGHANDS %>

use app_units::Au;
use cssparser::{Parser, RGBA};
Expand All @@ -23,7 +23,8 @@ use properties::longhands::transform::computed_value::ComputedOperation as Trans
use properties::longhands::transform::computed_value::T as TransformList;
use properties::longhands::vertical_align::computed_value::T as VerticalAlign;
use properties::longhands::visibility::computed_value::T as Visibility;
#[cfg(feature = "gecko")] use properties::{PropertyDeclarationId, LonghandId};
#[cfg(feature = "gecko")] use properties::{PropertyId, PropertyDeclarationId, LonghandId};
#[cfg(feature = "gecko")] use properties::{ShorthandId};
use selectors::parser::SelectorParseError;
use smallvec::SmallVec;
use std::cmp;
Expand Down Expand Up @@ -3204,3 +3205,56 @@ impl Animatable for AnimatedFilterList {
Ok(square_distance.sqrt())
}
}

/// A comparator to sort PropertyIds such that longhands are sorted before shorthands,
/// shorthands with fewer components are sorted before shorthands with more components,
/// and otherwise shorthands are sorted by IDL name as defined by [Web Animations][property-order].
///
/// Using this allows us to prioritize values specified by longhands (or smaller
/// shorthand subsets) when longhands and shorthands are both specified on the one keyframe.
///
/// Example orderings that result from this:
///
/// margin-left, margin
///
/// and:
///
/// border-top-color, border-color, border-top, border
///
/// [property-order] https://w3c.github.io/web-animations/#calculating-computed-keyframes
#[cfg(feature = "gecko")]
pub fn compare_property_priority(a: &PropertyId, b: &PropertyId) -> cmp::Ordering {
match (a.as_shorthand(), b.as_shorthand()) {
// Within shorthands, sort by the number of subproperties, then by IDL name.
(Ok(a), Ok(b)) => {
let subprop_count_a = a.longhands().len();
let subprop_count_b = b.longhands().len();
subprop_count_a.cmp(&subprop_count_b).then_with(
|| get_idl_name_sort_order(&a).cmp(&get_idl_name_sort_order(&b)))
},

// Longhands go before shorthands.
(Ok(_), Err(_)) => cmp::Ordering::Greater,
(Err(_), Ok(_)) => cmp::Ordering::Less,

// Both are longhands or custom properties in which case they don't overlap and should
// sort equally.
_ => cmp::Ordering::Equal,
}
}

#[cfg(feature = "gecko")]
fn get_idl_name_sort_order(shorthand: &ShorthandId) -> u32 {
<%
# Sort by IDL name.
sorted_shorthands = sorted(data.shorthands, key=lambda p: to_idl_name(p.ident))

# Annotate with sorted position
sorted_shorthands = [(p, position) for position, p in enumerate(sorted_shorthands)]
%>
match *shorthand {
% for property, position in sorted_shorthands:
ShorthandId::${property.camel_case} => ${position},
% endfor
}
}
54 changes: 51 additions & 3 deletions ports/geckolib/glue.rs
Expand Up @@ -83,6 +83,7 @@ use style::gecko_bindings::structs::nsCSSValueSharedList;
use style::gecko_bindings::structs::nsCompatibility;
use style::gecko_bindings::structs::nsIDocument;
use style::gecko_bindings::structs::nsStyleTransformMatrix::MatrixTransformOperator;
use style::gecko_bindings::structs::nsTArray;
use style::gecko_bindings::structs::nsresult;
use style::gecko_bindings::sugar::ownership::{FFIArcHelpers, HasFFI, HasArcFFI, HasBoxFFI};
use style::gecko_bindings::sugar::ownership::{HasSimpleFFI, Strong};
Expand All @@ -94,9 +95,10 @@ use style::parallel;
use style::parser::ParserContext;
use style::properties::{ComputedValues, Importance};
use style::properties::{IS_FIELDSET_CONTENT, LonghandIdSet};
use style::properties::{PropertyDeclaration, PropertyDeclarationBlock, PropertyId};
use style::properties::{PropertyDeclaration, PropertyDeclarationBlock, PropertyId, ShorthandId};
use style::properties::{SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, SourcePropertyDeclaration, StyleBuilder};
use style::properties::animated_properties::{Animatable, AnimatableLonghand, AnimationValue};
use style::properties::animated_properties::compare_property_priority;
use style::properties::parse_one_declaration_into;
use style::rule_tree::StyleSource;
use style::selector_parser::PseudoElementCascadeType;
Expand Down Expand Up @@ -2916,6 +2918,53 @@ fn create_context<'a>(
}
}

struct PropertyAndIndex {
property: PropertyId,
index: usize,
}

struct PrioritizedPropertyIter<'a> {
properties: &'a nsTArray<PropertyValuePair>,
sorted_property_indices: Vec<PropertyAndIndex>,
curr: usize,
}

impl<'a> PrioritizedPropertyIter<'a> {
pub fn new(properties: &'a nsTArray<PropertyValuePair>) -> PrioritizedPropertyIter {
// If we fail to convert a nsCSSPropertyID into a PropertyId we shouldn't fail outright
// but instead by treating that property as the 'all' property we make it sort last.
let all = PropertyId::Shorthand(ShorthandId::All);

let mut sorted_property_indices: Vec<PropertyAndIndex> =
properties.iter().enumerate().map(|(index, pair)| {
PropertyAndIndex {
property: PropertyId::from_nscsspropertyid(pair.mProperty)
.unwrap_or(all.clone()),
index,
}
}).collect();
sorted_property_indices.sort_by(|a, b| compare_property_priority(&a.property, &b.property));

PrioritizedPropertyIter {
properties,
sorted_property_indices,
curr: 0,
}
}
}

impl<'a> Iterator for PrioritizedPropertyIter<'a> {
type Item = &'a PropertyValuePair;

fn next(&mut self) -> Option<&'a PropertyValuePair> {
if self.curr >= self.sorted_property_indices.len() {
return None
}
self.curr += 1;
Some(&self.properties[self.sorted_property_indices[self.curr - 1].index])
}
}

#[no_mangle]
pub extern "C" fn Servo_GetComputedKeyframeValues(keyframes: RawGeckoKeyframeListBorrowed,
element: RawGeckoElementBorrowed,
Expand Down Expand Up @@ -2946,9 +2995,8 @@ pub extern "C" fn Servo_GetComputedKeyframeValues(keyframes: RawGeckoKeyframeLis

let mut seen = LonghandIdSet::new();

let iter = keyframe.mPropertyValues.iter();
let mut property_index = 0;
for property in iter {
for property in PrioritizedPropertyIter::new(&keyframe.mPropertyValues) {
if simulate_compute_values_failure(property) {
continue;
}
Expand Down

0 comments on commit 8e7011d

Please sign in to comment.