mirror of
https://github.com/servo/servo.git
synced 2025-08-06 22:15:33 +01:00
Auto merge of #22036 - emilio:gecko-sync, r=emilio
style: Sync changes from mozilla-central. See each individual commit. This syncs everything except a bindgen update which is blocked on https://github.com/servo/mozjs/pull/159. <!-- 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/22036) <!-- Reviewable:end -->
This commit is contained in:
commit
1628bd5c9c
23 changed files with 441 additions and 594 deletions
|
@ -72,7 +72,7 @@ impl<Impl: SelectorImpl> SelectorBuilder<Impl> {
|
||||||
/// Pushes a simple selector onto the current compound selector.
|
/// Pushes a simple selector onto the current compound selector.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn push_simple_selector(&mut self, ss: Component<Impl>) {
|
pub fn push_simple_selector(&mut self, ss: Component<Impl>) {
|
||||||
debug_assert!(!ss.is_combinator());
|
assert!(!ss.is_combinator());
|
||||||
self.simple_selectors.push(ss);
|
self.simple_selectors.push(ss);
|
||||||
self.current_len += 1;
|
self.current_len += 1;
|
||||||
}
|
}
|
||||||
|
@ -105,7 +105,7 @@ impl<Impl: SelectorImpl> SelectorBuilder<Impl> {
|
||||||
parsed_slotted: bool,
|
parsed_slotted: bool,
|
||||||
) -> ThinArc<SpecificityAndFlags, Component<Impl>> {
|
) -> ThinArc<SpecificityAndFlags, Component<Impl>> {
|
||||||
// Compute the specificity and flags.
|
// 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 {
|
if parsed_pseudo {
|
||||||
spec.0 |= HAS_PSEUDO_BIT;
|
spec.0 |= HAS_PSEUDO_BIT;
|
||||||
}
|
}
|
||||||
|
@ -281,33 +281,26 @@ impl From<Specificity> for u32 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn specificity<Impl>(builder: &SelectorBuilder<Impl>, iter: slice::Iter<Component<Impl>>) -> u32
|
fn specificity<Impl>(iter: slice::Iter<Component<Impl>>) -> u32
|
||||||
where
|
where
|
||||||
Impl: SelectorImpl,
|
Impl: SelectorImpl,
|
||||||
{
|
{
|
||||||
complex_selector_specificity(builder, iter).into()
|
complex_selector_specificity(iter).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn complex_selector_specificity<Impl>(
|
fn complex_selector_specificity<Impl>(iter: slice::Iter<Component<Impl>>) -> Specificity
|
||||||
builder: &SelectorBuilder<Impl>,
|
|
||||||
mut iter: slice::Iter<Component<Impl>>,
|
|
||||||
) -> Specificity
|
|
||||||
where
|
where
|
||||||
Impl: SelectorImpl,
|
Impl: SelectorImpl,
|
||||||
{
|
{
|
||||||
fn simple_selector_specificity<Impl>(
|
fn simple_selector_specificity<Impl>(
|
||||||
builder: &SelectorBuilder<Impl>,
|
|
||||||
simple_selector: &Component<Impl>,
|
simple_selector: &Component<Impl>,
|
||||||
specificity: &mut Specificity,
|
specificity: &mut Specificity,
|
||||||
) where
|
) where
|
||||||
Impl: SelectorImpl,
|
Impl: SelectorImpl,
|
||||||
{
|
{
|
||||||
match *simple_selector {
|
match *simple_selector {
|
||||||
Component::Combinator(ref combinator) => {
|
Component::Combinator(..) => {
|
||||||
unreachable!(
|
unreachable!("Found combinator in simple selectors vector?");
|
||||||
"Found combinator {:?} in simple selectors vector? {:?}",
|
|
||||||
combinator, builder,
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
Component::PseudoElement(..) | Component::LocalName(..) => {
|
Component::PseudoElement(..) | Component::LocalName(..) => {
|
||||||
specificity.element_selectors += 1
|
specificity.element_selectors += 1
|
||||||
|
@ -361,15 +354,15 @@ where
|
||||||
},
|
},
|
||||||
Component::Negation(ref negated) => {
|
Component::Negation(ref negated) => {
|
||||||
for ss in negated.iter() {
|
for ss in negated.iter() {
|
||||||
simple_selector_specificity(builder, &ss, specificity);
|
simple_selector_specificity(&ss, specificity);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut specificity = Default::default();
|
let mut specificity = Default::default();
|
||||||
for simple_selector in &mut iter {
|
for simple_selector in iter {
|
||||||
simple_selector_specificity(builder, &simple_selector, &mut specificity);
|
simple_selector_specificity(&simple_selector, &mut specificity);
|
||||||
}
|
}
|
||||||
specificity
|
specificity
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,10 +27,10 @@ use std::sync::mpsc::Sender;
|
||||||
use stylesheets::keyframes_rule::{KeyframesAnimation, KeyframesStep, KeyframesStepValue};
|
use stylesheets::keyframes_rule::{KeyframesAnimation, KeyframesStep, KeyframesStepValue};
|
||||||
use timer::Timer;
|
use timer::Timer;
|
||||||
use values::computed::Time;
|
use values::computed::Time;
|
||||||
|
use values::computed::TimingFunction;
|
||||||
use values::computed::box_::TransitionProperty;
|
use values::computed::box_::TransitionProperty;
|
||||||
use values::computed::transform::TimingFunction;
|
|
||||||
use values::generics::box_::AnimationIterationCount;
|
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.
|
/// This structure represents a keyframes animation current iteration state.
|
||||||
|
@ -363,27 +363,39 @@ impl PropertyAnimation {
|
||||||
GenericTimingFunction::CubicBezier { x1, y1, x2, y2 } => {
|
GenericTimingFunction::CubicBezier { x1, y1, x2, y2 } => {
|
||||||
Bezier::new(x1, y1, x2, y2).solve(time, epsilon)
|
Bezier::new(x1, y1, x2, y2).solve(time, epsilon)
|
||||||
},
|
},
|
||||||
GenericTimingFunction::Steps(steps, StepPosition::Start) => {
|
GenericTimingFunction::Steps(steps, pos) => {
|
||||||
(time * (steps as f64)).ceil() / (steps as f64)
|
let mut current_step = (time * (steps as f64)).floor() as i32;
|
||||||
},
|
|
||||||
GenericTimingFunction::Steps(steps, StepPosition::End) => {
|
if pos == StepPosition::Start ||
|
||||||
(time * (steps as f64)).floor() / (steps as f64)
|
pos == StepPosition::JumpStart ||
|
||||||
},
|
pos == StepPosition::JumpBoth {
|
||||||
GenericTimingFunction::Frames(frames) => {
|
current_step = current_step + 1;
|
||||||
// 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
|
|
||||||
|
// 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) => {
|
GenericTimingFunction::Keyword(keyword) => {
|
||||||
let (x1, x2, y1, y2) = keyword.to_bezier();
|
let (x1, x2, y1, y2) = keyword.to_bezier();
|
||||||
|
|
|
@ -42,12 +42,14 @@ include = [
|
||||||
"StyleComputedFontStretchRange",
|
"StyleComputedFontStretchRange",
|
||||||
"StyleComputedFontStyleDescriptor",
|
"StyleComputedFontStyleDescriptor",
|
||||||
"StyleComputedFontWeightRange",
|
"StyleComputedFontWeightRange",
|
||||||
|
"StyleComputedTimingFunction",
|
||||||
"StyleDisplay",
|
"StyleDisplay",
|
||||||
"StyleDisplayMode",
|
"StyleDisplayMode",
|
||||||
"StyleFillRule",
|
"StyleFillRule",
|
||||||
"StyleFontDisplay",
|
"StyleFontDisplay",
|
||||||
"StyleFontFaceSourceListComponent",
|
"StyleFontFaceSourceListComponent",
|
||||||
"StyleFontLanguageOverride",
|
"StyleFontLanguageOverride",
|
||||||
|
"StyleTimingFunction",
|
||||||
"StylePathCommand",
|
"StylePathCommand",
|
||||||
"StyleUnicodeRange",
|
"StyleUnicodeRange",
|
||||||
]
|
]
|
||||||
|
|
|
@ -1032,13 +1032,13 @@ impl TrackSize<LengthOrPercentage> {
|
||||||
match *self {
|
match *self {
|
||||||
TrackSize::FitContent(ref lop) => {
|
TrackSize::FitContent(ref lop) => {
|
||||||
// Gecko sets min value to None and max value to the actual value in fit-content
|
// 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/c05d9d61188d32b8/layout/style/nsRuleNode.cpp#7910
|
||||||
gecko_min.set_value(CoordDataValue::None);
|
gecko_min.set_value(CoordDataValue::None);
|
||||||
lop.to_gecko_style_coord(gecko_max);
|
lop.to_gecko_style_coord(gecko_max);
|
||||||
},
|
},
|
||||||
TrackSize::Breadth(ref breadth) => {
|
TrackSize::Breadth(ref breadth) => {
|
||||||
// Set the value to both fields if there's one breadth value
|
// 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/c05d9d61188d32b8/layout/style/nsRuleNode.cpp#7919
|
||||||
breadth.to_gecko_style_coord(gecko_min);
|
breadth.to_gecko_style_coord(gecko_min);
|
||||||
breadth.to_gecko_style_coord(gecko_max);
|
breadth.to_gecko_style_coord(gecko_max);
|
||||||
},
|
},
|
||||||
|
|
|
@ -12,7 +12,6 @@ pub mod ns_css_value;
|
||||||
mod ns_style_auto_array;
|
mod ns_style_auto_array;
|
||||||
pub mod ns_style_coord;
|
pub mod ns_style_coord;
|
||||||
mod ns_t_array;
|
mod ns_t_array;
|
||||||
mod ns_timing_function;
|
|
||||||
pub mod origin_flags;
|
pub mod origin_flags;
|
||||||
pub mod ownership;
|
pub mod ownership;
|
||||||
pub mod refptr;
|
pub mod refptr;
|
||||||
|
|
|
@ -1,160 +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::transform::TimingFunction as ComputedTimingFunction;
|
|
||||||
use values::generics::transform::{StepPosition, TimingKeyword};
|
|
||||||
use values::generics::transform::TimingFunction as GenericTimingFunction;
|
|
||||||
use values::specified::transform::TimingFunction;
|
|
||||||
|
|
||||||
impl nsTimingFunction {
|
|
||||||
fn set_as_step(&mut self, function_type: nsTimingFunction_Type, steps: u32) {
|
|
||||||
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()
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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<ComputedTimingFunction> for nsTimingFunction {
|
|
||||||
fn from(function: ComputedTimingFunction) -> nsTimingFunction {
|
|
||||||
TimingFunction::from_computed_value(&function).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<TimingFunction> 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() as u32);
|
|
||||||
},
|
|
||||||
GenericTimingFunction::Steps(steps, StepPosition::End) => {
|
|
||||||
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,
|
|
||||||
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<nsTimingFunction> 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()
|
|
||||||
.mStepsOrFrames
|
|
||||||
},
|
|
||||||
StepPosition::Start,
|
|
||||||
),
|
|
||||||
nsTimingFunction_Type::StepEnd => GenericTimingFunction::Steps(
|
|
||||||
unsafe {
|
|
||||||
function
|
|
||||||
.__bindgen_anon_1
|
|
||||||
.__bindgen_anon_1
|
|
||||||
.as_ref()
|
|
||||||
.mStepsOrFrames
|
|
||||||
},
|
|
||||||
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),
|
|
||||||
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<TimingKeyword> 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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2394,7 +2394,7 @@ fn static_assert() {
|
||||||
/// from the parent.
|
/// from the parent.
|
||||||
///
|
///
|
||||||
/// This is a port of Gecko's old ComputeScriptLevelSize function:
|
/// 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/c05d9d61188d32b8/layout/style/nsRuleNode.cpp#3103
|
||||||
///
|
///
|
||||||
/// scriptlevel is a property that affects how font-size is inherited. If scriptlevel is
|
/// 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
|
/// +1, for example, it will inherit as the script size multiplier times
|
||||||
|
@ -2855,7 +2855,7 @@ fn static_assert() {
|
||||||
${impl_simple_copy('_moz_min_font_size_ratio', 'mMinFontSizeRatio')}
|
${impl_simple_copy('_moz_min_font_size_ratio', 'mMinFontSizeRatio')}
|
||||||
</%self:impl_trait>
|
</%self:impl_trait>
|
||||||
|
|
||||||
<%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)]
|
#[allow(non_snake_case)]
|
||||||
pub fn copy_${type}_${ident}_from(&mut self, other: &Self) {
|
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());
|
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 {
|
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};
|
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;
|
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) {
|
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_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)
|
pub fn ${type}_timing_function_at(&self, index: usize)
|
||||||
-> longhands::${type}_timing_function::computed_value::SingleComputedValue {
|
-> 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
|
||||||
}
|
}
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
|
@ -2996,7 +3000,9 @@ fn static_assert() {
|
||||||
% for value in keyword.gecko_values():
|
% for value in keyword.gecko_values():
|
||||||
structs::${keyword.gecko_constant(value)} => Keyword::${to_camel_case(value)},
|
structs::${keyword.gecko_constant(value)} => Keyword::${to_camel_case(value)},
|
||||||
% endfor
|
% endfor
|
||||||
|
% if keyword.gecko_inexhaustive:
|
||||||
_ => panic!("Found unexpected value for animation-${ident}"),
|
_ => panic!("Found unexpected value for animation-${ident}"),
|
||||||
|
% endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
${impl_animation_count(ident, gecko_ffi_name)}
|
${impl_animation_count(ident, gecko_ffi_name)}
|
||||||
|
|
|
@ -1356,11 +1356,11 @@ fn is_matched_operation(first: &ComputedTransformOperation, second: &ComputedTra
|
||||||
&TransformOperation::RotateZ(..)) |
|
&TransformOperation::RotateZ(..)) |
|
||||||
(&TransformOperation::Perspective(..),
|
(&TransformOperation::Perspective(..),
|
||||||
&TransformOperation::Perspective(..)) => true,
|
&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_translate() && b.is_translate() => true,
|
||||||
(a, b) if a.is_scale() && b.is_scale() => true,
|
(a, b) if a.is_scale() && b.is_scale() => true,
|
||||||
(a, b) if a.is_rotate() && b.is_rotate() => 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
|
_ => false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1829,7 +1829,7 @@ impl Animate for Quaternion {
|
||||||
self.3 * other.3)
|
self.3 * other.3)
|
||||||
.min(1.0).max(-1.0);
|
.min(1.0).max(-1.0);
|
||||||
|
|
||||||
if dot == 1.0 {
|
if dot.abs() == 1.0 {
|
||||||
return Ok(*self);
|
return Ok(*self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2468,79 +2468,112 @@ impl Animate for ComputedTransform {
|
||||||
return Ok(Transform(result));
|
return Ok(Transform(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://drafts.csswg.org/css-transforms-1/#transform-transform-neutral-extend-animation
|
let this = Cow::Borrowed(&self.0);
|
||||||
fn match_operations_if_possible<'a>(
|
let other = Cow::Borrowed(&other.0);
|
||||||
this: &mut Cow<'a, Vec<ComputedTransformOperation>>,
|
|
||||||
other: &mut Cow<'a, Vec<ComputedTransformOperation>>,
|
|
||||||
) -> bool {
|
|
||||||
if !this.iter().zip(other.iter()).all(|(this, other)| is_matched_operation(this, other)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if this.len() == other.len() {
|
// Interpolate the common prefix
|
||||||
return true;
|
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::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
let (shorter, longer) =
|
// Deal with the remainders
|
||||||
if this.len() < other.len() {
|
let this_remainder = if this.len() > result.len() {
|
||||||
(this.to_mut(), other)
|
Some(&this[result.len()..])
|
||||||
} else {
|
} else {
|
||||||
(other.to_mut(), this)
|
None
|
||||||
};
|
};
|
||||||
|
let other_remainder = if other.len() > result.len() {
|
||||||
|
Some(&other[result.len()..])
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
shorter.reserve(longer.len());
|
match (this_remainder, other_remainder) {
|
||||||
for op in longer.iter().skip(shorter.len()) {
|
// If there is a remainder from *both* lists we must have had mismatched functions.
|
||||||
shorter.push(op.to_animated_zero().unwrap());
|
// => Add the remainders to a suitable ___Matrix function.
|
||||||
}
|
(Some(this_remainder), Some(other_remainder)) => match procedure {
|
||||||
|
Procedure::Add => {
|
||||||
// The resulting operations won't be matched regardless if the
|
debug_assert!(false, "Should have already dealt with add by the point");
|
||||||
// extended component is already InterpolateMatrix /
|
return Err(());
|
||||||
// AccumulateMatrix.
|
}
|
||||||
//
|
Procedure::Interpolate { progress } => {
|
||||||
// Otherwise they should be matching operations all the time.
|
result.push(TransformOperation::InterpolateMatrix {
|
||||||
let already_mismatched = matches!(
|
from_list: Transform(this_remainder.to_vec()),
|
||||||
longer[0],
|
to_list: Transform(other_remainder.to_vec()),
|
||||||
TransformOperation::InterpolateMatrix { .. } |
|
progress: Percentage(progress as f32),
|
||||||
TransformOperation::AccumulateMatrix { .. }
|
});
|
||||||
);
|
}
|
||||||
|
Procedure::Accumulate { count } => {
|
||||||
debug_assert_eq!(
|
result.push(TransformOperation::AccumulateMatrix {
|
||||||
!already_mismatched,
|
from_list: Transform(this_remainder.to_vec()),
|
||||||
longer.iter().zip(shorter.iter()).all(|(this, other)| is_matched_operation(this, other)),
|
to_list: Transform(other_remainder.to_vec()),
|
||||||
"ToAnimatedZero should generate matched operations"
|
count: cmp::min(count, i32::max_value() as u64) as i32,
|
||||||
);
|
});
|
||||||
|
}
|
||||||
!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::<Result<Vec<_>, _>>()?
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
}]))
|
|
||||||
},
|
},
|
||||||
|
// 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::<Result<Vec<_>, _>>()?,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
(None, None) => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(Transform(result))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -247,6 +247,7 @@ ${helpers.single_keyword(
|
||||||
gecko_enum_prefix="PlaybackDirection",
|
gecko_enum_prefix="PlaybackDirection",
|
||||||
custom_consts=animation_direction_custom_consts,
|
custom_consts=animation_direction_custom_consts,
|
||||||
extra_prefixes=animation_extra_prefixes,
|
extra_prefixes=animation_extra_prefixes,
|
||||||
|
gecko_inexhaustive=True,
|
||||||
spec="https://drafts.csswg.org/css-animations/#propdef-animation-direction",
|
spec="https://drafts.csswg.org/css-animations/#propdef-animation-direction",
|
||||||
allowed_in_keyframe_block=False,
|
allowed_in_keyframe_block=False,
|
||||||
)}
|
)}
|
||||||
|
@ -258,6 +259,7 @@ ${helpers.single_keyword(
|
||||||
animation_value_type="none",
|
animation_value_type="none",
|
||||||
vector=True,
|
vector=True,
|
||||||
extra_prefixes=animation_extra_prefixes,
|
extra_prefixes=animation_extra_prefixes,
|
||||||
|
gecko_enum_prefix="StyleAnimationPlayState",
|
||||||
spec="https://drafts.csswg.org/css-animations/#propdef-animation-play-state",
|
spec="https://drafts.csswg.org/css-animations/#propdef-animation-play-state",
|
||||||
allowed_in_keyframe_block=False,
|
allowed_in_keyframe_block=False,
|
||||||
)}
|
)}
|
||||||
|
@ -270,6 +272,7 @@ ${helpers.single_keyword(
|
||||||
vector=True,
|
vector=True,
|
||||||
gecko_enum_prefix="FillMode",
|
gecko_enum_prefix="FillMode",
|
||||||
extra_prefixes=animation_extra_prefixes,
|
extra_prefixes=animation_extra_prefixes,
|
||||||
|
gecko_inexhaustive=True,
|
||||||
spec="https://drafts.csswg.org/css-animations/#propdef-animation-fill-mode",
|
spec="https://drafts.csswg.org/css-animations/#propdef-animation-fill-mode",
|
||||||
allowed_in_keyframe_block=False,
|
allowed_in_keyframe_block=False,
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -694,6 +694,34 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
|
||||||
.set_computed_justify_items(parent_justify_items.computed);
|
.set_computed_justify_items(parent_justify_items.computed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If '-webkit-appearance' is 'menulist' on a <select> element then
|
||||||
|
/// the computed value of 'line-height' is 'normal'.
|
||||||
|
///
|
||||||
|
/// https://github.com/w3c/csswg-drafts/issues/3257
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
fn adjust_for_appearance<E>(&mut self, element: Option<E>)
|
||||||
|
where
|
||||||
|
E: TElement,
|
||||||
|
{
|
||||||
|
use properties::longhands::_moz_appearance::computed_value::T as Appearance;
|
||||||
|
use properties::longhands::line_height::computed_value::T as LineHeight;
|
||||||
|
|
||||||
|
if self.style.get_box().clone__moz_appearance() == Appearance::Menulist {
|
||||||
|
if self.style.get_inherited_text().clone_line_height() == LineHeight::normal() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if self.style.pseudo.is_some() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let is_html_select_element =
|
||||||
|
element.map_or(false, |e| e.is_html_element() && e.local_name() == &*local_name!("select"));
|
||||||
|
if !is_html_select_element {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.style.mutate_inherited_text().set_line_height(LineHeight::normal());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Adjusts the style to account for various fixups that don't fit naturally
|
/// Adjusts the style to account for various fixups that don't fit naturally
|
||||||
/// into the cascade.
|
/// into the cascade.
|
||||||
///
|
///
|
||||||
|
@ -755,6 +783,10 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
|
||||||
{
|
{
|
||||||
self.adjust_for_text_decorations_in_effect();
|
self.adjust_for_text_decorations_in_effect();
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
{
|
||||||
|
self.adjust_for_appearance(element);
|
||||||
|
}
|
||||||
self.set_bits();
|
self.set_bits();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
14
components/style/values/computed/easing.rs
Normal file
14
components/style/values/computed/easing.rs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
/* 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::{Integer, Number};
|
||||||
|
use values::generics::easing;
|
||||||
|
|
||||||
|
/// A computed timing function.
|
||||||
|
pub type ComputedTimingFunction = easing::TimingFunction<Integer, Number>;
|
||||||
|
|
||||||
|
/// An alias of the computed timing function.
|
||||||
|
pub type TimingFunction = ComputedTimingFunction;
|
|
@ -49,6 +49,7 @@ pub use self::box_::{ScrollSnapType, TouchAction, VerticalAlign, WillChange};
|
||||||
pub use self::color::{Color, ColorPropertyValue, RGBAColor};
|
pub use self::color::{Color, ColorPropertyValue, RGBAColor};
|
||||||
pub use self::column::ColumnCount;
|
pub use self::column::ColumnCount;
|
||||||
pub use self::counters::{Content, ContentItem, CounterIncrement, CounterReset};
|
pub use self::counters::{Content, ContentItem, CounterIncrement, CounterReset};
|
||||||
|
pub use self::easing::TimingFunction;
|
||||||
pub use self::effects::{BoxShadow, Filter, SimpleShadow};
|
pub use self::effects::{BoxShadow, Filter, SimpleShadow};
|
||||||
pub use self::flex::FlexBasis;
|
pub use self::flex::FlexBasis;
|
||||||
pub use self::image::{Gradient, GradientItem, Image, ImageLayer, LineDirection, MozImageRect};
|
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::{InitialLetter, LetterSpacing, LineHeight, MozTabSize};
|
||||||
pub use self::text::{TextAlign, TextEmphasisPosition, TextEmphasisStyle, TextOverflow, WordSpacing};
|
pub use self::text::{TextAlign, TextEmphasisPosition, TextEmphasisStyle, TextOverflow, WordSpacing};
|
||||||
pub use self::time::Time;
|
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::transform::{TransformOrigin, TransformStyle, Translate};
|
||||||
pub use self::ui::{ColorOrAuto, Cursor, MozForceBrokenImageIcon};
|
pub use self::ui::{ColorOrAuto, Cursor, MozForceBrokenImageIcon};
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
|
@ -93,6 +94,7 @@ pub mod box_;
|
||||||
pub mod color;
|
pub mod color;
|
||||||
pub mod column;
|
pub mod column;
|
||||||
pub mod counters;
|
pub mod counters;
|
||||||
|
pub mod easing;
|
||||||
pub mod effects;
|
pub mod effects;
|
||||||
pub mod flex;
|
pub mod flex;
|
||||||
pub mod font;
|
pub mod font;
|
||||||
|
|
|
@ -22,9 +22,6 @@ pub type Transform = generic::Transform<TransformOperation>;
|
||||||
/// The computed value of a CSS `<transform-origin>`
|
/// The computed value of a CSS `<transform-origin>`
|
||||||
pub type TransformOrigin = generic::TransformOrigin<LengthOrPercentage, LengthOrPercentage, Length>;
|
pub type TransformOrigin = generic::TransformOrigin<LengthOrPercentage, LengthOrPercentage, Length>;
|
||||||
|
|
||||||
/// A computed timing function.
|
|
||||||
pub type TimingFunction = generic::TimingFunction<u32, Number>;
|
|
||||||
|
|
||||||
/// A vector to represent the direction vector (rotate axis) for Rotate3D.
|
/// A vector to represent the direction vector (rotate axis) for Rotate3D.
|
||||||
pub type DirectionVector = Vector3D<CSSFloat>;
|
pub type DirectionVector = Vector3D<CSSFloat>;
|
||||||
|
|
||||||
|
|
111
components/style/values/generics/easing.rs
Normal file
111
components/style/values/generics/easing.rs
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
/* 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 parser::ParserContext;
|
||||||
|
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<Integer, Number> {
|
||||||
|
/// `linear | ease | ease-in | ease-out | ease-in-out`
|
||||||
|
Keyword(TimingKeyword),
|
||||||
|
/// `cubic-bezier(<number>, <number>, <number>, <number>)`
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[css(comma, function)]
|
||||||
|
CubicBezier {
|
||||||
|
x1: Number,
|
||||||
|
y1: Number,
|
||||||
|
x2: Number,
|
||||||
|
y2: Number,
|
||||||
|
},
|
||||||
|
/// `step-start | step-end | steps(<integer>, [ <step-position> ]?)`
|
||||||
|
/// `<step-position> = 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),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
|
||||||
|
#[derive(
|
||||||
|
Clone,
|
||||||
|
Copy,
|
||||||
|
Debug,
|
||||||
|
Eq,
|
||||||
|
MallocSizeOf,
|
||||||
|
Parse,
|
||||||
|
PartialEq,
|
||||||
|
SpecifiedValueInfo,
|
||||||
|
ToComputedValue,
|
||||||
|
ToCss,
|
||||||
|
)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum TimingKeyword {
|
||||||
|
Linear,
|
||||||
|
Ease,
|
||||||
|
EaseIn,
|
||||||
|
EaseOut,
|
||||||
|
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::JumpEnd || *position == StepPosition::End
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Integer, Number> TimingFunction<Integer, Number> {
|
||||||
|
/// `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.),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,6 +20,7 @@ pub mod box_;
|
||||||
pub mod color;
|
pub mod color;
|
||||||
pub mod column;
|
pub mod column;
|
||||||
pub mod counters;
|
pub mod counters;
|
||||||
|
pub mod easing;
|
||||||
pub mod effects;
|
pub mod effects;
|
||||||
pub mod flex;
|
pub mod flex;
|
||||||
pub mod font;
|
pub mod font;
|
||||||
|
|
|
@ -90,67 +90,6 @@ pub struct TransformOrigin<H, V, Depth> {
|
||||||
pub depth: Depth,
|
pub depth: Depth,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A generic timing function.
|
|
||||||
///
|
|
||||||
/// <https://drafts.csswg.org/css-timing-1/#single-timing-function-production>
|
|
||||||
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
|
|
||||||
#[value_info(ty = "TIMING_FUNCTION")]
|
|
||||||
pub enum TimingFunction<Integer, Number> {
|
|
||||||
/// `linear | ease | ease-in | ease-out | ease-in-out`
|
|
||||||
Keyword(TimingKeyword),
|
|
||||||
/// `cubic-bezier(<number>, <number>, <number>, <number>)`
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
#[css(comma, function)]
|
|
||||||
CubicBezier {
|
|
||||||
x1: Number,
|
|
||||||
y1: Number,
|
|
||||||
x2: Number,
|
|
||||||
y2: Number,
|
|
||||||
},
|
|
||||||
/// `step-start | step-end | steps(<integer>, [ start | end ]?)`
|
|
||||||
#[css(comma, function)]
|
|
||||||
#[value_info(other_values = "step-start,step-end")]
|
|
||||||
Steps(Integer, #[css(skip_if = "is_end")] StepPosition),
|
|
||||||
/// `frames(<integer>)`
|
|
||||||
#[css(comma, function)]
|
|
||||||
Frames(Integer),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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<H, V, D> TransformOrigin<H, V, D> {
|
impl<H, V, D> TransformOrigin<H, V, D> {
|
||||||
/// Returns a new transform origin.
|
/// Returns a new transform origin.
|
||||||
pub fn new(horizontal: H, vertical: V, depth: D) -> Self {
|
pub fn new(horizontal: H, vertical: V, depth: D) -> Self {
|
||||||
|
@ -162,29 +101,6 @@ impl<H, V, D> TransformOrigin<H, V, D> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Integer, Number> TimingFunction<Integer, Number> {
|
|
||||||
/// `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)]
|
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss)]
|
||||||
/// A single operation in the list of a `transform` value
|
/// A single operation in the list of a `transform` value
|
||||||
pub enum TransformOperation<Angle, Number, Length, Integer, LengthOrPercentage> {
|
pub enum TransformOperation<Angle, Number, Length, Integer, LengthOrPercentage> {
|
||||||
|
|
107
components/style/values/specified/easing.rs
Normal file
107
components/style/values/specified/easing.rs
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
/* 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::easing::TimingFunction as ComputedTimingFunction;
|
||||||
|
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<Integer, Number>;
|
||||||
|
|
||||||
|
impl Parse for TimingFunction {
|
||||||
|
fn parse<'i, 't>(
|
||||||
|
context: &ParserContext,
|
||||||
|
input: &mut Parser<'i, 't>,
|
||||||
|
) -> Result<Self, ParseError<'i>> {
|
||||||
|
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(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(()),
|
||||||
|
}).map_err(|()| {
|
||||||
|
location.new_custom_error(
|
||||||
|
StyleParseErrorKind::UnexpectedFunction(function.clone())
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -753,8 +753,8 @@ impl ToComputedValue for KeywordSize {
|
||||||
// The tables in this function are originally from
|
// The tables in this function are originally from
|
||||||
// nsRuleNode::CalcFontPointSize in Gecko:
|
// nsRuleNode::CalcFontPointSize in Gecko:
|
||||||
//
|
//
|
||||||
// https://dxr.mozilla.org/mozilla-central/rev/35fbf14b9/layout/style/nsRuleNode.cpp#3262-3336
|
// https://searchfox.org/mozilla-central/rev/c05d9d61188d32b8/layout/style/nsRuleNode.cpp#3150
|
||||||
|
//
|
||||||
// Mapping from base size and HTML size to pixels
|
// Mapping from base size and HTML size to pixels
|
||||||
// The first index is (base_size - 9), the second is the
|
// The first index is (base_size - 9), the second is the
|
||||||
// HTML size. "0" is CSS keyword xx-small, not HTML size 0,
|
// HTML size. "0" is CSS keyword xx-small, not HTML size 0,
|
||||||
|
|
|
@ -43,6 +43,7 @@ pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective, Resize};
|
||||||
pub use self::box_::{ScrollSnapType, TouchAction, TransitionProperty, VerticalAlign, WillChange};
|
pub use self::box_::{ScrollSnapType, TouchAction, TransitionProperty, VerticalAlign, WillChange};
|
||||||
pub use self::color::{Color, ColorPropertyValue, RGBAColor};
|
pub use self::color::{Color, ColorPropertyValue, RGBAColor};
|
||||||
pub use self::counters::{Content, ContentItem, CounterIncrement, CounterReset};
|
pub use self::counters::{Content, ContentItem, CounterIncrement, CounterReset};
|
||||||
|
pub use self::easing::TimingFunction;
|
||||||
pub use self::effects::{BoxShadow, Filter, SimpleShadow};
|
pub use self::effects::{BoxShadow, Filter, SimpleShadow};
|
||||||
pub use self::flex::FlexBasis;
|
pub use self::flex::FlexBasis;
|
||||||
#[cfg(feature = "gecko")]
|
#[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::{TextEmphasisPosition, TextEmphasisStyle};
|
||||||
pub use self::text::{TextAlignKeyword, TextDecorationLine, TextOverflow, WordSpacing};
|
pub use self::text::{TextAlignKeyword, TextDecorationLine, TextOverflow, WordSpacing};
|
||||||
pub use self::time::Time;
|
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::transform::{TransformOrigin, TransformStyle, Translate};
|
||||||
pub use self::ui::{ColorOrAuto, Cursor, MozForceBrokenImageIcon};
|
pub use self::ui::{ColorOrAuto, Cursor, MozForceBrokenImageIcon};
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
|
@ -93,6 +94,7 @@ pub mod calc;
|
||||||
pub mod color;
|
pub mod color;
|
||||||
pub mod column;
|
pub mod column;
|
||||||
pub mod counters;
|
pub mod counters;
|
||||||
|
pub mod easing;
|
||||||
pub mod effects;
|
pub mod effects;
|
||||||
pub mod flex;
|
pub mod flex;
|
||||||
pub mod font;
|
pub mod font;
|
||||||
|
|
|
@ -6,13 +6,11 @@
|
||||||
|
|
||||||
use cssparser::Parser;
|
use cssparser::Parser;
|
||||||
use parser::{Parse, ParserContext};
|
use parser::{Parse, ParserContext};
|
||||||
use selectors::parser::SelectorParseErrorKind;
|
|
||||||
use style_traits::{ParseError, StyleParseErrorKind};
|
use style_traits::{ParseError, StyleParseErrorKind};
|
||||||
use values::computed::{Context, LengthOrPercentage as ComputedLengthOrPercentage};
|
use values::computed::{Context, LengthOrPercentage as ComputedLengthOrPercentage};
|
||||||
use values::computed::{Percentage as ComputedPercentage, ToComputedValue};
|
use values::computed::{Percentage as ComputedPercentage, ToComputedValue};
|
||||||
use values::computed::transform::TimingFunction as ComputedTimingFunction;
|
|
||||||
use values::generics::transform as generic;
|
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::{self, Angle, Integer, Length, LengthOrPercentage, Number};
|
||||||
use values::specified::position::{Side, X, Y};
|
use values::specified::position::{Side, X, Y};
|
||||||
|
|
||||||
|
@ -243,9 +241,6 @@ pub enum OriginComponent<S> {
|
||||||
Side(S),
|
Side(S),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A specified timing function.
|
|
||||||
pub type TimingFunction = generic::TimingFunction<Integer, Number>;
|
|
||||||
|
|
||||||
impl Parse for TransformOrigin {
|
impl Parse for TransformOrigin {
|
||||||
fn parse<'i, 't>(
|
fn parse<'i, 't>(
|
||||||
context: &ParserContext,
|
context: &ParserContext,
|
||||||
|
@ -350,128 +345,6 @@ impl<S> OriginComponent<S> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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,
|
|
||||||
input: &mut Parser<'i, 't>,
|
|
||||||
) -> Result<Self, ParseError<'i>> {
|
|
||||||
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))
|
|
||||||
},
|
|
||||||
"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()))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
},
|
|
||||||
generic::TimingFunction::Frames(frames) => {
|
|
||||||
generic::TimingFunction::Frames(frames.to_computed_value(context) as u32)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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,
|
|
||||||
),
|
|
||||||
generic::TimingFunction::Frames(frames) => {
|
|
||||||
generic::TimingFunction::Frames(Integer::from_computed_value(&(frames as i32)))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A specified CSS `rotate`
|
/// A specified CSS `rotate`
|
||||||
pub type Rotate = generic::Rotate<Number, Angle>;
|
pub type Rotate = generic::Rotate<Number, Angle>;
|
||||||
|
|
||||||
|
|
|
@ -35,19 +35,3 @@ fn test_steps() {
|
||||||
assert!(parse(transition_timing_function::parse, "steps(-1)").is_err());
|
assert!(parse(transition_timing_function::parse, "steps(-1)").is_err());
|
||||||
assert!(parse(transition_timing_function::parse, "steps(1, middle)").is_err());
|
assert!(parse(transition_timing_function::parse, "steps(1, middle)").is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_frames() {
|
|
||||||
assert_roundtrip_with_context!(transition_timing_function::parse, "frames( 2 )", "frames(2)");
|
|
||||||
assert_roundtrip_with_context!(transition_timing_function::parse, "frames(10000)");
|
|
||||||
|
|
||||||
// Frames number must be an integer greater than 1
|
|
||||||
assert!(parse(transition_timing_function::parse, "frames(1)").is_err());
|
|
||||||
assert!(parse(transition_timing_function::parse, "frames(-2)").is_err());
|
|
||||||
assert!(parse(transition_timing_function::parse, "frames()").is_err());
|
|
||||||
assert!(parse(transition_timing_function::parse, "frames(,)").is_err());
|
|
||||||
assert!(parse(transition_timing_function::parse, "frames(a)").is_err());
|
|
||||||
assert!(parse(transition_timing_function::parse, "frames(2.0)").is_err());
|
|
||||||
assert!(parse(transition_timing_function::parse, "frames(2.5)").is_err());
|
|
||||||
assert!(parse(transition_timing_function::parse, "frames(2 3)").is_err());
|
|
||||||
}
|
|
||||||
|
|
|
@ -707,86 +707,6 @@ mod shorthand_serialization {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod transition {
|
|
||||||
pub use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn transition_should_serialize_all_available_properties() {
|
|
||||||
let block_text = "transition-property: margin-left; \
|
|
||||||
transition-duration: 3s; \
|
|
||||||
transition-delay: 4s; \
|
|
||||||
transition-timing-function: cubic-bezier(0.2, 5, 0.5, 2);";
|
|
||||||
|
|
||||||
let block = parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap();
|
|
||||||
|
|
||||||
let serialization = block.to_css_string();
|
|
||||||
|
|
||||||
assert_eq!(serialization, "transition: margin-left 3s cubic-bezier(0.2, 5, 0.5, 2) 4s;");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn serialize_multiple_transitions() {
|
|
||||||
let block_text = "transition-property: margin-left, width; \
|
|
||||||
transition-duration: 3s, 2s; \
|
|
||||||
transition-delay: 4s, 5s; \
|
|
||||||
transition-timing-function: cubic-bezier(0.2, 5, 0.5, 2), ease;";
|
|
||||||
|
|
||||||
let block = parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap();
|
|
||||||
|
|
||||||
let serialization = block.to_css_string();
|
|
||||||
|
|
||||||
assert_eq!(serialization, "transition: \
|
|
||||||
margin-left 3s cubic-bezier(0.2, 5, 0.5, 2) 4s, \
|
|
||||||
width 2s ease 5s;");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn serialize_multiple_transitions_unequal_property_lists() {
|
|
||||||
// When the lengths of property values are different, the shorthand serialization
|
|
||||||
// should not be used. Previously the implementation cycled values if the lists were
|
|
||||||
// uneven. This is incorrect, in that we should serialize to a shorthand only when the
|
|
||||||
// lists have the same length (this affects background, transition and animation).
|
|
||||||
// https://github.com/servo/servo/issues/15398 )
|
|
||||||
// The duration below has 1 extra value.
|
|
||||||
let block_text = "transition-property: margin-left, width; \
|
|
||||||
transition-duration: 3s, 2s, 4s; \
|
|
||||||
transition-delay: 4s, 5s; \
|
|
||||||
transition-timing-function: cubic-bezier(0.2, 5, 0.5, 2), ease;";
|
|
||||||
|
|
||||||
let block = parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap();
|
|
||||||
|
|
||||||
let serialization = block.to_css_string();
|
|
||||||
|
|
||||||
assert_eq!(serialization, block_text);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn transition_should_serialize_acceptable_step_timing_function() {
|
|
||||||
let block_text = "transition-property: margin-left; \
|
|
||||||
transition-duration: 3s; \
|
|
||||||
transition-delay: 4s; \
|
|
||||||
transition-timing-function: steps(2, start);";
|
|
||||||
let block = parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap();
|
|
||||||
|
|
||||||
let serialization = block.to_css_string();
|
|
||||||
|
|
||||||
assert_eq!(serialization, "transition: margin-left 3s steps(2, start) 4s;");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn transition_should_serialize_acceptable_frames_timing_function() {
|
|
||||||
let block_text = "transition-property: margin-left; \
|
|
||||||
transition-duration: 3s; \
|
|
||||||
transition-delay: 4s; \
|
|
||||||
transition-timing-function: frames(2);";
|
|
||||||
let block = parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap();
|
|
||||||
|
|
||||||
let serialization = block.to_css_string();
|
|
||||||
|
|
||||||
assert_eq!(serialization, "transition: margin-left 3s frames(2) 4s;");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mod keywords {
|
mod keywords {
|
||||||
pub use super::*;
|
pub use super::*;
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -28,7 +28,7 @@ use style::stylesheets::keyframes_rule::{Keyframe, KeyframeSelector, KeyframePer
|
||||||
use style::values::{KeyframesName, CustomIdent};
|
use style::values::{KeyframesName, CustomIdent};
|
||||||
use style::values::computed::Percentage;
|
use style::values::computed::Percentage;
|
||||||
use style::values::specified::{LengthOrPercentageOrAuto, PositionComponent};
|
use style::values::specified::{LengthOrPercentageOrAuto, PositionComponent};
|
||||||
use style::values::specified::transform::TimingFunction;
|
use style::values::specified::TimingFunction;
|
||||||
|
|
||||||
pub fn block_from<I>(iterable: I) -> PropertyDeclarationBlock
|
pub fn block_from<I>(iterable: I) -> PropertyDeclarationBlock
|
||||||
where I: IntoIterator<Item=(PropertyDeclaration, Importance)> {
|
where I: IntoIterator<Item=(PropertyDeclaration, Importance)> {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue