mirror of
https://github.com/servo/servo.git
synced 2025-08-05 21:50:18 +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.
|
||||
#[inline(always)]
|
||||
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.current_len += 1;
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ impl<Impl: SelectorImpl> SelectorBuilder<Impl> {
|
|||
parsed_slotted: bool,
|
||||
) -> ThinArc<SpecificityAndFlags, Component<Impl>> {
|
||||
// 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<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
|
||||
Impl: SelectorImpl,
|
||||
{
|
||||
complex_selector_specificity(builder, iter).into()
|
||||
complex_selector_specificity(iter).into()
|
||||
}
|
||||
|
||||
fn complex_selector_specificity<Impl>(
|
||||
builder: &SelectorBuilder<Impl>,
|
||||
mut iter: slice::Iter<Component<Impl>>,
|
||||
) -> Specificity
|
||||
fn complex_selector_specificity<Impl>(iter: slice::Iter<Component<Impl>>) -> Specificity
|
||||
where
|
||||
Impl: SelectorImpl,
|
||||
{
|
||||
fn simple_selector_specificity<Impl>(
|
||||
builder: &SelectorBuilder<Impl>,
|
||||
simple_selector: &Component<Impl>,
|
||||
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
|
||||
}
|
||||
|
|
|
@ -27,10 +27,10 @@ use std::sync::mpsc::Sender;
|
|||
use stylesheets::keyframes_rule::{KeyframesAnimation, KeyframesStep, KeyframesStepValue};
|
||||
use timer::Timer;
|
||||
use values::computed::Time;
|
||||
use values::computed::TimingFunction;
|
||||
use values::computed::box_::TransitionProperty;
|
||||
use values::computed::transform::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.
|
||||
|
@ -363,27 +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::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;
|
||||
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;
|
||||
}
|
||||
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) => {
|
||||
let (x1, x2, y1, y2) = keyword.to_bezier();
|
||||
|
|
|
@ -42,12 +42,14 @@ include = [
|
|||
"StyleComputedFontStretchRange",
|
||||
"StyleComputedFontStyleDescriptor",
|
||||
"StyleComputedFontWeightRange",
|
||||
"StyleComputedTimingFunction",
|
||||
"StyleDisplay",
|
||||
"StyleDisplayMode",
|
||||
"StyleFillRule",
|
||||
"StyleFontDisplay",
|
||||
"StyleFontFaceSourceListComponent",
|
||||
"StyleFontLanguageOverride",
|
||||
"StyleTimingFunction",
|
||||
"StylePathCommand",
|
||||
"StyleUnicodeRange",
|
||||
]
|
||||
|
|
|
@ -1032,13 +1032,13 @@ impl TrackSize<LengthOrPercentage> {
|
|||
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/c05d9d61188d32b8/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/c05d9d61188d32b8/layout/style/nsRuleNode.cpp#7919
|
||||
breadth.to_gecko_style_coord(gecko_min);
|
||||
breadth.to_gecko_style_coord(gecko_max);
|
||||
},
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
///
|
||||
/// 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
|
||||
/// +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')}
|
||||
</%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)]
|
||||
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
|
||||
}
|
||||
</%def>
|
||||
|
||||
|
@ -2996,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)}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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<ComputedTransformOperation>>,
|
||||
other: &mut Cow<'a, Vec<ComputedTransformOperation>>,
|
||||
) -> 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::<Result<Vec<_>, _>>()?;
|
||||
|
||||
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::<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,
|
||||
}]))
|
||||
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::<Result<Vec<_>, _>>()?,
|
||||
);
|
||||
}
|
||||
(None, None) => {}
|
||||
}
|
||||
|
||||
Ok(Transform(result))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
)}
|
||||
|
@ -258,6 +259,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,
|
||||
)}
|
||||
|
@ -270,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,
|
||||
)}
|
||||
|
|
|
@ -694,6 +694,34 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
|
|||
.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
|
||||
/// into the cascade.
|
||||
///
|
||||
|
@ -755,6 +783,10 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
|
|||
{
|
||||
self.adjust_for_text_decorations_in_effect();
|
||||
}
|
||||
#[cfg(feature = "gecko")]
|
||||
{
|
||||
self.adjust_for_appearance(element);
|
||||
}
|
||||
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::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;
|
||||
|
|
|
@ -22,9 +22,6 @@ pub type Transform = generic::Transform<TransformOperation>;
|
|||
/// The computed value of a CSS `<transform-origin>`
|
||||
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.
|
||||
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 column;
|
||||
pub mod counters;
|
||||
pub mod easing;
|
||||
pub mod effects;
|
||||
pub mod flex;
|
||||
pub mod font;
|
||||
|
|
|
@ -90,67 +90,6 @@ pub struct TransformOrigin<H, V, 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> {
|
||||
/// Returns a new transform origin.
|
||||
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)]
|
||||
/// A single operation in the list of a `transform` value
|
||||
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
|
||||
// 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
|
||||
// The first index is (base_size - 9), the second is the
|
||||
// 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::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;
|
||||
|
|
|
@ -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<S> {
|
|||
Side(S),
|
||||
}
|
||||
|
||||
/// A specified timing function.
|
||||
pub type TimingFunction = generic::TimingFunction<Integer, Number>;
|
||||
|
||||
impl Parse for TransformOrigin {
|
||||
fn parse<'i, 't>(
|
||||
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`
|
||||
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, 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 {
|
||||
pub use super::*;
|
||||
#[test]
|
||||
|
|
|
@ -28,7 +28,7 @@ use style::stylesheets::keyframes_rule::{Keyframe, KeyframeSelector, KeyframePer
|
|||
use style::values::{KeyframesName, CustomIdent};
|
||||
use style::values::computed::Percentage;
|
||||
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
|
||||
where I: IntoIterator<Item=(PropertyDeclaration, Importance)> {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue