mirror of
https://github.com/servo/servo.git
synced 2025-08-05 05:30:08 +01:00
Iterate through properties in priority order when computing keyframes
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
This commit is contained in:
parent
46ffcbaf7b
commit
8e7011da8a
3 changed files with 112 additions and 5 deletions
|
@ -46,6 +46,11 @@ def to_camel_case_lower(ident):
|
||||||
return camel[0].lower() + camel[1:]
|
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):
|
def parse_aliases(value):
|
||||||
aliases = {}
|
aliases = {}
|
||||||
for pair in value.split():
|
for pair in value.split():
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
<%namespace name="helpers" file="/helpers.mako.rs" />
|
<%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 app_units::Au;
|
||||||
use cssparser::{Parser, RGBA};
|
use cssparser::{Parser, RGBA};
|
||||||
|
@ -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::transform::computed_value::T as TransformList;
|
||||||
use properties::longhands::vertical_align::computed_value::T as VerticalAlign;
|
use properties::longhands::vertical_align::computed_value::T as VerticalAlign;
|
||||||
use properties::longhands::visibility::computed_value::T as Visibility;
|
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 selectors::parser::SelectorParseError;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
|
@ -3204,3 +3205,56 @@ impl Animatable for AnimatedFilterList {
|
||||||
Ok(square_distance.sqrt())
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -83,6 +83,7 @@ use style::gecko_bindings::structs::nsCSSValueSharedList;
|
||||||
use style::gecko_bindings::structs::nsCompatibility;
|
use style::gecko_bindings::structs::nsCompatibility;
|
||||||
use style::gecko_bindings::structs::nsIDocument;
|
use style::gecko_bindings::structs::nsIDocument;
|
||||||
use style::gecko_bindings::structs::nsStyleTransformMatrix::MatrixTransformOperator;
|
use style::gecko_bindings::structs::nsStyleTransformMatrix::MatrixTransformOperator;
|
||||||
|
use style::gecko_bindings::structs::nsTArray;
|
||||||
use style::gecko_bindings::structs::nsresult;
|
use style::gecko_bindings::structs::nsresult;
|
||||||
use style::gecko_bindings::sugar::ownership::{FFIArcHelpers, HasFFI, HasArcFFI, HasBoxFFI};
|
use style::gecko_bindings::sugar::ownership::{FFIArcHelpers, HasFFI, HasArcFFI, HasBoxFFI};
|
||||||
use style::gecko_bindings::sugar::ownership::{HasSimpleFFI, Strong};
|
use style::gecko_bindings::sugar::ownership::{HasSimpleFFI, Strong};
|
||||||
|
@ -94,9 +95,10 @@ use style::parallel;
|
||||||
use style::parser::ParserContext;
|
use style::parser::ParserContext;
|
||||||
use style::properties::{ComputedValues, Importance};
|
use style::properties::{ComputedValues, Importance};
|
||||||
use style::properties::{IS_FIELDSET_CONTENT, LonghandIdSet};
|
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::{SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, SourcePropertyDeclaration, StyleBuilder};
|
||||||
use style::properties::animated_properties::{Animatable, AnimatableLonghand, AnimationValue};
|
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::properties::parse_one_declaration_into;
|
||||||
use style::rule_tree::StyleSource;
|
use style::rule_tree::StyleSource;
|
||||||
use style::selector_parser::PseudoElementCascadeType;
|
use style::selector_parser::PseudoElementCascadeType;
|
||||||
|
@ -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]
|
#[no_mangle]
|
||||||
pub extern "C" fn Servo_GetComputedKeyframeValues(keyframes: RawGeckoKeyframeListBorrowed,
|
pub extern "C" fn Servo_GetComputedKeyframeValues(keyframes: RawGeckoKeyframeListBorrowed,
|
||||||
element: RawGeckoElementBorrowed,
|
element: RawGeckoElementBorrowed,
|
||||||
|
@ -2946,9 +2995,8 @@ pub extern "C" fn Servo_GetComputedKeyframeValues(keyframes: RawGeckoKeyframeLis
|
||||||
|
|
||||||
let mut seen = LonghandIdSet::new();
|
let mut seen = LonghandIdSet::new();
|
||||||
|
|
||||||
let iter = keyframe.mPropertyValues.iter();
|
|
||||||
let mut property_index = 0;
|
let mut property_index = 0;
|
||||||
for property in iter {
|
for property in PrioritizedPropertyIter::new(&keyframe.mPropertyValues) {
|
||||||
if simulate_compute_values_failure(property) {
|
if simulate_compute_values_failure(property) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue