From fc59165ff3432088f5c55f7cebf54f9fae707de6 Mon Sep 17 00:00:00 2001 From: Brian Birtles Date: Fri, 19 Oct 2018 04:41:12 +0000 Subject: [PATCH 01/14] style: Implement the even more forgiving interpolation rules for transform lists. As discussed in: https://github.com/w3c/csswg-drafts/issues/927 with tentative spec text: https://github.com/w3c/csswg-drafts/pull/3215 Differential Revision: https://phabricator.services.mozilla.com/D9185 --- .../helpers/animated_properties.mako.rs | 173 +++++++++++------- 1 file changed, 103 insertions(+), 70 deletions(-) diff --git a/components/style/properties/helpers/animated_properties.mako.rs b/components/style/properties/helpers/animated_properties.mako.rs index 5259f66107d..11ae9115fe8 100644 --- a/components/style/properties/helpers/animated_properties.mako.rs +++ b/components/style/properties/helpers/animated_properties.mako.rs @@ -1356,11 +1356,11 @@ fn is_matched_operation(first: &ComputedTransformOperation, second: &ComputedTra &TransformOperation::RotateZ(..)) | (&TransformOperation::Perspective(..), &TransformOperation::Perspective(..)) => true, - // we animate scale and translate operations against each other + // Match functions that have the same primitive transform function (a, b) if a.is_translate() && b.is_translate() => true, (a, b) if a.is_scale() && b.is_scale() => true, (a, b) if a.is_rotate() && b.is_rotate() => true, - // InterpolateMatrix and AccumulateMatrix are for mismatched transform. + // InterpolateMatrix and AccumulateMatrix are for mismatched transforms _ => false } } @@ -2468,79 +2468,112 @@ impl Animate for ComputedTransform { return Ok(Transform(result)); } - // https://drafts.csswg.org/css-transforms-1/#transform-transform-neutral-extend-animation - fn match_operations_if_possible<'a>( - this: &mut Cow<'a, Vec>, - other: &mut Cow<'a, Vec>, - ) -> bool { - if !this.iter().zip(other.iter()).all(|(this, other)| is_matched_operation(this, other)) { - return false; - } + let this = Cow::Borrowed(&self.0); + let other = Cow::Borrowed(&other.0); - if this.len() == other.len() { - return true; - } + // Interpolate the common prefix + let mut result = this + .iter() + .zip(other.iter()) + .take_while(|(this, other)| is_matched_operation(this, other)) + .map(|(this, other)| this.animate(other, procedure)) + .collect::, _>>()?; - let (shorter, longer) = - if this.len() < other.len() { - (this.to_mut(), other) - } else { - (other.to_mut(), this) - }; + // Deal with the remainders + let this_remainder = if this.len() > result.len() { + Some(&this[result.len()..]) + } else { + None + }; + let other_remainder = if other.len() > result.len() { + Some(&other[result.len()..]) + } else { + None + }; - shorter.reserve(longer.len()); - for op in longer.iter().skip(shorter.len()) { - shorter.push(op.to_animated_zero().unwrap()); - } - - // The resulting operations won't be matched regardless if the - // extended component is already InterpolateMatrix / - // AccumulateMatrix. - // - // Otherwise they should be matching operations all the time. - let already_mismatched = matches!( - longer[0], - TransformOperation::InterpolateMatrix { .. } | - TransformOperation::AccumulateMatrix { .. } - ); - - debug_assert_eq!( - !already_mismatched, - longer.iter().zip(shorter.iter()).all(|(this, other)| is_matched_operation(this, other)), - "ToAnimatedZero should generate matched operations" - ); - - !already_mismatched - } - - let mut this = Cow::Borrowed(&self.0); - let mut other = Cow::Borrowed(&other.0); - - if match_operations_if_possible(&mut this, &mut other) { - return Ok(Transform( - this.iter().zip(other.iter()) - .map(|(this, other)| this.animate(other, procedure)) - .collect::, _>>()? - )); - } - - match procedure { - Procedure::Add => Err(()), - Procedure::Interpolate { progress } => { - Ok(Transform(vec![TransformOperation::InterpolateMatrix { - from_list: Transform(this.into_owned()), - to_list: Transform(other.into_owned()), - progress: Percentage(progress as f32), - }])) - }, - Procedure::Accumulate { count } => { - Ok(Transform(vec![TransformOperation::AccumulateMatrix { - from_list: Transform(this.into_owned()), - to_list: Transform(other.into_owned()), - count: cmp::min(count, i32::max_value() as u64) as i32, - }])) + match (this_remainder, other_remainder) { + // If there is a remainder from *both* lists we must have had mismatched functions. + // => Add the remainders to a suitable ___Matrix function. + (Some(this_remainder), Some(other_remainder)) => match procedure { + Procedure::Add => { + debug_assert!(false, "Should have already dealt with add by the point"); + return Err(()); + } + Procedure::Interpolate { progress } => { + result.push(TransformOperation::InterpolateMatrix { + from_list: Transform(this_remainder.to_vec()), + to_list: Transform(other_remainder.to_vec()), + progress: Percentage(progress as f32), + }); + } + Procedure::Accumulate { count } => { + result.push(TransformOperation::AccumulateMatrix { + from_list: Transform(this_remainder.to_vec()), + to_list: Transform(other_remainder.to_vec()), + count: cmp::min(count, i32::max_value() as u64) as i32, + }); + } }, + // If there is a remainder from just one list, then one list must be shorter but + // completely match the type of the corresponding functions in the longer list. + // => Interpolate the remainder with identity transforms. + (Some(remainder), None) | (None, Some(remainder)) => { + let fill_right = this_remainder.is_some(); + result.append( + &mut remainder + .iter() + .map(|transform| { + let identity = transform.to_animated_zero().unwrap(); + + match transform { + // We can't interpolate/accumulate ___Matrix types directly with a + // matrix. Instead we need to wrap it in another ___Matrix type. + TransformOperation::AccumulateMatrix { .. } + | TransformOperation::InterpolateMatrix { .. } => { + let transform_list = Transform(vec![transform.clone()]); + let identity_list = Transform(vec![identity]); + let (from_list, to_list) = if fill_right { + (transform_list, identity_list) + } else { + (identity_list, transform_list) + }; + + match procedure { + Procedure::Add => Err(()), + Procedure::Interpolate { progress } => { + Ok(TransformOperation::InterpolateMatrix { + from_list, + to_list, + progress: Percentage(progress as f32), + }) + } + Procedure::Accumulate { count } => { + Ok(TransformOperation::AccumulateMatrix { + from_list, + to_list, + count: cmp::min(count, i32::max_value() as u64) + as i32, + }) + } + } + } + _ => { + let (lhs, rhs) = if fill_right { + (transform, &identity) + } else { + (&identity, transform) + }; + lhs.animate(rhs, procedure) + } + } + }) + .collect::, _>>()?, + ); + } + (None, None) => {} } + + Ok(Transform(result)) } } From 0191705e87d1e61d24b8fc1a9b2a7d8ac82fa5df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Mon, 22 Oct 2018 11:59:09 +0000 Subject: [PATCH 02/14] style: Use searchfox links instead of dxr links for nsRuleNode. Searchfox has better blame. Differential Revision: https://phabricator.services.mozilla.com/D9375 --- components/style/gecko/conversions.rs | 4 ++-- components/style/properties/gecko.mako.rs | 2 +- components/style/values/specified/font.rs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/components/style/gecko/conversions.rs b/components/style/gecko/conversions.rs index 10b177558fa..8cd9e4bdde2 100644 --- a/components/style/gecko/conversions.rs +++ b/components/style/gecko/conversions.rs @@ -1032,13 +1032,13 @@ impl TrackSize { match *self { TrackSize::FitContent(ref lop) => { // Gecko sets min value to None and max value to the actual value in fit-content - // https://dxr.mozilla.org/mozilla-central/rev/0eef1d5/layout/style/nsRuleNode.cpp#8221 + // https://searchfox.org/mozilla-central/rev/c05d9d61188d32b8209dfe4295944c0f1e0ce621/layout/style/nsRuleNode.cpp#7910 gecko_min.set_value(CoordDataValue::None); lop.to_gecko_style_coord(gecko_max); }, TrackSize::Breadth(ref breadth) => { // Set the value to both fields if there's one breadth value - // https://dxr.mozilla.org/mozilla-central/rev/0eef1d5/layout/style/nsRuleNode.cpp#8230 + // https://searchfox.org/mozilla-central/rev/c05d9d61188d32b8209dfe4295944c0f1e0ce621/layout/style/nsRuleNode.cpp#7919 breadth.to_gecko_style_coord(gecko_min); breadth.to_gecko_style_coord(gecko_max); }, diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 04e65b5fa7f..c0c01b5120f 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -2394,7 +2394,7 @@ fn static_assert() { /// from the parent. /// /// This is a port of Gecko's old ComputeScriptLevelSize function: - /// https://dxr.mozilla.org/mozilla-central/rev/35fbf14b9/layout/style/nsRuleNode.cpp#3197-3254 + /// https://searchfox.org/mozilla-central/rev/c05d9d61188d32b8209dfe4295944c0f1e0ce621/layout/style/nsRuleNode.cpp#3103 /// /// scriptlevel is a property that affects how font-size is inherited. If scriptlevel is /// +1, for example, it will inherit as the script size multiplier times diff --git a/components/style/values/specified/font.rs b/components/style/values/specified/font.rs index b7bc40b116e..daedd16887e 100644 --- a/components/style/values/specified/font.rs +++ b/components/style/values/specified/font.rs @@ -753,8 +753,8 @@ impl ToComputedValue for KeywordSize { // The tables in this function are originally from // nsRuleNode::CalcFontPointSize in Gecko: // - // https://dxr.mozilla.org/mozilla-central/rev/35fbf14b9/layout/style/nsRuleNode.cpp#3262-3336 - + // https://searchfox.org/mozilla-central/rev/c05d9d61188d32b8209dfe4295944c0f1e0ce621/layout/style/nsRuleNode.cpp#3150 + // // Mapping from base size and HTML size to pixels // The first index is (base_size - 9), the second is the // HTML size. "0" is CSS keyword xx-small, not HTML size 0, From ac873c714fa504fc1db18c5ebaf4c06d8c5d1458 Mon Sep 17 00:00:00 2001 From: tb120 Date: Mon, 22 Oct 2018 21:58:01 +0000 Subject: [PATCH 03/14] style: Convert NS_STYLE_ANIMATION_PLAY_STATE_* to scoped enum StyleAnimationPlayState. This change removes directives NS_STYLE_ANIMATION_PLAY_STATE_* and replaces the values with those from a scoped enum called StyleAnimationPlayState. Differential Revision: https://phabricator.services.mozilla.com/D9382 --- components/style/properties/longhands/box.mako.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/components/style/properties/longhands/box.mako.rs b/components/style/properties/longhands/box.mako.rs index ed7839360d9..8cbba2d8b83 100644 --- a/components/style/properties/longhands/box.mako.rs +++ b/components/style/properties/longhands/box.mako.rs @@ -258,6 +258,7 @@ ${helpers.single_keyword( animation_value_type="none", vector=True, extra_prefixes=animation_extra_prefixes, + gecko_enum_prefix="StyleAnimationPlayState", spec="https://drafts.csswg.org/css-animations/#propdef-animation-play-state", allowed_in_keyframe_block=False, )} From 990f2c6bb9ec1513df25511505c81d0456d56960 Mon Sep 17 00:00:00 2001 From: Brian Birtles Date: Wed, 24 Oct 2018 07:06:43 +0000 Subject: [PATCH 04/14] style: Compare absolute dot-product to 1.0 when interpolating quaternions. See the extended commit message for the following spec change: https://github.com/w3c/csswg-drafts/commit/6b36d41ebc2e790c86cc768a6ea1dcfa81fffe75 Basically, by failing to take the absolute value, for certain content we can end up doing division by zero which will mean that the test included in this patch will cause an assertion to fail in debug builds and return "matrix(NaN, NaN....)" in release builds. Differential Revision: https://phabricator.services.mozilla.com/D9618 --- components/style/properties/helpers/animated_properties.mako.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/style/properties/helpers/animated_properties.mako.rs b/components/style/properties/helpers/animated_properties.mako.rs index 11ae9115fe8..b079f30b1a8 100644 --- a/components/style/properties/helpers/animated_properties.mako.rs +++ b/components/style/properties/helpers/animated_properties.mako.rs @@ -1829,7 +1829,7 @@ impl Animate for Quaternion { self.3 * other.3) .min(1.0).max(-1.0); - if dot == 1.0 { + if dot.abs() == 1.0 { return Ok(*self); } From 3a536f463cec2116d12bebbc50f50395f5354e5b Mon Sep 17 00:00:00 2001 From: Boris Chiou Date: Fri, 26 Oct 2018 18:03:24 +0000 Subject: [PATCH 05/14] style: Drop frames() timing function. frames() timing function was removed from the spec, so we drop it. Besides, some devtool tests are removed because they use frame(). I will add them back by using new step function later. Differential Revision: https://phabricator.services.mozilla.com/D9309 --- components/style/animation.rs | 16 ----------- .../sugar/ns_timing_function.rs | 27 +++---------------- components/style/values/generics/transform.rs | 5 +--- .../style/values/specified/transform.rs | 27 ------------------- 4 files changed, 4 insertions(+), 71 deletions(-) diff --git a/components/style/animation.rs b/components/style/animation.rs index 5d96f5cacd1..59d45dc6302 100644 --- a/components/style/animation.rs +++ b/components/style/animation.rs @@ -369,22 +369,6 @@ impl PropertyAnimation { GenericTimingFunction::Steps(steps, StepPosition::End) => { (time * (steps as f64)).floor() / (steps as f64) }, - GenericTimingFunction::Frames(frames) => { - // https://drafts.csswg.org/css-timing/#frames-timing-functions - let mut out = (time * (frames as f64)).floor() / ((frames - 1) as f64); - if out > 1.0 { - // FIXME: Basically, during the animation sampling process, the input progress - // should be in the range of [0, 1]. However, |time| is not accurate enough - // here, which means |time| could be larger than 1.0 in the last animation - // frame. (It should be equal to 1.0 exactly.) This makes the output of frames - // timing function jumps to the next frame/level. - // However, this solution is still not correct because |time| is possible - // outside the range of [0, 1] after introducing Web Animations. We should fix - // this problem when implementing web animations. - out = 1.0; - } - out - }, GenericTimingFunction::Keyword(keyword) => { let (x1, x2, y1, y2) = keyword.to_bezier(); Bezier::new(x1, x2, y1, y2).solve(time, epsilon) diff --git a/components/style/gecko_bindings/sugar/ns_timing_function.rs b/components/style/gecko_bindings/sugar/ns_timing_function.rs index 635b1f86768..8d7e68b5b44 100644 --- a/components/style/gecko_bindings/sugar/ns_timing_function.rs +++ b/components/style/gecko_bindings/sugar/ns_timing_function.rs @@ -22,17 +22,7 @@ impl nsTimingFunction { self.__bindgen_anon_1 .__bindgen_anon_1 .as_mut() - .mStepsOrFrames = steps; - } - } - - fn set_as_frames(&mut self, frames: u32) { - self.mType = nsTimingFunction_Type::Frames; - unsafe { - self.__bindgen_anon_1 - .__bindgen_anon_1 - .as_mut() - .mStepsOrFrames = frames; + .mSteps = steps; } } @@ -74,10 +64,6 @@ impl From for nsTimingFunction { debug_assert!(steps.value() >= 0); tf.set_as_step(nsTimingFunction_Type::StepEnd, steps.value() as u32); }, - GenericTimingFunction::Frames(frames) => { - debug_assert!(frames.value() >= 2); - tf.set_as_frames(frames.value() as u32); - }, GenericTimingFunction::CubicBezier { x1, y1, x2, y2 } => { tf.set_as_bezier( nsTimingFunction_Type::CubicBezier, @@ -105,7 +91,7 @@ impl From for ComputedTimingFunction { .__bindgen_anon_1 .__bindgen_anon_1 .as_ref() - .mStepsOrFrames + .mSteps }, StepPosition::Start, ), @@ -115,17 +101,10 @@ impl From for ComputedTimingFunction { .__bindgen_anon_1 .__bindgen_anon_1 .as_ref() - .mStepsOrFrames + .mSteps }, StepPosition::End, ), - nsTimingFunction_Type::Frames => GenericTimingFunction::Frames(unsafe { - function - .__bindgen_anon_1 - .__bindgen_anon_1 - .as_ref() - .mStepsOrFrames - }), nsTimingFunction_Type::Ease => GenericTimingFunction::Keyword(TimingKeyword::Ease), nsTimingFunction_Type::Linear => GenericTimingFunction::Keyword(TimingKeyword::Linear), nsTimingFunction_Type::EaseIn => GenericTimingFunction::Keyword(TimingKeyword::EaseIn), diff --git a/components/style/values/generics/transform.rs b/components/style/values/generics/transform.rs index d398b40c3f1..61c9821a816 100644 --- a/components/style/values/generics/transform.rs +++ b/components/style/values/generics/transform.rs @@ -92,7 +92,7 @@ pub struct TransformOrigin { /// A generic timing function. /// -/// +/// https://drafts.csswg.org/css-easing-1/#timing-functions #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)] #[value_info(ty = "TIMING_FUNCTION")] pub enum TimingFunction { @@ -111,9 +111,6 @@ pub enum TimingFunction { #[css(comma, function)] #[value_info(other_values = "step-start,step-end")] Steps(Integer, #[css(skip_if = "is_end")] StepPosition), - /// `frames()` - #[css(comma, function)] - Frames(Integer), } #[allow(missing_docs)] diff --git a/components/style/values/specified/transform.rs b/components/style/values/specified/transform.rs index 835d505836c..749a3d7b265 100644 --- a/components/style/values/specified/transform.rs +++ b/components/style/values/specified/transform.rs @@ -350,19 +350,6 @@ impl OriginComponent { } } -#[cfg(feature = "gecko")] -#[inline] -fn allow_frames_timing() -> bool { - use gecko_bindings::structs::mozilla; - unsafe { mozilla::StaticPrefs_sVarCache_layout_css_frames_timing_enabled } -} - -#[cfg(feature = "servo")] -#[inline] -fn allow_frames_timing() -> bool { - true -} - impl Parse for TimingFunction { fn parse<'i, 't>( context: &ParserContext, @@ -406,14 +393,6 @@ impl Parse for TimingFunction { }).unwrap_or(StepPosition::End); Ok(generic::TimingFunction::Steps(steps, position)) }, - "frames" => { - if allow_frames_timing() { - let frames = Integer::parse_with_minimum(context, i, 2)?; - Ok(generic::TimingFunction::Frames(frames)) - } else { - Err(()) - } - }, _ => Err(()), }).map_err(|()| { location.new_custom_error(StyleParseErrorKind::UnexpectedFunction(function.clone())) @@ -440,9 +419,6 @@ impl ToComputedValue for TimingFunction { generic::TimingFunction::Steps(steps, position) => { generic::TimingFunction::Steps(steps.to_computed_value(context) as u32, position) }, - generic::TimingFunction::Frames(frames) => { - generic::TimingFunction::Frames(frames.to_computed_value(context) as u32) - }, } } @@ -465,9 +441,6 @@ impl ToComputedValue for TimingFunction { Integer::from_computed_value(&(steps as i32)), position, ), - generic::TimingFunction::Frames(frames) => { - generic::TimingFunction::Frames(Integer::from_computed_value(&(frames as i32))) - }, } } } From a20b6a51665c9009ebe65dd1b9dc5d1d1c289faa Mon Sep 17 00:00:00 2001 From: Boris Chiou Date: Fri, 26 Oct 2018 18:03:29 +0000 Subject: [PATCH 06/14] style: Split TimingFunction into a separate file to match spec. TimingFunction is defined in a separate spec (i.e. css-easing), instead of transform, so we move it into a different file. Depends on D9310 Differential Revision: https://phabricator.services.mozilla.com/D9311 --- components/style/animation.rs | 4 +- .../sugar/ns_timing_function.rs | 8 +- components/style/values/computed/easing.rs | 11 ++ components/style/values/computed/mod.rs | 4 +- components/style/values/computed/transform.rs | 3 - components/style/values/generics/easing.rs | 87 +++++++++++++ components/style/values/generics/mod.rs | 1 + components/style/values/generics/transform.rs | 81 ------------ components/style/values/specified/easing.rs | 118 ++++++++++++++++++ components/style/values/specified/mod.rs | 4 +- .../style/values/specified/transform.rs | 102 +-------------- 11 files changed, 230 insertions(+), 193 deletions(-) create mode 100644 components/style/values/computed/easing.rs create mode 100644 components/style/values/generics/easing.rs create mode 100644 components/style/values/specified/easing.rs diff --git a/components/style/animation.rs b/components/style/animation.rs index 59d45dc6302..311d102729f 100644 --- a/components/style/animation.rs +++ b/components/style/animation.rs @@ -28,9 +28,9 @@ use stylesheets::keyframes_rule::{KeyframesAnimation, KeyframesStep, KeyframesSt use timer::Timer; use values::computed::Time; use values::computed::box_::TransitionProperty; -use values::computed::transform::TimingFunction; +use values::computed::TimingFunction; use values::generics::box_::AnimationIterationCount; -use values::generics::transform::{StepPosition, TimingFunction as GenericTimingFunction}; +use values::generics::easing::{StepPosition, TimingFunction as GenericTimingFunction}; /// This structure represents a keyframes animation current iteration state. diff --git a/components/style/gecko_bindings/sugar/ns_timing_function.rs b/components/style/gecko_bindings/sugar/ns_timing_function.rs index 8d7e68b5b44..ff3352ea6c9 100644 --- a/components/style/gecko_bindings/sugar/ns_timing_function.rs +++ b/components/style/gecko_bindings/sugar/ns_timing_function.rs @@ -5,10 +5,10 @@ use gecko_bindings::structs::{nsTimingFunction, nsTimingFunction_Type}; use std::mem; use values::computed::ToComputedValue; -use values::computed::transform::TimingFunction as ComputedTimingFunction; -use values::generics::transform::{StepPosition, TimingKeyword}; -use values::generics::transform::TimingFunction as GenericTimingFunction; -use values::specified::transform::TimingFunction; +use values::computed::easing::TimingFunction as ComputedTimingFunction; +use values::generics::easing::{StepPosition, TimingKeyword}; +use values::generics::easing::TimingFunction as GenericTimingFunction; +use values::specified::easing::TimingFunction; impl nsTimingFunction { fn set_as_step(&mut self, function_type: nsTimingFunction_Type, steps: u32) { diff --git a/components/style/values/computed/easing.rs b/components/style/values/computed/easing.rs new file mode 100644 index 00000000000..a2fefe3a811 --- /dev/null +++ b/components/style/values/computed/easing.rs @@ -0,0 +1,11 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//! Computed types for CSS Easing functions. + +use values::computed::Number; +use values::generics::easing::TimingFunction as GenericTimingFunction; + +/// A computed timing function. +pub type TimingFunction = GenericTimingFunction; diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs index d8cc938c1f3..d1787decf46 100644 --- a/components/style/values/computed/mod.rs +++ b/components/style/values/computed/mod.rs @@ -49,6 +49,7 @@ pub use self::box_::{ScrollSnapType, TouchAction, VerticalAlign, WillChange}; pub use self::color::{Color, ColorPropertyValue, RGBAColor}; pub use self::column::ColumnCount; pub use self::counters::{Content, ContentItem, CounterIncrement, CounterReset}; +pub use self::easing::TimingFunction; pub use self::effects::{BoxShadow, Filter, SimpleShadow}; pub use self::flex::FlexBasis; pub use self::image::{Gradient, GradientItem, Image, ImageLayer, LineDirection, MozImageRect}; @@ -76,7 +77,7 @@ pub use self::table::XSpan; pub use self::text::{InitialLetter, LetterSpacing, LineHeight, MozTabSize}; pub use self::text::{TextAlign, TextEmphasisPosition, TextEmphasisStyle, TextOverflow, WordSpacing}; pub use self::time::Time; -pub use self::transform::{Rotate, Scale, TimingFunction, Transform, TransformOperation}; +pub use self::transform::{Rotate, Scale, Transform, TransformOperation}; pub use self::transform::{TransformOrigin, TransformStyle, Translate}; pub use self::ui::{ColorOrAuto, Cursor, MozForceBrokenImageIcon}; #[cfg(feature = "gecko")] @@ -93,6 +94,7 @@ pub mod box_; pub mod color; pub mod column; pub mod counters; +pub mod easing; pub mod effects; pub mod flex; pub mod font; diff --git a/components/style/values/computed/transform.rs b/components/style/values/computed/transform.rs index a83495a09b5..33c55d8a1e2 100644 --- a/components/style/values/computed/transform.rs +++ b/components/style/values/computed/transform.rs @@ -22,9 +22,6 @@ pub type Transform = generic::Transform; /// The computed value of a CSS `` pub type TransformOrigin = generic::TransformOrigin; -/// A computed timing function. -pub type TimingFunction = generic::TimingFunction; - /// A vector to represent the direction vector (rotate axis) for Rotate3D. pub type DirectionVector = Vector3D; diff --git a/components/style/values/generics/easing.rs b/components/style/values/generics/easing.rs new file mode 100644 index 00000000000..0d0ebdfe44c --- /dev/null +++ b/components/style/values/generics/easing.rs @@ -0,0 +1,87 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//! Generic types for CSS Easing Functions. +//! https://drafts.csswg.org/css-easing/#timing-functions + +use values::CSSFloat; + +/// A generic easing function. +#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)] +#[value_info(ty = "TIMING_FUNCTION")] +pub enum TimingFunction { + /// `linear | ease | ease-in | ease-out | ease-in-out` + Keyword(TimingKeyword), + /// `cubic-bezier(, , , )` + #[allow(missing_docs)] + #[css(comma, function)] + CubicBezier { + x1: Number, + y1: Number, + x2: Number, + y2: Number, + }, + /// `step-start | step-end | steps(, [ start | end ]?)` + #[css(comma, function)] + #[value_info(other_values = "step-start,step-end")] + Steps(Integer, #[css(skip_if = "is_end")] StepPosition), +} + +#[allow(missing_docs)] +#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] +#[derive( + Clone, + Copy, + Debug, + Eq, + MallocSizeOf, + Parse, + PartialEq, + SpecifiedValueInfo, + ToComputedValue, + ToCss, +)] +pub enum TimingKeyword { + Linear, + Ease, + EaseIn, + EaseOut, + EaseInOut, +} + +#[allow(missing_docs)] +#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] +#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToComputedValue, ToCss)] +pub enum StepPosition { + Start, + End, +} + +#[inline] +fn is_end(position: &StepPosition) -> bool { + *position == StepPosition::End +} + +impl TimingFunction { + /// `ease` + #[inline] + pub fn ease() -> Self { + TimingFunction::Keyword(TimingKeyword::Ease) + } +} + +impl TimingKeyword { + /// Returns the keyword as a quadruplet of Bezier point coordinates + /// `(x1, y1, x2, y2)`. + #[inline] + pub fn to_bezier(self) -> (CSSFloat, CSSFloat, CSSFloat, CSSFloat) { + match self { + TimingKeyword::Linear => (0., 0., 1., 1.), + TimingKeyword::Ease => (0.25, 0.1, 0.25, 1.), + TimingKeyword::EaseIn => (0.42, 0., 1., 1.), + TimingKeyword::EaseOut => (0., 0., 0.58, 1.), + TimingKeyword::EaseInOut => (0.42, 0., 0.58, 1.), + } + } +} diff --git a/components/style/values/generics/mod.rs b/components/style/values/generics/mod.rs index 32e389512e4..712b62679d8 100644 --- a/components/style/values/generics/mod.rs +++ b/components/style/values/generics/mod.rs @@ -20,6 +20,7 @@ pub mod box_; pub mod color; pub mod column; pub mod counters; +pub mod easing; pub mod effects; pub mod flex; pub mod font; diff --git a/components/style/values/generics/transform.rs b/components/style/values/generics/transform.rs index 61c9821a816..7a3eaead1cb 100644 --- a/components/style/values/generics/transform.rs +++ b/components/style/values/generics/transform.rs @@ -90,64 +90,6 @@ pub struct TransformOrigin { pub depth: Depth, } -/// A generic timing function. -/// -/// https://drafts.csswg.org/css-easing-1/#timing-functions -#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)] -#[value_info(ty = "TIMING_FUNCTION")] -pub enum TimingFunction { - /// `linear | ease | ease-in | ease-out | ease-in-out` - Keyword(TimingKeyword), - /// `cubic-bezier(, , , )` - #[allow(missing_docs)] - #[css(comma, function)] - CubicBezier { - x1: Number, - y1: Number, - x2: Number, - y2: Number, - }, - /// `step-start | step-end | steps(, [ start | end ]?)` - #[css(comma, function)] - #[value_info(other_values = "step-start,step-end")] - Steps(Integer, #[css(skip_if = "is_end")] StepPosition), -} - -#[allow(missing_docs)] -#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] -#[derive( - Clone, - Copy, - Debug, - Eq, - MallocSizeOf, - Parse, - PartialEq, - SpecifiedValueInfo, - ToComputedValue, - ToCss, -)] -pub enum TimingKeyword { - Linear, - Ease, - EaseIn, - EaseOut, - EaseInOut, -} - -#[allow(missing_docs)] -#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] -#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToComputedValue, ToCss)] -pub enum StepPosition { - Start, - End, -} - -#[inline] -fn is_end(position: &StepPosition) -> bool { - *position == StepPosition::End -} - impl TransformOrigin { /// Returns a new transform origin. pub fn new(horizontal: H, vertical: V, depth: D) -> Self { @@ -159,29 +101,6 @@ impl TransformOrigin { } } -impl TimingFunction { - /// `ease` - #[inline] - pub fn ease() -> Self { - TimingFunction::Keyword(TimingKeyword::Ease) - } -} - -impl TimingKeyword { - /// Returns the keyword as a quadruplet of Bezier point coordinates - /// `(x1, y1, x2, y2)`. - #[inline] - pub fn to_bezier(self) -> (CSSFloat, CSSFloat, CSSFloat, CSSFloat) { - match self { - TimingKeyword::Linear => (0., 0., 1., 1.), - TimingKeyword::Ease => (0.25, 0.1, 0.25, 1.), - TimingKeyword::EaseIn => (0.42, 0., 1., 1.), - TimingKeyword::EaseOut => (0., 0., 0.58, 1.), - TimingKeyword::EaseInOut => (0.42, 0., 0.58, 1.), - } - } -} - #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss)] /// A single operation in the list of a `transform` value pub enum TransformOperation { diff --git a/components/style/values/specified/easing.rs b/components/style/values/specified/easing.rs new file mode 100644 index 00000000000..051029c233d --- /dev/null +++ b/components/style/values/specified/easing.rs @@ -0,0 +1,118 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//! Specified types for CSS Easing functions. + +use cssparser::Parser; +use parser::{Parse, ParserContext}; +use selectors::parser::SelectorParseErrorKind; +use style_traits::{ParseError, StyleParseErrorKind}; +use values::computed::{Context, TimingFunction as ComputedTimingFunction, ToComputedValue}; +use values::generics::easing::{StepPosition, TimingKeyword}; +use values::generics::easing::TimingFunction as GenericTimingFunction; +use values::specified::{Integer, Number}; + +/// A specified timing function. +pub type TimingFunction = GenericTimingFunction; + +impl Parse for TimingFunction { + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + if let Ok(keyword) = input.try(TimingKeyword::parse) { + return Ok(GenericTimingFunction::Keyword(keyword)); + } + if let Ok(ident) = input.try(|i| i.expect_ident_cloned()) { + let position = match_ignore_ascii_case! { &ident, + "step-start" => StepPosition::Start, + "step-end" => StepPosition::End, + _ => { + return Err(input.new_custom_error( + SelectorParseErrorKind::UnexpectedIdent(ident.clone()) + )); + }, + }; + return Ok(GenericTimingFunction::Steps(Integer::new(1), position)); + } + let location = input.current_source_location(); + let function = input.expect_function()?.clone(); + input.parse_nested_block(move |i| { + (match_ignore_ascii_case! { &function, + "cubic-bezier" => { + let x1 = Number::parse(context, i)?; + i.expect_comma()?; + let y1 = Number::parse(context, i)?; + i.expect_comma()?; + let x2 = Number::parse(context, i)?; + i.expect_comma()?; + let y2 = Number::parse(context, i)?; + + if x1.get() < 0.0 || x1.get() > 1.0 || x2.get() < 0.0 || x2.get() > 1.0 { + return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + } + + Ok(GenericTimingFunction::CubicBezier { x1, y1, x2, y2 }) + }, + "steps" => { + let steps = Integer::parse_positive(context, i)?; + let position = i.try(|i| { + i.expect_comma()?; + StepPosition::parse(i) + }).unwrap_or(StepPosition::End); + Ok(GenericTimingFunction::Steps(steps, position)) + }, + _ => Err(()), + }).map_err(|()| { + location.new_custom_error( + StyleParseErrorKind::UnexpectedFunction(function.clone()) + ) + }) + }) + } +} + +impl ToComputedValue for TimingFunction { + type ComputedValue = ComputedTimingFunction; + + #[inline] + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + match *self { + GenericTimingFunction::Keyword(keyword) => GenericTimingFunction::Keyword(keyword), + GenericTimingFunction::CubicBezier { x1, y1, x2, y2 } => { + GenericTimingFunction::CubicBezier { + x1: x1.to_computed_value(context), + y1: y1.to_computed_value(context), + x2: x2.to_computed_value(context), + y2: y2.to_computed_value(context), + } + }, + GenericTimingFunction::Steps(steps, position) => { + GenericTimingFunction::Steps(steps.to_computed_value(context) as u32, position) + }, + } + } + + #[inline] + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + match *computed { + GenericTimingFunction::Keyword(keyword) => GenericTimingFunction::Keyword(keyword), + GenericTimingFunction::CubicBezier { + ref x1, + ref y1, + ref x2, + ref y2, + } => GenericTimingFunction::CubicBezier { + x1: Number::from_computed_value(x1), + y1: Number::from_computed_value(y1), + x2: Number::from_computed_value(x2), + y2: Number::from_computed_value(y2), + }, + GenericTimingFunction::Steps(steps, position) => GenericTimingFunction::Steps( + Integer::from_computed_value(&(steps as i32)), + position, + ), + } + } +} diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index b8c2cd01fe9..9955f7232be 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -43,6 +43,7 @@ pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective, Resize}; pub use self::box_::{ScrollSnapType, TouchAction, TransitionProperty, VerticalAlign, WillChange}; pub use self::color::{Color, ColorPropertyValue, RGBAColor}; pub use self::counters::{Content, ContentItem, CounterIncrement, CounterReset}; +pub use self::easing::TimingFunction; pub use self::effects::{BoxShadow, Filter, SimpleShadow}; pub use self::flex::FlexBasis; #[cfg(feature = "gecko")] @@ -74,7 +75,7 @@ pub use self::text::{InitialLetter, LetterSpacing, LineHeight, MozTabSize, TextA pub use self::text::{TextEmphasisPosition, TextEmphasisStyle}; pub use self::text::{TextAlignKeyword, TextDecorationLine, TextOverflow, WordSpacing}; pub use self::time::Time; -pub use self::transform::{Rotate, Scale, TimingFunction, Transform}; +pub use self::transform::{Rotate, Scale, Transform}; pub use self::transform::{TransformOrigin, TransformStyle, Translate}; pub use self::ui::{ColorOrAuto, Cursor, MozForceBrokenImageIcon}; #[cfg(feature = "gecko")] @@ -93,6 +94,7 @@ pub mod calc; pub mod color; pub mod column; pub mod counters; +pub mod easing; pub mod effects; pub mod flex; pub mod font; diff --git a/components/style/values/specified/transform.rs b/components/style/values/specified/transform.rs index 749a3d7b265..1a789c1959d 100644 --- a/components/style/values/specified/transform.rs +++ b/components/style/values/specified/transform.rs @@ -6,13 +6,11 @@ use cssparser::Parser; use parser::{Parse, ParserContext}; -use selectors::parser::SelectorParseErrorKind; use style_traits::{ParseError, StyleParseErrorKind}; use values::computed::{Context, LengthOrPercentage as ComputedLengthOrPercentage}; use values::computed::{Percentage as ComputedPercentage, ToComputedValue}; -use values::computed::transform::TimingFunction as ComputedTimingFunction; use values::generics::transform as generic; -use values::generics::transform::{Matrix, Matrix3D, StepPosition, TimingKeyword}; +use values::generics::transform::{Matrix, Matrix3D}; use values::specified::{self, Angle, Integer, Length, LengthOrPercentage, Number}; use values::specified::position::{Side, X, Y}; @@ -243,9 +241,6 @@ pub enum OriginComponent { Side(S), } -/// A specified timing function. -pub type TimingFunction = generic::TimingFunction; - impl Parse for TransformOrigin { fn parse<'i, 't>( context: &ParserContext, @@ -350,101 +345,6 @@ impl OriginComponent { } } -impl Parse for TimingFunction { - fn parse<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result> { - if let Ok(keyword) = input.try(TimingKeyword::parse) { - return Ok(generic::TimingFunction::Keyword(keyword)); - } - if let Ok(ident) = input.try(|i| i.expect_ident_cloned()) { - let position = match_ignore_ascii_case! { &ident, - "step-start" => StepPosition::Start, - "step-end" => StepPosition::End, - _ => return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone()))), - }; - return Ok(generic::TimingFunction::Steps(Integer::new(1), position)); - } - let location = input.current_source_location(); - let function = input.expect_function()?.clone(); - input.parse_nested_block(move |i| { - (match_ignore_ascii_case! { &function, - "cubic-bezier" => { - let x1 = Number::parse(context, i)?; - i.expect_comma()?; - let y1 = Number::parse(context, i)?; - i.expect_comma()?; - let x2 = Number::parse(context, i)?; - i.expect_comma()?; - let y2 = Number::parse(context, i)?; - - if x1.get() < 0.0 || x1.get() > 1.0 || x2.get() < 0.0 || x2.get() > 1.0 { - return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError)); - } - - Ok(generic::TimingFunction::CubicBezier { x1, y1, x2, y2 }) - }, - "steps" => { - let steps = Integer::parse_positive(context, i)?; - let position = i.try(|i| { - i.expect_comma()?; - StepPosition::parse(i) - }).unwrap_or(StepPosition::End); - Ok(generic::TimingFunction::Steps(steps, position)) - }, - _ => Err(()), - }).map_err(|()| { - location.new_custom_error(StyleParseErrorKind::UnexpectedFunction(function.clone())) - }) - }) - } -} - -impl ToComputedValue for TimingFunction { - type ComputedValue = ComputedTimingFunction; - - #[inline] - fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { - match *self { - generic::TimingFunction::Keyword(keyword) => generic::TimingFunction::Keyword(keyword), - generic::TimingFunction::CubicBezier { x1, y1, x2, y2 } => { - generic::TimingFunction::CubicBezier { - x1: x1.to_computed_value(context), - y1: y1.to_computed_value(context), - x2: x2.to_computed_value(context), - y2: y2.to_computed_value(context), - } - }, - generic::TimingFunction::Steps(steps, position) => { - generic::TimingFunction::Steps(steps.to_computed_value(context) as u32, position) - }, - } - } - - #[inline] - fn from_computed_value(computed: &Self::ComputedValue) -> Self { - match *computed { - generic::TimingFunction::Keyword(keyword) => generic::TimingFunction::Keyword(keyword), - generic::TimingFunction::CubicBezier { - ref x1, - ref y1, - ref x2, - ref y2, - } => generic::TimingFunction::CubicBezier { - x1: Number::from_computed_value(x1), - y1: Number::from_computed_value(y1), - x2: Number::from_computed_value(x2), - y2: Number::from_computed_value(y2), - }, - generic::TimingFunction::Steps(steps, position) => generic::TimingFunction::Steps( - Integer::from_computed_value(&(steps as i32)), - position, - ), - } - } -} - /// A specified CSS `rotate` pub type Rotate = generic::Rotate; From 2bbcb5c63324aa54a2bc443f5428481fa80f66ad Mon Sep 17 00:00:00 2001 From: Boris Chiou Date: Fri, 26 Oct 2018 18:03:31 +0000 Subject: [PATCH 07/14] style: Replace u32 with computed::Integer for computed::TimingFunction. We make sure the step number is always positive, so using computed::Integer is safe and can derive ToComputedValue. Depends on D9311 Differential Revision: https://phabricator.services.mozilla.com/D9845 --- .../sugar/ns_timing_function.rs | 12 ++--- components/style/values/computed/easing.rs | 4 +- components/style/values/generics/easing.rs | 2 +- components/style/values/specified/easing.rs | 45 ------------------- 4 files changed, 9 insertions(+), 54 deletions(-) diff --git a/components/style/gecko_bindings/sugar/ns_timing_function.rs b/components/style/gecko_bindings/sugar/ns_timing_function.rs index ff3352ea6c9..3eb3b96bb5f 100644 --- a/components/style/gecko_bindings/sugar/ns_timing_function.rs +++ b/components/style/gecko_bindings/sugar/ns_timing_function.rs @@ -11,7 +11,7 @@ use values::generics::easing::TimingFunction as GenericTimingFunction; use values::specified::easing::TimingFunction; impl nsTimingFunction { - fn set_as_step(&mut self, function_type: nsTimingFunction_Type, steps: u32) { + fn set_as_step(&mut self, function_type: nsTimingFunction_Type, steps: i32) { debug_assert!( function_type == nsTimingFunction_Type::StepStart || function_type == nsTimingFunction_Type::StepEnd, @@ -22,7 +22,7 @@ impl nsTimingFunction { self.__bindgen_anon_1 .__bindgen_anon_1 .as_mut() - .mSteps = steps; + .mSteps = steps as u32; } } @@ -58,11 +58,11 @@ impl From for nsTimingFunction { match function { GenericTimingFunction::Steps(steps, StepPosition::Start) => { debug_assert!(steps.value() >= 0); - tf.set_as_step(nsTimingFunction_Type::StepStart, steps.value() as u32); + tf.set_as_step(nsTimingFunction_Type::StepStart, steps.value()); }, GenericTimingFunction::Steps(steps, StepPosition::End) => { debug_assert!(steps.value() >= 0); - tf.set_as_step(nsTimingFunction_Type::StepEnd, steps.value() as u32); + tf.set_as_step(nsTimingFunction_Type::StepEnd, steps.value()); }, GenericTimingFunction::CubicBezier { x1, y1, x2, y2 } => { tf.set_as_bezier( @@ -91,7 +91,7 @@ impl From for ComputedTimingFunction { .__bindgen_anon_1 .__bindgen_anon_1 .as_ref() - .mSteps + .mSteps as i32 }, StepPosition::Start, ), @@ -101,7 +101,7 @@ impl From for ComputedTimingFunction { .__bindgen_anon_1 .__bindgen_anon_1 .as_ref() - .mSteps + .mSteps as i32 }, StepPosition::End, ), diff --git a/components/style/values/computed/easing.rs b/components/style/values/computed/easing.rs index a2fefe3a811..47a24efae66 100644 --- a/components/style/values/computed/easing.rs +++ b/components/style/values/computed/easing.rs @@ -4,8 +4,8 @@ //! Computed types for CSS Easing functions. -use values::computed::Number; +use values::computed::{Integer, Number}; use values::generics::easing::TimingFunction as GenericTimingFunction; /// A computed timing function. -pub type TimingFunction = GenericTimingFunction; +pub type TimingFunction = GenericTimingFunction; diff --git a/components/style/values/generics/easing.rs b/components/style/values/generics/easing.rs index 0d0ebdfe44c..41d60765baa 100644 --- a/components/style/values/generics/easing.rs +++ b/components/style/values/generics/easing.rs @@ -8,7 +8,7 @@ use values::CSSFloat; /// A generic easing function. -#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)] +#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss)] #[value_info(ty = "TIMING_FUNCTION")] pub enum TimingFunction { /// `linear | ease | ease-in | ease-out | ease-in-out` diff --git a/components/style/values/specified/easing.rs b/components/style/values/specified/easing.rs index 051029c233d..48b2bcd3233 100644 --- a/components/style/values/specified/easing.rs +++ b/components/style/values/specified/easing.rs @@ -8,7 +8,6 @@ use cssparser::Parser; use parser::{Parse, ParserContext}; use selectors::parser::SelectorParseErrorKind; use style_traits::{ParseError, StyleParseErrorKind}; -use values::computed::{Context, TimingFunction as ComputedTimingFunction, ToComputedValue}; use values::generics::easing::{StepPosition, TimingKeyword}; use values::generics::easing::TimingFunction as GenericTimingFunction; use values::specified::{Integer, Number}; @@ -72,47 +71,3 @@ impl Parse for TimingFunction { }) } } - -impl ToComputedValue for TimingFunction { - type ComputedValue = ComputedTimingFunction; - - #[inline] - fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { - match *self { - GenericTimingFunction::Keyword(keyword) => GenericTimingFunction::Keyword(keyword), - GenericTimingFunction::CubicBezier { x1, y1, x2, y2 } => { - GenericTimingFunction::CubicBezier { - x1: x1.to_computed_value(context), - y1: y1.to_computed_value(context), - x2: x2.to_computed_value(context), - y2: y2.to_computed_value(context), - } - }, - GenericTimingFunction::Steps(steps, position) => { - GenericTimingFunction::Steps(steps.to_computed_value(context) as u32, position) - }, - } - } - - #[inline] - fn from_computed_value(computed: &Self::ComputedValue) -> Self { - match *computed { - GenericTimingFunction::Keyword(keyword) => GenericTimingFunction::Keyword(keyword), - GenericTimingFunction::CubicBezier { - ref x1, - ref y1, - ref x2, - ref y2, - } => GenericTimingFunction::CubicBezier { - x1: Number::from_computed_value(x1), - y1: Number::from_computed_value(y1), - x2: Number::from_computed_value(x2), - y2: Number::from_computed_value(y2), - }, - GenericTimingFunction::Steps(steps, position) => GenericTimingFunction::Steps( - Integer::from_computed_value(&(steps as i32)), - position, - ), - } - } -} From 3723042937909ec01683b500706d05457d7ab6a7 Mon Sep 17 00:00:00 2001 From: Boris Chiou Date: Fri, 26 Oct 2018 18:03:35 +0000 Subject: [PATCH 08/14] style: Generate StyleTimingFunction and drop ns_timing_function.rs. First, we generate StyleComputedTimingFunction by cbindgen from Rust, and use it in nsTimingFunction, so we could copy it directly without handling the different memory layout. However, we have to rewrite the nsTimingFunction and mozilla::ComputedTimingFunction for this. Second, the rust-bindgen seems cannot generate the correct generic members from complex C++ templates, especially for the nested template struct, (https://github.com/rust-lang-nursery/rust-bindgen/issues/1429) So we have to hide StyleTimingFunction to avoid the compilation errors. Differential Revision: https://phabricator.services.mozilla.com/D9313 --- components/style/cbindgen.toml | 2 + components/style/gecko_bindings/sugar/mod.rs | 1 - .../sugar/ns_timing_function.rs | 139 ------------------ components/style/properties/gecko.mako.rs | 12 +- components/style/values/computed/easing.rs | 7 +- components/style/values/generics/easing.rs | 3 + components/style/values/specified/easing.rs | 24 +++ 7 files changed, 42 insertions(+), 146 deletions(-) delete mode 100644 components/style/gecko_bindings/sugar/ns_timing_function.rs diff --git a/components/style/cbindgen.toml b/components/style/cbindgen.toml index 8bcba69c057..1c8d14e16e8 100644 --- a/components/style/cbindgen.toml +++ b/components/style/cbindgen.toml @@ -42,12 +42,14 @@ include = [ "StyleComputedFontStretchRange", "StyleComputedFontStyleDescriptor", "StyleComputedFontWeightRange", + "StyleComputedTimingFunction", "StyleDisplay", "StyleDisplayMode", "StyleFillRule", "StyleFontDisplay", "StyleFontFaceSourceListComponent", "StyleFontLanguageOverride", + "StyleTimingFunction", "StylePathCommand", "StyleUnicodeRange", ] diff --git a/components/style/gecko_bindings/sugar/mod.rs b/components/style/gecko_bindings/sugar/mod.rs index a455cf2b714..6ca9881b750 100644 --- a/components/style/gecko_bindings/sugar/mod.rs +++ b/components/style/gecko_bindings/sugar/mod.rs @@ -12,7 +12,6 @@ pub mod ns_css_value; mod ns_style_auto_array; pub mod ns_style_coord; mod ns_t_array; -mod ns_timing_function; pub mod origin_flags; pub mod ownership; pub mod refptr; diff --git a/components/style/gecko_bindings/sugar/ns_timing_function.rs b/components/style/gecko_bindings/sugar/ns_timing_function.rs deleted file mode 100644 index 3eb3b96bb5f..00000000000 --- a/components/style/gecko_bindings/sugar/ns_timing_function.rs +++ /dev/null @@ -1,139 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -use gecko_bindings::structs::{nsTimingFunction, nsTimingFunction_Type}; -use std::mem; -use values::computed::ToComputedValue; -use values::computed::easing::TimingFunction as ComputedTimingFunction; -use values::generics::easing::{StepPosition, TimingKeyword}; -use values::generics::easing::TimingFunction as GenericTimingFunction; -use values::specified::easing::TimingFunction; - -impl nsTimingFunction { - fn set_as_step(&mut self, function_type: nsTimingFunction_Type, steps: i32) { - debug_assert!( - function_type == nsTimingFunction_Type::StepStart || - function_type == nsTimingFunction_Type::StepEnd, - "function_type should be step-start or step-end" - ); - self.mType = function_type; - unsafe { - self.__bindgen_anon_1 - .__bindgen_anon_1 - .as_mut() - .mSteps = steps as u32; - } - } - - fn set_as_bezier( - &mut self, - function_type: nsTimingFunction_Type, - x1: f32, - y1: f32, - x2: f32, - y2: f32, - ) { - self.mType = function_type; - unsafe { - let ref mut gecko_cubic_bezier = self.__bindgen_anon_1.mFunc.as_mut(); - gecko_cubic_bezier.mX1 = x1; - gecko_cubic_bezier.mY1 = y1; - gecko_cubic_bezier.mX2 = x2; - gecko_cubic_bezier.mY2 = y2; - } - } -} - -impl From for nsTimingFunction { - fn from(function: ComputedTimingFunction) -> nsTimingFunction { - TimingFunction::from_computed_value(&function).into() - } -} - -impl From for nsTimingFunction { - fn from(function: TimingFunction) -> nsTimingFunction { - let mut tf: nsTimingFunction = unsafe { mem::zeroed() }; - - match function { - GenericTimingFunction::Steps(steps, StepPosition::Start) => { - debug_assert!(steps.value() >= 0); - tf.set_as_step(nsTimingFunction_Type::StepStart, steps.value()); - }, - GenericTimingFunction::Steps(steps, StepPosition::End) => { - debug_assert!(steps.value() >= 0); - tf.set_as_step(nsTimingFunction_Type::StepEnd, steps.value()); - }, - GenericTimingFunction::CubicBezier { x1, y1, x2, y2 } => { - tf.set_as_bezier( - nsTimingFunction_Type::CubicBezier, - x1.get(), - y1.get(), - x2.get(), - y2.get(), - ); - }, - GenericTimingFunction::Keyword(keyword) => { - let (x1, y1, x2, y2) = keyword.to_bezier(); - tf.set_as_bezier(keyword.into(), x1, y1, x2, y2); - }, - } - tf - } -} - -impl From for ComputedTimingFunction { - fn from(function: nsTimingFunction) -> ComputedTimingFunction { - match function.mType { - nsTimingFunction_Type::StepStart => GenericTimingFunction::Steps( - unsafe { - function - .__bindgen_anon_1 - .__bindgen_anon_1 - .as_ref() - .mSteps as i32 - }, - StepPosition::Start, - ), - nsTimingFunction_Type::StepEnd => GenericTimingFunction::Steps( - unsafe { - function - .__bindgen_anon_1 - .__bindgen_anon_1 - .as_ref() - .mSteps as i32 - }, - StepPosition::End, - ), - nsTimingFunction_Type::Ease => GenericTimingFunction::Keyword(TimingKeyword::Ease), - nsTimingFunction_Type::Linear => GenericTimingFunction::Keyword(TimingKeyword::Linear), - nsTimingFunction_Type::EaseIn => GenericTimingFunction::Keyword(TimingKeyword::EaseIn), - nsTimingFunction_Type::EaseOut => { - GenericTimingFunction::Keyword(TimingKeyword::EaseOut) - }, - nsTimingFunction_Type::EaseInOut => { - GenericTimingFunction::Keyword(TimingKeyword::EaseInOut) - }, - nsTimingFunction_Type::CubicBezier => unsafe { - GenericTimingFunction::CubicBezier { - x1: function.__bindgen_anon_1.mFunc.as_ref().mX1, - y1: function.__bindgen_anon_1.mFunc.as_ref().mY1, - x2: function.__bindgen_anon_1.mFunc.as_ref().mX2, - y2: function.__bindgen_anon_1.mFunc.as_ref().mY2, - } - }, - } - } -} - -impl From for nsTimingFunction_Type { - fn from(keyword: TimingKeyword) -> Self { - match keyword { - TimingKeyword::Linear => nsTimingFunction_Type::Linear, - TimingKeyword::Ease => nsTimingFunction_Type::Ease, - TimingKeyword::EaseIn => nsTimingFunction_Type::EaseIn, - TimingKeyword::EaseOut => nsTimingFunction_Type::EaseOut, - TimingKeyword::EaseInOut => nsTimingFunction_Type::EaseInOut, - } - } -} diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index c0c01b5120f..d54c8418689 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -2855,7 +2855,7 @@ fn static_assert() { ${impl_simple_copy('_moz_min_font_size_ratio', 'mMinFontSizeRatio')} -<%def name="impl_copy_animation_or_transition_value(type, ident, gecko_ffi_name)"> +<%def name="impl_copy_animation_or_transition_value(type, ident, gecko_ffi_name, member=None)"> #[allow(non_snake_case)] pub fn copy_${type}_${ident}_from(&mut self, other: &Self) { self.gecko.m${type.capitalize()}s.ensure_len(other.gecko.m${type.capitalize()}s.len()); @@ -2868,7 +2868,11 @@ fn static_assert() { ); for (ours, others) in iter { + % if member: + ours.m${gecko_ffi_name}.${member} = others.m${gecko_ffi_name}.${member}; + % else: ours.m${gecko_ffi_name} = others.m${gecko_ffi_name}; + % endif } } @@ -2923,14 +2927,14 @@ fn static_assert() { self.gecko.m${type.capitalize()}TimingFunctionCount = input_len as u32; for (gecko, servo) in self.gecko.m${type.capitalize()}s.iter_mut().take(input_len as usize).zip(v) { - gecko.mTimingFunction = servo.into(); + gecko.mTimingFunction.mTiming = servo; } } ${impl_animation_or_transition_count(type, 'timing_function', 'TimingFunction')} - ${impl_copy_animation_or_transition_value(type, 'timing_function', 'TimingFunction')} + ${impl_copy_animation_or_transition_value(type, 'timing_function', "TimingFunction", "mTiming")} pub fn ${type}_timing_function_at(&self, index: usize) -> longhands::${type}_timing_function::computed_value::SingleComputedValue { - self.gecko.m${type.capitalize()}s[index].mTimingFunction.into() + self.gecko.m${type.capitalize()}s[index].mTimingFunction.mTiming } diff --git a/components/style/values/computed/easing.rs b/components/style/values/computed/easing.rs index 47a24efae66..394cf3ea41e 100644 --- a/components/style/values/computed/easing.rs +++ b/components/style/values/computed/easing.rs @@ -5,7 +5,10 @@ //! Computed types for CSS Easing functions. use values::computed::{Integer, Number}; -use values::generics::easing::TimingFunction as GenericTimingFunction; +use values::generics::easing; /// A computed timing function. -pub type TimingFunction = GenericTimingFunction; +pub type ComputedTimingFunction = easing::TimingFunction; + +/// An alias of the computed timing function. +pub type TimingFunction = ComputedTimingFunction; diff --git a/components/style/values/generics/easing.rs b/components/style/values/generics/easing.rs index 41d60765baa..018f51bba8f 100644 --- a/components/style/values/generics/easing.rs +++ b/components/style/values/generics/easing.rs @@ -10,6 +10,7 @@ use values::CSSFloat; /// A generic easing function. #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss)] #[value_info(ty = "TIMING_FUNCTION")] +#[repr(u8, C)] pub enum TimingFunction { /// `linear | ease | ease-in | ease-out | ease-in-out` Keyword(TimingKeyword), @@ -42,6 +43,7 @@ pub enum TimingFunction { ToComputedValue, ToCss, )] +#[repr(u8)] pub enum TimingKeyword { Linear, Ease, @@ -53,6 +55,7 @@ pub enum TimingKeyword { #[allow(missing_docs)] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToComputedValue, ToCss)] +#[repr(u8)] pub enum StepPosition { Start, End, diff --git a/components/style/values/specified/easing.rs b/components/style/values/specified/easing.rs index 48b2bcd3233..1bc92e0dd48 100644 --- a/components/style/values/specified/easing.rs +++ b/components/style/values/specified/easing.rs @@ -8,6 +8,7 @@ use cssparser::Parser; use parser::{Parse, ParserContext}; use selectors::parser::SelectorParseErrorKind; use style_traits::{ParseError, StyleParseErrorKind}; +use values::computed::easing::TimingFunction as ComputedTimingFunction; use values::generics::easing::{StepPosition, TimingKeyword}; use values::generics::easing::TimingFunction as GenericTimingFunction; use values::specified::{Integer, Number}; @@ -71,3 +72,26 @@ impl Parse for TimingFunction { }) } } + +// We need this for converting the specified TimingFunction into computed TimingFunction without +// Context (for some FFIs in glue.rs). In fact, we don't really need Context to get the computed +// value of TimingFunction. +impl TimingFunction { + /// Generate the ComputedTimingFunction without Context. + pub fn to_computed_value_without_context(&self) -> ComputedTimingFunction { + match *self { + GenericTimingFunction::Steps(steps, pos) => { + GenericTimingFunction::Steps(steps.value(), pos) + }, + GenericTimingFunction::CubicBezier { x1, y1, x2, y2 } => { + GenericTimingFunction::CubicBezier { + x1: x1.get(), + y1: y1.get(), + x2: x2.get(), + y2: y2.get(), + } + }, + GenericTimingFunction::Keyword(keyword) => GenericTimingFunction::Keyword(keyword), + } + } +} From 0c057d9299e3fbec2677c25b9a76ba61d9dbdad2 Mon Sep 17 00:00:00 2001 From: Boris Chiou Date: Fri, 26 Oct 2018 18:03:37 +0000 Subject: [PATCH 09/14] style: Implement steps(jump-*) functions. 1. Add a new preference, layout.css.step-position-jump.enabled, for step(_, jump-*) timing functions. 2. We still keep JumpEnd and End tags, even though there is no difference between them. Therefore, we could disable the preference if needed. 3. Update the calculation of StepTiming to match the algorithm in the spec. 4. For servo, we implement the correct step function algorithm except for the handling of before_flag. This could be fixed later. Depends on D9313 Differential Revision: https://phabricator.services.mozilla.com/D9314 --- components/style/animation.rs | 38 ++++++++++++++++++--- components/style/values/generics/easing.rs | 25 ++++++++++++-- components/style/values/specified/easing.rs | 12 ++++++- 3 files changed, 67 insertions(+), 8 deletions(-) diff --git a/components/style/animation.rs b/components/style/animation.rs index 311d102729f..057019950a4 100644 --- a/components/style/animation.rs +++ b/components/style/animation.rs @@ -363,11 +363,39 @@ impl PropertyAnimation { GenericTimingFunction::CubicBezier { x1, y1, x2, y2 } => { Bezier::new(x1, y1, x2, y2).solve(time, epsilon) }, - GenericTimingFunction::Steps(steps, StepPosition::Start) => { - (time * (steps as f64)).ceil() / (steps as f64) - }, - GenericTimingFunction::Steps(steps, StepPosition::End) => { - (time * (steps as f64)).floor() / (steps as f64) + GenericTimingFunction::Steps(steps, pos) => { + let mut current_step = (time * (steps as f64)).floor() as i32; + + if pos == StepPosition::Start || + pos == StepPosition::JumpStart || + pos == StepPosition::JumpBoth { + current_step = current_step + 1; + } + + // FIXME: We should update current_step according to the "before flag". + // In order to get the before flag, we have to know the current animation phase + // and whether the iteration is reversed. For now, we skip this calculation. + // (i.e. Treat before_flag is unset,) + // https://drafts.csswg.org/css-easing/#step-timing-function-algo + + if time >= 0.0 && current_step < 0 { + current_step = 0; + } + + let jumps = match pos { + StepPosition::JumpBoth => steps + 1, + StepPosition::JumpNone => steps - 1, + StepPosition::JumpStart | + StepPosition::JumpEnd | + StepPosition::Start | + StepPosition::End => steps, + }; + + if time <= 1.0 && current_step > jumps { + current_step = jumps; + } + + (current_step as f64) / (jumps as f64) }, GenericTimingFunction::Keyword(keyword) => { let (x1, x2, y1, y2) = keyword.to_bezier(); diff --git a/components/style/values/generics/easing.rs b/components/style/values/generics/easing.rs index 018f51bba8f..f4f93a45479 100644 --- a/components/style/values/generics/easing.rs +++ b/components/style/values/generics/easing.rs @@ -6,6 +6,7 @@ //! https://drafts.csswg.org/css-easing/#timing-functions use values::CSSFloat; +use parser::ParserContext; /// A generic easing function. #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss)] @@ -23,7 +24,8 @@ pub enum TimingFunction { x2: Number, y2: Number, }, - /// `step-start | step-end | steps(, [ start | end ]?)` + /// `step-start | step-end | steps(, [ ]?)` + /// ` = jump-start | jump-end | jump-none | jump-both | start | end` #[css(comma, function)] #[value_info(other_values = "step-start,step-end")] Steps(Integer, #[css(skip_if = "is_end")] StepPosition), @@ -52,18 +54,37 @@ pub enum TimingKeyword { EaseInOut, } +#[cfg(feature = "gecko")] +fn step_position_jump_enabled(_context: &ParserContext) -> bool { + use gecko_bindings::structs; + unsafe { structs::StaticPrefs_sVarCache_layout_css_step_position_jump_enabled } +} + +#[cfg(feature = "servo")] +fn step_position_jump_enabled(_context: &ParserContext) -> bool { + false +} + #[allow(missing_docs)] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToComputedValue, ToCss)] #[repr(u8)] pub enum StepPosition { + #[parse(condition = "step_position_jump_enabled")] + JumpStart, + #[parse(condition = "step_position_jump_enabled")] + JumpEnd, + #[parse(condition = "step_position_jump_enabled")] + JumpNone, + #[parse(condition = "step_position_jump_enabled")] + JumpBoth, Start, End, } #[inline] fn is_end(position: &StepPosition) -> bool { - *position == StepPosition::End + *position == StepPosition::JumpEnd || *position == StepPosition::End } impl TimingFunction { diff --git a/components/style/values/specified/easing.rs b/components/style/values/specified/easing.rs index 1bc92e0dd48..017643cdab7 100644 --- a/components/style/values/specified/easing.rs +++ b/components/style/values/specified/easing.rs @@ -59,8 +59,18 @@ impl Parse for TimingFunction { let steps = Integer::parse_positive(context, i)?; let position = i.try(|i| { i.expect_comma()?; - StepPosition::parse(i) + StepPosition::parse(context, i) }).unwrap_or(StepPosition::End); + + // jump-none accepts a positive integer greater than 1. + // FIXME(emilio): The spec asks us to avoid rejecting it at parse + // time except until computed value time. + // + // It's not totally clear it's worth it though, and no other browser + // does this. + if position == StepPosition::JumpNone && 2 > steps.value() { + return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + } Ok(GenericTimingFunction::Steps(steps, position)) }, _ => Err(()), From e7261d51e99a18214a1de022a34db61b21b3adb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Sat, 27 Oct 2018 13:10:57 +0200 Subject: [PATCH 10/14] style: Back out diagnostics for not being helpful enough at diagnosing. The selectors that crash seem just corrupted data structures, none of the selectors from crash dumps make sense, and the ones for which I could trace the source found no issue. --- components/selectors/builder.rs | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/components/selectors/builder.rs b/components/selectors/builder.rs index 584a210152f..33c0ec1b694 100644 --- a/components/selectors/builder.rs +++ b/components/selectors/builder.rs @@ -105,7 +105,7 @@ impl SelectorBuilder { parsed_slotted: bool, ) -> ThinArc> { // Compute the specificity and flags. - let mut spec = SpecificityAndFlags(specificity(&*self, self.simple_selectors.iter())); + let mut spec = SpecificityAndFlags(specificity(self.simple_selectors.iter())); if parsed_pseudo { spec.0 |= HAS_PSEUDO_BIT; } @@ -281,33 +281,26 @@ impl From for u32 { } } -fn specificity(builder: &SelectorBuilder, iter: slice::Iter>) -> u32 +fn specificity(iter: slice::Iter>) -> u32 where Impl: SelectorImpl, { - complex_selector_specificity(builder, iter).into() + complex_selector_specificity(iter).into() } -fn complex_selector_specificity( - builder: &SelectorBuilder, - mut iter: slice::Iter>, -) -> Specificity +fn complex_selector_specificity(iter: slice::Iter>) -> Specificity where Impl: SelectorImpl, { fn simple_selector_specificity( - builder: &SelectorBuilder, simple_selector: &Component, specificity: &mut Specificity, ) where Impl: SelectorImpl, { match *simple_selector { - Component::Combinator(ref combinator) => { - unreachable!( - "Found combinator {:?} in simple selectors vector? {:?}", - combinator, builder, - ); + Component::Combinator(..) => { + unreachable!("Found combinator in simple selectors vector?"); }, Component::PseudoElement(..) | Component::LocalName(..) => { specificity.element_selectors += 1 @@ -361,15 +354,15 @@ where }, Component::Negation(ref negated) => { for ss in negated.iter() { - simple_selector_specificity(builder, &ss, specificity); + simple_selector_specificity(&ss, specificity); } }, } } let mut specificity = Default::default(); - for simple_selector in &mut iter { - simple_selector_specificity(builder, &simple_selector, &mut specificity); + for simple_selector in iter { + simple_selector_specificity(&simple_selector, &mut specificity); } specificity } From 67fae7a2ce9fc15e8703a86504528dad41b6d48f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Sat, 27 Oct 2018 13:16:48 +0200 Subject: [PATCH 11/14] style: Assert earlier to try to get a more helpful stack. --- components/selectors/builder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/selectors/builder.rs b/components/selectors/builder.rs index 33c0ec1b694..b17ed9bfc63 100644 --- a/components/selectors/builder.rs +++ b/components/selectors/builder.rs @@ -72,7 +72,7 @@ impl SelectorBuilder { /// Pushes a simple selector onto the current compound selector. #[inline(always)] pub fn push_simple_selector(&mut self, ss: Component) { - debug_assert!(!ss.is_combinator()); + assert!(!ss.is_combinator()); self.simple_selectors.push(ss); self.current_len += 1; } From 20d9a076d431d4af9eeedc75e7326249edf9829c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Sat, 27 Oct 2018 23:41:30 +0200 Subject: [PATCH 12/14] style: Properly handle exhaustive matches in animation keywords to avoid a warning. Need to do the gecko_inexhaustive thing because those animation properties have an EndGuard_ generated by IPDL I suspect. --- components/style/properties/gecko.mako.rs | 2 ++ components/style/properties/longhands/box.mako.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index d54c8418689..430eb42bdc9 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -3000,7 +3000,9 @@ fn static_assert() { % for value in keyword.gecko_values(): structs::${keyword.gecko_constant(value)} => Keyword::${to_camel_case(value)}, % endfor + % if keyword.gecko_inexhaustive: _ => panic!("Found unexpected value for animation-${ident}"), + % endif } } ${impl_animation_count(ident, gecko_ffi_name)} diff --git a/components/style/properties/longhands/box.mako.rs b/components/style/properties/longhands/box.mako.rs index 8cbba2d8b83..d5e6a9b1106 100644 --- a/components/style/properties/longhands/box.mako.rs +++ b/components/style/properties/longhands/box.mako.rs @@ -247,6 +247,7 @@ ${helpers.single_keyword( gecko_enum_prefix="PlaybackDirection", custom_consts=animation_direction_custom_consts, extra_prefixes=animation_extra_prefixes, + gecko_inexhaustive=True, spec="https://drafts.csswg.org/css-animations/#propdef-animation-direction", allowed_in_keyframe_block=False, )} @@ -271,6 +272,7 @@ ${helpers.single_keyword( vector=True, gecko_enum_prefix="FillMode", extra_prefixes=animation_extra_prefixes, + gecko_inexhaustive=True, spec="https://drafts.csswg.org/css-animations/#propdef-animation-fill-mode", allowed_in_keyframe_block=False, )} From a51b4e754cd572a6d9d303d3be738f3c32abe118 Mon Sep 17 00:00:00 2001 From: Mats Palmgren Date: Sun, 28 Oct 2018 23:00:13 +0100 Subject: [PATCH 13/14] style: Force line-height:normal for themed comboboxes for compat with other UAs. Bug: 1501908 Reviewed-by: emilio --- components/style/style_adjuster.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/components/style/style_adjuster.rs b/components/style/style_adjuster.rs index 7c65798286e..bf5752e6ea7 100644 --- a/components/style/style_adjuster.rs +++ b/components/style/style_adjuster.rs @@ -9,8 +9,10 @@ use app_units::Au; use dom::TElement; use properties::{self, ComputedValues, StyleBuilder}; use properties::computed_value_flags::ComputedValueFlags; +use properties::longhands::_moz_appearance::computed_value::T as Appearance; use properties::longhands::display::computed_value::T as Display; use properties::longhands::float::computed_value::T as Float; +use properties::longhands::line_height::computed_value::T as LineHeight; use properties::longhands::overflow_x::computed_value::T as Overflow; use properties::longhands::position::computed_value::T as Position; @@ -694,6 +696,28 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> { .set_computed_justify_items(parent_justify_items.computed); } + /// If '-webkit-appearance' is 'menulist' on a