Revert "Backport several style changes from Gecko (5) (#30099)" (#30104)

This reverts commit 8e15389cae.
This commit is contained in:
Oriol Brufau 2023-08-16 08:24:42 +02:00 committed by GitHub
parent 8e15389cae
commit d6ae8dc112
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
152 changed files with 4622 additions and 5862 deletions

View file

@ -28,8 +28,34 @@ use smallvec::SmallVec;
use std::borrow::Cow;
use std::cell::RefCell;
/// We split the cascade in two phases: 'early' properties, and 'late'
/// properties.
///
/// Early properties are the ones that don't have dependencies _and_ other
/// properties depend on, for example, writing-mode related properties, color
/// (for currentColor), or font-size (for em, etc).
///
/// Late properties are all the others.
trait CascadePhase {
fn is_early() -> bool;
}
struct EarlyProperties;
impl CascadePhase for EarlyProperties {
fn is_early() -> bool {
true
}
}
struct LateProperties;
impl CascadePhase for LateProperties {
fn is_early() -> bool {
false
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum CanHaveLogicalProperties {
enum ApplyResetProperties {
No,
Yes,
}
@ -257,7 +283,6 @@ where
let inherited_style = parent_style.unwrap_or(device.default_computed_values());
let mut declarations = SmallVec::<[(&_, CascadePriority); 32]>::new();
let mut referenced_properties = LonghandIdSet::default();
let custom_properties = {
let mut builder = CustomPropertiesBuilder::new(inherited_style.custom_properties(), device);
@ -265,8 +290,6 @@ where
declarations.push((declaration, priority));
if let PropertyDeclaration::Custom(ref declaration) = *declaration {
builder.cascade(declaration, priority);
} else {
referenced_properties.insert(declaration.id().as_longhand().unwrap());
}
}
@ -292,74 +315,46 @@ where
in_media_query: false,
for_smil_animation: false,
for_non_inherited_property: None,
container_info: None,
quirks_mode,
rule_cache_conditions: RefCell::new(rule_cache_conditions),
};
let using_cached_reset_properties;
let mut cascade = Cascade::new(&mut context, cascade_mode, &referenced_properties);
let mut shorthand_cache = ShorthandsWithPropertyReferencesCache::default();
let using_cached_reset_properties = {
let mut cascade = Cascade::new(&mut context, cascade_mode);
let mut shorthand_cache = ShorthandsWithPropertyReferencesCache::default();
let properties_to_apply = match cascade.cascade_mode {
CascadeMode::Visited { writing_mode } => {
cascade.context.builder.writing_mode = writing_mode;
// We never insert visited styles into the cache so we don't need to
// try looking it up. It also wouldn't be super-profitable, only a
// handful reset properties are non-inherited.
using_cached_reset_properties = false;
LonghandIdSet::visited_dependent()
},
CascadeMode::Unvisited { visited_rules } => {
if cascade.apply_properties(
CanHaveLogicalProperties::No,
LonghandIdSet::writing_mode_group(),
declarations.iter().cloned(),
&mut shorthand_cache,
) {
cascade.compute_writing_mode();
}
cascade.apply_properties::<EarlyProperties, _>(
ApplyResetProperties::Yes,
declarations.iter().cloned(),
&mut shorthand_cache,
);
if cascade.apply_properties(
CanHaveLogicalProperties::No,
LonghandIdSet::fonts_and_color_group(),
declarations.iter().cloned(),
&mut shorthand_cache,
) {
cascade.fixup_font_stuff();
}
cascade.compute_visited_style_if_needed(
element,
parent_style,
parent_style_ignoring_first_line,
layout_parent_style,
guards,
);
if let Some(visited_rules) = visited_rules {
cascade.compute_visited_style_if_needed(
element,
parent_style,
parent_style_ignoring_first_line,
layout_parent_style,
visited_rules,
guards,
);
}
let using_cached_reset_properties =
cascade.try_to_use_cached_reset_properties(rule_cache, guards);
using_cached_reset_properties =
cascade.try_to_use_cached_reset_properties(rule_cache, guards);
let apply_reset = if using_cached_reset_properties {
ApplyResetProperties::No
} else {
ApplyResetProperties::Yes
};
if using_cached_reset_properties {
LonghandIdSet::late_group_only_inherited()
} else {
LonghandIdSet::late_group()
}
}
cascade.apply_properties::<LateProperties, _>(
apply_reset,
declarations.iter().cloned(),
&mut shorthand_cache,
);
using_cached_reset_properties
};
cascade.apply_properties(
CanHaveLogicalProperties::Yes,
properties_to_apply,
declarations.iter().cloned(),
&mut shorthand_cache,
);
cascade.finished_applying_properties();
context.builder.clear_modified_reset();
if matches!(cascade_mode, CascadeMode::Unvisited { .. }) {
@ -418,7 +413,7 @@ fn tweak_when_ignoring_colors(
fn alpha_channel(color: &Color, context: &computed::Context) -> u8 {
// We assume here currentColor is opaque.
let color = color.to_computed_value(context).into_rgba(RGBA::new(0, 0, 0, 255));
let color = color.to_computed_value(context).to_rgba(RGBA::new(0, 0, 0, 255));
color.alpha
}
@ -433,17 +428,14 @@ fn tweak_when_ignoring_colors(
// otherwise, this is needed to preserve semi-transparent
// backgrounds.
//
// NOTE(emilio): We honor transparent unconditionally, like we do
// for color, even though it causes issues like bug 1625036. The
// reasoning is that the conditions that trigger that (having
// mismatched widget and default backgrounds) are both uncommon, and
// broken in other applications as well, and not honoring
// transparent makes stuff uglier or break unconditionally
// NOTE(emilio): We revert even for alpha == 0. Not doing so would
// be a bit special casey, even though it causes issues like
// bug 1625036. The reasoning is that the conditions that trigger
// that (having mismatched widget and default backgrounds) are both
// uncommon, and broken in other applications as well, and not
// honoring transparent makes stuff uglier or break unconditionally
// (bug 1666059, bug 1755713).
let alpha = alpha_channel(color, context);
if alpha == 0 {
return;
}
let mut color = context.builder.device.default_background_color();
color.alpha = alpha;
declarations_to_apply_unless_overriden
@ -505,8 +497,6 @@ fn tweak_when_ignoring_colors(
struct Cascade<'a, 'b: 'a> {
context: &'a mut computed::Context<'b>,
cascade_mode: CascadeMode<'a>,
/// All the properties that have a declaration in the cascade.
referenced: &'a LonghandIdSet,
seen: LonghandIdSet,
author_specified: LonghandIdSet,
reverted_set: LonghandIdSet,
@ -514,15 +504,10 @@ struct Cascade<'a, 'b: 'a> {
}
impl<'a, 'b: 'a> Cascade<'a, 'b> {
fn new(
context: &'a mut computed::Context<'b>,
cascade_mode: CascadeMode<'a>,
referenced: &'a LonghandIdSet,
) -> Self {
fn new(context: &'a mut computed::Context<'b>, cascade_mode: CascadeMode<'a>) -> Self {
Self {
context,
cascade_mode,
referenced,
seen: LonghandIdSet::default(),
author_specified: LonghandIdSet::default(),
reverted_set: Default::default(),
@ -590,21 +575,23 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
(CASCADE_PROPERTY[discriminant])(declaration, &mut self.context);
}
fn apply_properties<'decls, I>(
fn apply_properties<'decls, Phase, I>(
&mut self,
can_have_logical_properties: CanHaveLogicalProperties,
properties_to_apply: &'a LonghandIdSet,
apply_reset: ApplyResetProperties,
declarations: I,
mut shorthand_cache: &mut ShorthandsWithPropertyReferencesCache,
) -> bool
where
) where
Phase: CascadePhase,
I: Iterator<Item = (&'decls PropertyDeclaration, CascadePriority)>,
{
if !self.referenced.contains_any(properties_to_apply) {
return false;
}
let apply_reset = apply_reset == ApplyResetProperties::Yes;
let can_have_logical_properties = can_have_logical_properties == CanHaveLogicalProperties::Yes;
debug_assert!(
!Phase::is_early() || apply_reset,
"Should always apply reset properties in the early phase, since we \
need to know font-size / writing-mode to decide whether to use the \
cached reset properties"
);
let ignore_colors = !self.context.builder.device.use_document_colors();
let mut declarations_to_apply_unless_overriden = DeclarationsToApplyUnlessOverriden::new();
@ -618,15 +605,20 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
PropertyDeclarationId::Custom(..) => continue,
};
if !properties_to_apply.contains(longhand_id) {
let inherited = longhand_id.inherited();
if !apply_reset && !inherited {
continue;
}
debug_assert!(can_have_logical_properties || !longhand_id.is_logical());
let physical_longhand_id = if can_have_logical_properties {
longhand_id.to_physical(self.context.builder.writing_mode)
} else {
if Phase::is_early() != longhand_id.is_early_property() {
continue;
}
debug_assert!(!Phase::is_early() || !longhand_id.is_logical());
let physical_longhand_id = if Phase::is_early() {
longhand_id
} else {
longhand_id.to_physical(self.context.builder.writing_mode)
};
if self.seen.contains(physical_longhand_id) {
@ -641,6 +633,15 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
}
}
// Only a few properties are allowed to depend on the visited state
// of links. When cascading visited styles, we can save time by
// only processing these properties.
if matches!(self.cascade_mode, CascadeMode::Visited { .. }) &&
!physical_longhand_id.is_visited_dependent()
{
continue;
}
let mut declaration =
self.substitute_variables_if_needed(declaration, &mut shorthand_cache);
@ -674,8 +675,8 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
continue;
},
CSSWideKeyword::Unset => true,
CSSWideKeyword::Inherit => longhand_id.inherited(),
CSSWideKeyword::Initial => !longhand_id.inherited(),
CSSWideKeyword::Inherit => inherited,
CSSWideKeyword::Initial => !inherited,
},
None => false,
};
@ -709,13 +710,22 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
}
}
true
if Phase::is_early() {
self.fixup_font_stuff();
self.compute_writing_mode();
} else {
self.finished_applying_properties();
}
}
fn compute_writing_mode(&mut self) {
debug_assert!(matches!(self.cascade_mode, CascadeMode::Unvisited { .. }));
self.context.builder.writing_mode =
WritingMode::new(self.context.builder.get_inherited_box())
let writing_mode = match self.cascade_mode {
CascadeMode::Unvisited { .. } => {
WritingMode::new(self.context.builder.get_inherited_box())
},
CascadeMode::Visited { writing_mode } => writing_mode,
};
self.context.builder.writing_mode = writing_mode;
}
fn compute_visited_style_if_needed<E>(
@ -724,12 +734,20 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
parent_style: Option<&ComputedValues>,
parent_style_ignoring_first_line: Option<&ComputedValues>,
layout_parent_style: Option<&ComputedValues>,
visited_rules: &StrongRuleNode,
guards: &StylesheetGuards,
) where
E: TElement,
{
debug_assert!(matches!(self.cascade_mode, CascadeMode::Unvisited { .. }));
let visited_rules = match self.cascade_mode {
CascadeMode::Unvisited { visited_rules } => visited_rules,
CascadeMode::Visited { .. } => return,
};
let visited_rules = match visited_rules {
Some(rules) => rules,
None => return,
};
let is_link = self.context.builder.pseudo.is_none() && element.unwrap().is_link();
macro_rules! visited_parent {

View file

@ -73,6 +73,7 @@ COUNTED_UNKNOWN_PROPERTIES = [
"-webkit-perspective-origin-y",
"-webkit-margin-before-collapse",
"-webkit-border-before-style",
"scroll-snap-stop",
"-webkit-margin-bottom-collapse",
"-webkit-ruby-position",
"-webkit-column-break-after",

View file

@ -444,7 +444,6 @@ class Longhand(Property):
"ColumnCount",
"Contain",
"ContentVisibility",
"ContainerType",
"Display",
"FillRule",
"Float",
@ -492,7 +491,6 @@ class Longhand(Property):
"ScrollbarGutter",
"ScrollSnapAlign",
"ScrollSnapAxis",
"ScrollSnapStop",
"ScrollSnapStrictness",
"ScrollSnapType",
"TextAlign",

View file

@ -755,6 +755,7 @@ fn static_assert() {
<%self:impl_trait style_struct_name="Margin"
skip_longhands="${skip_margin_longhands}
${skip_scroll_margin_longhands}">
% for side in SIDES:
<% impl_split_style_coord("margin_%s" % side.ident,
"mMargin",
@ -1180,7 +1181,11 @@ fn static_assert() {
</%def>
<% skip_box_longhands= """display
clear
animation-name animation-delay animation-duration
animation-direction animation-fill-mode animation-play-state
animation-iteration-count animation-timeline animation-timing-function
clear transition-duration transition-delay
transition-timing-function transition-property
-webkit-line-clamp""" %>
<%self:impl_trait style_struct_name="Box" skip_longhands="${skip_box_longhands}">
#[inline]
@ -1222,6 +1227,245 @@ fn static_assert() {
) %>
${impl_keyword('clear', 'mBreakType', clear_keyword)}
${impl_transition_time_value('delay', 'Delay')}
${impl_transition_time_value('duration', 'Duration')}
${impl_animation_or_transition_timing_function('transition')}
pub fn transition_combined_duration_at(&self, index: usize) -> f32 {
// https://drafts.csswg.org/css-transitions/#transition-combined-duration
self.gecko.mTransitions[index % self.gecko.mTransitionDurationCount as usize].mDuration.max(0.0)
+ self.gecko.mTransitions[index % self.gecko.mTransitionDelayCount as usize].mDelay
}
pub fn set_transition_property<I>(&mut self, v: I)
where I: IntoIterator<Item = longhands::transition_property::computed_value::single_value::T>,
I::IntoIter: ExactSizeIterator
{
use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_no_properties;
use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_variable;
use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSProperty_UNKNOWN;
let v = v.into_iter();
if v.len() != 0 {
self.gecko.mTransitions.ensure_len(v.len());
self.gecko.mTransitionPropertyCount = v.len() as u32;
for (servo, gecko) in v.zip(self.gecko.mTransitions.iter_mut()) {
unsafe { gecko.mUnknownProperty.clear() };
match servo {
TransitionProperty::Unsupported(ident) => {
gecko.mProperty = eCSSProperty_UNKNOWN;
gecko.mUnknownProperty.mRawPtr = ident.0.into_addrefed();
},
TransitionProperty::Custom(name) => {
gecko.mProperty = eCSSPropertyExtra_variable;
gecko.mUnknownProperty.mRawPtr = name.into_addrefed();
}
_ => gecko.mProperty = servo.to_nscsspropertyid().unwrap(),
}
}
} else {
// In gecko |none| is represented by eCSSPropertyExtra_no_properties.
self.gecko.mTransitionPropertyCount = 1;
self.gecko.mTransitions[0].mProperty = eCSSPropertyExtra_no_properties;
}
}
/// Returns whether there are any transitions specified.
pub fn specifies_transitions(&self) -> bool {
use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_all_properties;
if self.gecko.mTransitionPropertyCount == 1 &&
self.gecko.mTransitions[0].mProperty == eCSSPropertyExtra_all_properties &&
self.transition_combined_duration_at(0) <= 0.0f32 {
return false;
}
self.gecko.mTransitionPropertyCount > 0
}
pub fn transition_property_at(&self, index: usize)
-> longhands::transition_property::computed_value::SingleComputedValue {
use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_no_properties;
use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_variable;
use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSProperty_UNKNOWN;
let property = self.gecko.mTransitions[index].mProperty;
if property == eCSSProperty_UNKNOWN {
let atom = self.gecko.mTransitions[index].mUnknownProperty.mRawPtr;
debug_assert!(!atom.is_null());
TransitionProperty::Unsupported(CustomIdent(unsafe{
Atom::from_raw(atom)
}))
} else if property == eCSSPropertyExtra_variable {
let atom = self.gecko.mTransitions[index].mUnknownProperty.mRawPtr;
debug_assert!(!atom.is_null());
TransitionProperty::Custom(unsafe{
Atom::from_raw(atom)
})
} else if property == eCSSPropertyExtra_no_properties {
// Actually, we don't expect TransitionProperty::Unsupported also
// represents "none", but if the caller wants to convert it, it is
// fine. Please use it carefully.
//
// FIXME(emilio): This is a hack, is this reachable?
TransitionProperty::Unsupported(CustomIdent(atom!("none")))
} else {
property.into()
}
}
pub fn transition_nscsspropertyid_at(&self, index: usize) -> nsCSSPropertyID {
self.gecko.mTransitions[index].mProperty
}
pub fn copy_transition_property_from(&mut self, other: &Self) {
use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_variable;
use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSProperty_UNKNOWN;
self.gecko.mTransitions.ensure_len(other.gecko.mTransitions.len());
let count = other.gecko.mTransitionPropertyCount;
self.gecko.mTransitionPropertyCount = count;
for (index, transition) in self.gecko.mTransitions.iter_mut().enumerate().take(count as usize) {
transition.mProperty = other.gecko.mTransitions[index].mProperty;
unsafe { transition.mUnknownProperty.clear() };
if transition.mProperty == eCSSProperty_UNKNOWN ||
transition.mProperty == eCSSPropertyExtra_variable {
let atom = other.gecko.mTransitions[index].mUnknownProperty.mRawPtr;
debug_assert!(!atom.is_null());
transition.mUnknownProperty.mRawPtr = unsafe { Atom::from_raw(atom) }.into_addrefed();
}
}
}
pub fn reset_transition_property(&mut self, other: &Self) {
self.copy_transition_property_from(other)
}
${impl_transition_count('property', 'Property')}
pub fn animations_equals(&self, other: &Self) -> bool {
return self.gecko.mAnimationNameCount == other.gecko.mAnimationNameCount
&& self.gecko.mAnimationDelayCount == other.gecko.mAnimationDelayCount
&& self.gecko.mAnimationDirectionCount == other.gecko.mAnimationDirectionCount
&& self.gecko.mAnimationDurationCount == other.gecko.mAnimationDurationCount
&& self.gecko.mAnimationFillModeCount == other.gecko.mAnimationFillModeCount
&& self.gecko.mAnimationIterationCountCount == other.gecko.mAnimationIterationCountCount
&& self.gecko.mAnimationPlayStateCount == other.gecko.mAnimationPlayStateCount
&& self.gecko.mAnimationTimingFunctionCount == other.gecko.mAnimationTimingFunctionCount
&& unsafe { bindings::Gecko_StyleAnimationsEquals(&self.gecko.mAnimations, &other.gecko.mAnimations) }
}
pub fn set_animation_name<I>(&mut self, v: I)
where I: IntoIterator<Item = longhands::animation_name::computed_value::single_value::T>,
I::IntoIter: ExactSizeIterator
{
let v = v.into_iter();
debug_assert_ne!(v.len(), 0);
self.gecko.mAnimations.ensure_len(v.len());
self.gecko.mAnimationNameCount = v.len() as u32;
for (servo, gecko) in v.zip(self.gecko.mAnimations.iter_mut()) {
let atom = match servo.0 {
None => atom!(""),
Some(ref name) => name.as_atom().clone(),
};
unsafe { bindings::Gecko_SetAnimationName(gecko, atom.into_addrefed()); }
}
}
pub fn animation_name_at(&self, index: usize)
-> longhands::animation_name::computed_value::SingleComputedValue {
use crate::properties::longhands::animation_name::single_value::SpecifiedValue as AnimationName;
let atom = self.gecko.mAnimations[index].mName.mRawPtr;
if atom == atom!("").as_ptr() {
return AnimationName(None)
}
AnimationName(Some(KeyframesName::from_atom(unsafe { Atom::from_raw(atom) })))
}
pub fn copy_animation_name_from(&mut self, other: &Self) {
self.gecko.mAnimationNameCount = other.gecko.mAnimationNameCount;
unsafe { bindings::Gecko_CopyAnimationNames(&mut self.gecko.mAnimations, &other.gecko.mAnimations); }
}
pub fn reset_animation_name(&mut self, other: &Self) {
self.copy_animation_name_from(other)
}
${impl_animation_count('name', 'Name')}
${impl_animation_time_value('delay', 'Delay')}
${impl_animation_time_value('duration', 'Duration')}
${impl_animation_keyword('direction', 'Direction',
data.longhands_by_name["animation-direction"].keyword)}
${impl_animation_keyword('fill_mode', 'FillMode',
data.longhands_by_name["animation-fill-mode"].keyword)}
${impl_animation_keyword('play_state', 'PlayState',
data.longhands_by_name["animation-play-state"].keyword)}
pub fn set_animation_iteration_count<I>(&mut self, v: I)
where
I: IntoIterator<Item = values::computed::AnimationIterationCount>,
I::IntoIter: ExactSizeIterator + Clone
{
use std::f32;
use crate::values::generics::box_::AnimationIterationCount;
let v = v.into_iter();
debug_assert_ne!(v.len(), 0);
let input_len = v.len();
self.gecko.mAnimations.ensure_len(input_len);
self.gecko.mAnimationIterationCountCount = input_len as u32;
for (gecko, servo) in self.gecko.mAnimations.iter_mut().take(input_len as usize).zip(v) {
match servo {
AnimationIterationCount::Number(n) => gecko.mIterationCount = n,
AnimationIterationCount::Infinite => gecko.mIterationCount = f32::INFINITY,
}
}
}
pub fn animation_iteration_count_at(
&self,
index: usize,
) -> values::computed::AnimationIterationCount {
use crate::values::generics::box_::AnimationIterationCount;
if self.gecko.mAnimations[index].mIterationCount.is_infinite() {
AnimationIterationCount::Infinite
} else {
AnimationIterationCount::Number(self.gecko.mAnimations[index].mIterationCount)
}
}
${impl_animation_count('iteration_count', 'IterationCount')}
${impl_copy_animation_value('iteration_count', 'IterationCount')}
${impl_animation_or_transition_timing_function('animation')}
pub fn set_animation_timeline<I>(&mut self, v: I)
where
I: IntoIterator<Item = longhands::animation_timeline::computed_value::single_value::T>,
I::IntoIter: ExactSizeIterator
{
let v = v.into_iter();
debug_assert_ne!(v.len(), 0);
let input_len = v.len();
self.gecko.mAnimations.ensure_len(input_len);
self.gecko.mAnimationTimelineCount = input_len as u32;
for (gecko, servo) in self.gecko.mAnimations.iter_mut().take(input_len as usize).zip(v) {
gecko.mTimeline = servo;
}
}
pub fn animation_timeline_at(&self, index: usize) -> values::specified::box_::AnimationTimeline {
self.gecko.mAnimations[index].mTimeline.clone()
}
${impl_animation_count('timeline', 'Timeline')}
${impl_copy_animation_value('timeline', 'Timeline')}
#[allow(non_snake_case)]
pub fn set__webkit_line_clamp(&mut self, v: longhands::_webkit_line_clamp::computed_value::T) {
self.gecko.mLineClamp = match v {
@ -1776,247 +2020,7 @@ mask-mode mask-repeat mask-clip mask-origin mask-composite mask-position-x mask-
}
</%self:impl_trait>
<% skip_ui_longhands = """animation-name animation-delay animation-duration
animation-direction animation-fill-mode
animation-play-state animation-iteration-count
animation-timeline animation-timing-function
transition-duration transition-delay
transition-timing-function transition-property""" %>
<%self:impl_trait style_struct_name="UI" skip_longhands="${skip_ui_longhands}">
${impl_transition_time_value('delay', 'Delay')}
${impl_transition_time_value('duration', 'Duration')}
${impl_animation_or_transition_timing_function('transition')}
pub fn transition_combined_duration_at(&self, index: usize) -> f32 {
// https://drafts.csswg.org/css-transitions/#transition-combined-duration
self.gecko.mTransitions[index % self.gecko.mTransitionDurationCount as usize].mDuration.max(0.0)
+ self.gecko.mTransitions[index % self.gecko.mTransitionDelayCount as usize].mDelay
}
pub fn set_transition_property<I>(&mut self, v: I)
where I: IntoIterator<Item = longhands::transition_property::computed_value::single_value::T>,
I::IntoIter: ExactSizeIterator
{
use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_no_properties;
use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_variable;
use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSProperty_UNKNOWN;
let v = v.into_iter();
if v.len() != 0 {
self.gecko.mTransitions.ensure_len(v.len());
self.gecko.mTransitionPropertyCount = v.len() as u32;
for (servo, gecko) in v.zip(self.gecko.mTransitions.iter_mut()) {
unsafe { gecko.mUnknownProperty.clear() };
match servo {
TransitionProperty::Unsupported(ident) => {
gecko.mProperty = eCSSProperty_UNKNOWN;
gecko.mUnknownProperty.mRawPtr = ident.0.into_addrefed();
},
TransitionProperty::Custom(name) => {
gecko.mProperty = eCSSPropertyExtra_variable;
gecko.mUnknownProperty.mRawPtr = name.into_addrefed();
}
_ => gecko.mProperty = servo.to_nscsspropertyid().unwrap(),
}
}
} else {
// In gecko |none| is represented by eCSSPropertyExtra_no_properties.
self.gecko.mTransitionPropertyCount = 1;
self.gecko.mTransitions[0].mProperty = eCSSPropertyExtra_no_properties;
}
}
/// Returns whether there are any transitions specified.
pub fn specifies_transitions(&self) -> bool {
use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_all_properties;
if self.gecko.mTransitionPropertyCount == 1 &&
self.gecko.mTransitions[0].mProperty == eCSSPropertyExtra_all_properties &&
self.transition_combined_duration_at(0) <= 0.0f32 {
return false;
}
self.gecko.mTransitionPropertyCount > 0
}
pub fn transition_property_at(&self, index: usize)
-> longhands::transition_property::computed_value::SingleComputedValue {
use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_no_properties;
use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_variable;
use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSProperty_UNKNOWN;
let property = self.gecko.mTransitions[index].mProperty;
if property == eCSSProperty_UNKNOWN {
let atom = self.gecko.mTransitions[index].mUnknownProperty.mRawPtr;
debug_assert!(!atom.is_null());
TransitionProperty::Unsupported(CustomIdent(unsafe{
Atom::from_raw(atom)
}))
} else if property == eCSSPropertyExtra_variable {
let atom = self.gecko.mTransitions[index].mUnknownProperty.mRawPtr;
debug_assert!(!atom.is_null());
TransitionProperty::Custom(unsafe{
Atom::from_raw(atom)
})
} else if property == eCSSPropertyExtra_no_properties {
// Actually, we don't expect TransitionProperty::Unsupported also
// represents "none", but if the caller wants to convert it, it is
// fine. Please use it carefully.
//
// FIXME(emilio): This is a hack, is this reachable?
TransitionProperty::Unsupported(CustomIdent(atom!("none")))
} else {
property.into()
}
}
pub fn transition_nscsspropertyid_at(&self, index: usize) -> nsCSSPropertyID {
self.gecko.mTransitions[index].mProperty
}
pub fn copy_transition_property_from(&mut self, other: &Self) {
use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_variable;
use crate::gecko_bindings::structs::nsCSSPropertyID::eCSSProperty_UNKNOWN;
self.gecko.mTransitions.ensure_len(other.gecko.mTransitions.len());
let count = other.gecko.mTransitionPropertyCount;
self.gecko.mTransitionPropertyCount = count;
for (index, transition) in self.gecko.mTransitions.iter_mut().enumerate().take(count as usize) {
transition.mProperty = other.gecko.mTransitions[index].mProperty;
unsafe { transition.mUnknownProperty.clear() };
if transition.mProperty == eCSSProperty_UNKNOWN ||
transition.mProperty == eCSSPropertyExtra_variable {
let atom = other.gecko.mTransitions[index].mUnknownProperty.mRawPtr;
debug_assert!(!atom.is_null());
transition.mUnknownProperty.mRawPtr = unsafe { Atom::from_raw(atom) }.into_addrefed();
}
}
}
pub fn reset_transition_property(&mut self, other: &Self) {
self.copy_transition_property_from(other)
}
${impl_transition_count('property', 'Property')}
pub fn animations_equals(&self, other: &Self) -> bool {
return self.gecko.mAnimationNameCount == other.gecko.mAnimationNameCount
&& self.gecko.mAnimationDelayCount == other.gecko.mAnimationDelayCount
&& self.gecko.mAnimationDirectionCount == other.gecko.mAnimationDirectionCount
&& self.gecko.mAnimationDurationCount == other.gecko.mAnimationDurationCount
&& self.gecko.mAnimationFillModeCount == other.gecko.mAnimationFillModeCount
&& self.gecko.mAnimationIterationCountCount == other.gecko.mAnimationIterationCountCount
&& self.gecko.mAnimationPlayStateCount == other.gecko.mAnimationPlayStateCount
&& self.gecko.mAnimationTimingFunctionCount == other.gecko.mAnimationTimingFunctionCount
&& unsafe { bindings::Gecko_StyleAnimationsEquals(&self.gecko.mAnimations, &other.gecko.mAnimations) }
}
pub fn set_animation_name<I>(&mut self, v: I)
where I: IntoIterator<Item = longhands::animation_name::computed_value::single_value::T>,
I::IntoIter: ExactSizeIterator
{
let v = v.into_iter();
debug_assert_ne!(v.len(), 0);
self.gecko.mAnimations.ensure_len(v.len());
self.gecko.mAnimationNameCount = v.len() as u32;
for (servo, gecko) in v.zip(self.gecko.mAnimations.iter_mut()) {
let atom = servo.0.as_atom().clone();
unsafe { bindings::Gecko_SetAnimationName(gecko, atom.into_addrefed()); }
}
}
pub fn animation_name_at(&self, index: usize)
-> longhands::animation_name::computed_value::SingleComputedValue {
use crate::properties::longhands::animation_name::single_value::SpecifiedValue as AnimationName;
let atom = self.gecko.mAnimations[index].mName.mRawPtr;
AnimationName(KeyframesName::from_atom(unsafe { Atom::from_raw(atom) }))
}
pub fn copy_animation_name_from(&mut self, other: &Self) {
self.gecko.mAnimationNameCount = other.gecko.mAnimationNameCount;
unsafe { bindings::Gecko_CopyAnimationNames(&mut self.gecko.mAnimations, &other.gecko.mAnimations); }
}
pub fn reset_animation_name(&mut self, other: &Self) {
self.copy_animation_name_from(other)
}
${impl_animation_count('name', 'Name')}
${impl_animation_time_value('delay', 'Delay')}
${impl_animation_time_value('duration', 'Duration')}
${impl_animation_keyword('direction', 'Direction',
data.longhands_by_name["animation-direction"].keyword)}
${impl_animation_keyword('fill_mode', 'FillMode',
data.longhands_by_name["animation-fill-mode"].keyword)}
${impl_animation_keyword('play_state', 'PlayState',
data.longhands_by_name["animation-play-state"].keyword)}
pub fn set_animation_iteration_count<I>(&mut self, v: I)
where
I: IntoIterator<Item = values::computed::AnimationIterationCount>,
I::IntoIter: ExactSizeIterator + Clone
{
use std::f32;
use crate::values::generics::box_::AnimationIterationCount;
let v = v.into_iter();
debug_assert_ne!(v.len(), 0);
let input_len = v.len();
self.gecko.mAnimations.ensure_len(input_len);
self.gecko.mAnimationIterationCountCount = input_len as u32;
for (gecko, servo) in self.gecko.mAnimations.iter_mut().take(input_len as usize).zip(v) {
match servo {
AnimationIterationCount::Number(n) => gecko.mIterationCount = n,
AnimationIterationCount::Infinite => gecko.mIterationCount = f32::INFINITY,
}
}
}
pub fn animation_iteration_count_at(
&self,
index: usize,
) -> values::computed::AnimationIterationCount {
use crate::values::generics::box_::AnimationIterationCount;
if self.gecko.mAnimations[index].mIterationCount.is_infinite() {
AnimationIterationCount::Infinite
} else {
AnimationIterationCount::Number(self.gecko.mAnimations[index].mIterationCount)
}
}
${impl_animation_count('iteration_count', 'IterationCount')}
${impl_copy_animation_value('iteration_count', 'IterationCount')}
${impl_animation_or_transition_timing_function('animation')}
pub fn set_animation_timeline<I>(&mut self, v: I)
where
I: IntoIterator<Item = longhands::animation_timeline::computed_value::single_value::T>,
I::IntoIter: ExactSizeIterator
{
let v = v.into_iter();
debug_assert_ne!(v.len(), 0);
let input_len = v.len();
self.gecko.mAnimations.ensure_len(input_len);
self.gecko.mAnimationTimelineCount = input_len as u32;
for (gecko, servo) in self.gecko.mAnimations.iter_mut().take(input_len as usize).zip(v) {
gecko.mTimeline = servo;
}
}
pub fn animation_timeline_at(&self, index: usize) -> values::specified::box_::AnimationTimeline {
self.gecko.mAnimations[index].mTimeline.clone()
}
${impl_animation_count('timeline', 'Timeline')}
${impl_copy_animation_value('timeline', 'Timeline')}
<%self:impl_trait style_struct_name="UI">
</%self:impl_trait>
<%self:impl_trait style_struct_name="XUL">

View file

@ -799,7 +799,7 @@ impl<'a> TransitionPropertyIterator<'a> {
pub fn from_style(style: &'a ComputedValues) -> Self {
Self {
style,
index_range: 0..style.get_ui().transition_property_count(),
index_range: 0..style.get_box().transition_property_count(),
longhand_iterator: None,
}
}
@ -832,7 +832,7 @@ impl<'a> Iterator for TransitionPropertyIterator<'a> {
}
let index = self.index_range.next()?;
match self.style.get_ui().transition_property_at(index) {
match self.style.get_box().transition_property_at(index) {
TransitionProperty::Longhand(longhand_id) => {
return Some(TransitionPropertyIteration {
longhand_id,

View file

@ -3,7 +3,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
<%namespace name="helpers" file="/helpers.mako.rs" />
<% from data import ALL_AXES, Keyword, Method, to_rust_ident, to_camel_case%>
<% from data import ALL_AXES, DEFAULT_RULES_EXCEPT_KEYFRAME, Keyword, Method, to_rust_ident, to_camel_case%>
<% data.new_style_struct("Box",
inherited=False,
@ -150,6 +150,193 @@ ${helpers.predefined_type(
animation_value_type="discrete",
)}
<% transition_extra_prefixes = "moz:layout.css.prefixes.transitions webkit" %>
${helpers.predefined_type(
"transition-duration",
"Time",
"computed::Time::zero()",
engines="gecko servo",
initial_specified_value="specified::Time::zero()",
parse_method="parse_non_negative",
vector=True,
need_index=True,
animation_value_type="none",
extra_prefixes=transition_extra_prefixes,
spec="https://drafts.csswg.org/css-transitions/#propdef-transition-duration",
)}
${helpers.predefined_type(
"transition-timing-function",
"TimingFunction",
"computed::TimingFunction::ease()",
engines="gecko servo",
initial_specified_value="specified::TimingFunction::ease()",
vector=True,
need_index=True,
animation_value_type="none",
extra_prefixes=transition_extra_prefixes,
spec="https://drafts.csswg.org/css-transitions/#propdef-transition-timing-function",
)}
${helpers.predefined_type(
"transition-property",
"TransitionProperty",
"computed::TransitionProperty::all()",
engines="gecko servo",
initial_specified_value="specified::TransitionProperty::all()",
vector=True,
allow_empty="NotInitial",
need_index=True,
animation_value_type="none",
extra_prefixes=transition_extra_prefixes,
spec="https://drafts.csswg.org/css-transitions/#propdef-transition-property",
)}
${helpers.predefined_type(
"transition-delay",
"Time",
"computed::Time::zero()",
engines="gecko servo",
initial_specified_value="specified::Time::zero()",
vector=True,
need_index=True,
animation_value_type="none",
extra_prefixes=transition_extra_prefixes,
spec="https://drafts.csswg.org/css-transitions/#propdef-transition-delay",
)}
<% animation_extra_prefixes = "moz:layout.css.prefixes.animations webkit" %>
${helpers.predefined_type(
"animation-name",
"AnimationName",
"computed::AnimationName::none()",
engines="gecko servo",
initial_specified_value="specified::AnimationName::none()",
vector=True,
need_index=True,
animation_value_type="none",
extra_prefixes=animation_extra_prefixes,
rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME,
spec="https://drafts.csswg.org/css-animations/#propdef-animation-name",
)}
${helpers.predefined_type(
"animation-duration",
"Time",
"computed::Time::zero()",
engines="gecko servo",
initial_specified_value="specified::Time::zero()",
parse_method="parse_non_negative",
vector=True,
need_index=True,
animation_value_type="none",
extra_prefixes=animation_extra_prefixes,
spec="https://drafts.csswg.org/css-transitions/#propdef-transition-duration",
)}
// animation-timing-function is the exception to the rule for allowed_in_keyframe_block:
// https://drafts.csswg.org/css-animations/#keyframes
${helpers.predefined_type(
"animation-timing-function",
"TimingFunction",
"computed::TimingFunction::ease()",
engines="gecko servo",
initial_specified_value="specified::TimingFunction::ease()",
vector=True,
need_index=True,
animation_value_type="none",
extra_prefixes=animation_extra_prefixes,
spec="https://drafts.csswg.org/css-transitions/#propdef-animation-timing-function",
)}
${helpers.predefined_type(
"animation-iteration-count",
"AnimationIterationCount",
"computed::AnimationIterationCount::one()",
engines="gecko servo",
initial_specified_value="specified::AnimationIterationCount::one()",
vector=True,
need_index=True,
animation_value_type="none",
extra_prefixes=animation_extra_prefixes,
rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME,
spec="https://drafts.csswg.org/css-animations/#propdef-animation-iteration-count",
)}
<% animation_direction_custom_consts = { "alternate-reverse": "Alternate_reverse" } %>
${helpers.single_keyword(
"animation-direction",
"normal reverse alternate alternate-reverse",
engines="gecko servo",
need_index=True,
animation_value_type="none",
vector=True,
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",
rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME,
)}
${helpers.single_keyword(
"animation-play-state",
"running paused",
engines="gecko servo",
need_index=True,
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",
rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME,
)}
${helpers.single_keyword(
"animation-fill-mode",
"none forwards backwards both",
engines="gecko servo",
need_index=True,
animation_value_type="none",
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",
rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME,
)}
${helpers.predefined_type(
"animation-delay",
"Time",
"computed::Time::zero()",
engines="gecko servo",
initial_specified_value="specified::Time::zero()",
vector=True,
need_index=True,
animation_value_type="none",
extra_prefixes=animation_extra_prefixes,
spec="https://drafts.csswg.org/css-animations/#propdef-animation-delay",
rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME,
)}
${helpers.predefined_type(
"animation-timeline",
"AnimationTimeline",
"computed::AnimationTimeline::auto()",
engines="gecko servo",
servo_pref="layout.unimplemented",
initial_specified_value="specified::AnimationTimeline::auto()",
vector=True,
need_index=True,
animation_value_type="none",
gecko_pref="layout.css.scroll-linked-animations.enabled",
spec="https://drafts.csswg.org/css-animations-2/#propdef-animation-timeline",
rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME,
)}
<% transform_extra_prefixes = "moz:layout.css.prefixes.transforms webkit" %>
${helpers.predefined_type(
@ -285,15 +472,6 @@ ${helpers.predefined_type(
animation_value_type="discrete",
)}
${helpers.predefined_type(
"scroll-snap-stop",
"ScrollSnapStop",
"computed::ScrollSnapStop::Normal",
engines="gecko",
spec="https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-stop",
animation_value_type="discrete",
)}
% for (axis, logical) in ALL_AXES:
${helpers.predefined_type(
"overscroll-behavior-" + axis,
@ -445,28 +623,6 @@ ${helpers.predefined_type(
animation_value_type="none",
)}
${helpers.predefined_type(
"container-type",
"ContainerType",
"computed::ContainerType::NONE",
engines="gecko servo",
animation_value_type="none",
gecko_pref="layout.css.container-queries.enabled",
servo_pref="layout.container-queries.enabled",
spec="https://drafts.csswg.org/css-contain-3/#container-type",
)}
${helpers.predefined_type(
"container-name",
"ContainerName",
"computed::ContainerName::none()",
engines="gecko servo",
animation_value_type="none",
gecko_pref="layout.css.container-queries.enabled",
servo_pref="layout.container-queries.enabled",
spec="https://drafts.csswg.org/css-contain-3/#container-name",
)}
${helpers.predefined_type(
"appearance",
"Appearance",

View file

@ -28,16 +28,6 @@
)}
% endfor
${helpers.predefined_type(
"overflow-clip-margin",
"Length",
"computed::Length::zero()",
parse_method="parse_non_negative",
engines="gecko",
spec="https://drafts.csswg.org/css-overflow/#propdef-overflow-clip-margin",
animation_value_type="ComputedValue",
)}
% for side in ALL_SIDES:
${helpers.predefined_type(
"scroll-margin-%s" % side[0],

View file

@ -20,7 +20,7 @@ ${helpers.single_keyword(
${helpers.predefined_type(
"stop-color",
"Color",
"computed::Color::black()",
"RGBA::new(0, 0, 0, 255).into()",
engines="gecko",
animation_value_type="AnimatedRGBA",
spec="https://www.w3.org/TR/SVGTiny12/painting.html#StopColorProperty",
@ -40,7 +40,7 @@ ${helpers.predefined_type(
${helpers.predefined_type(
"flood-color",
"Color",
"computed::Color::black()",
"RGBA::new(0, 0, 0, 255).into()",
engines="gecko",
animation_value_type="AnimatedColor",
spec="https://www.w3.org/TR/SVG/filters.html#FloodColorProperty",
@ -58,7 +58,7 @@ ${helpers.predefined_type(
${helpers.predefined_type(
"lighting-color",
"Color",
"computed::Color::white()",
"RGBA::new(255, 255, 255, 255).into()",
engines="gecko",
animation_value_type="AnimatedColor",
spec="https://www.w3.org/TR/SVG/filters.html#LightingColorProperty",

View file

@ -3,7 +3,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
<%namespace name="helpers" file="/helpers.mako.rs" />
<% from data import DEFAULT_RULES_EXCEPT_KEYFRAME, Method %>
<% from data import Method %>
// CSS Basic User Interface Module Level 1
// https://drafts.csswg.org/css-ui-3/
@ -51,15 +51,12 @@ ${helpers.single_keyword(
spec="None (Nonstandard Firefox-only property)",
)}
// TODO(emilio): Maybe make shadow behavior on macOS match Linux / Windows, and remove this
// property.
${helpers.single_keyword(
"-moz-window-shadow",
"default none",
"default none menu tooltip sheet cliprounded",
engines="gecko",
gecko_ffi_name="mWindowShadow",
gecko_enum_prefix="StyleWindowShadow",
gecko_inexhaustive=True,
animation_value_type="discrete",
enabled_in="chrome",
spec="None (Nonstandard internal property)",
@ -98,16 +95,6 @@ ${helpers.predefined_type(
enabled_in="chrome",
)}
${helpers.predefined_type(
"-moz-window-input-region-margin",
"Length",
"computed::Length::zero()",
engines="gecko",
animation_value_type="ComputedValue",
spec="None (Nonstandard internal property)",
enabled_in="chrome",
)}
// TODO(emilio): Probably also should be hidden from content.
${helpers.predefined_type(
"-moz-force-broken-image-icon",
@ -117,190 +104,3 @@ ${helpers.predefined_type(
animation_value_type="discrete",
spec="None (Nonstandard Firefox-only property)",
)}
<% transition_extra_prefixes = "moz:layout.css.prefixes.transitions webkit" %>
${helpers.predefined_type(
"transition-duration",
"Time",
"computed::Time::zero()",
engines="gecko servo",
initial_specified_value="specified::Time::zero()",
parse_method="parse_non_negative",
vector=True,
need_index=True,
animation_value_type="none",
extra_prefixes=transition_extra_prefixes,
spec="https://drafts.csswg.org/css-transitions/#propdef-transition-duration",
)}
${helpers.predefined_type(
"transition-timing-function",
"TimingFunction",
"computed::TimingFunction::ease()",
engines="gecko servo",
initial_specified_value="specified::TimingFunction::ease()",
vector=True,
need_index=True,
animation_value_type="none",
extra_prefixes=transition_extra_prefixes,
spec="https://drafts.csswg.org/css-transitions/#propdef-transition-timing-function",
)}
${helpers.predefined_type(
"transition-property",
"TransitionProperty",
"computed::TransitionProperty::all()",
engines="gecko servo",
initial_specified_value="specified::TransitionProperty::all()",
vector=True,
allow_empty="NotInitial",
need_index=True,
animation_value_type="none",
extra_prefixes=transition_extra_prefixes,
spec="https://drafts.csswg.org/css-transitions/#propdef-transition-property",
)}
${helpers.predefined_type(
"transition-delay",
"Time",
"computed::Time::zero()",
engines="gecko servo",
initial_specified_value="specified::Time::zero()",
vector=True,
need_index=True,
animation_value_type="none",
extra_prefixes=transition_extra_prefixes,
spec="https://drafts.csswg.org/css-transitions/#propdef-transition-delay",
)}
<% animation_extra_prefixes = "moz:layout.css.prefixes.animations webkit" %>
${helpers.predefined_type(
"animation-name",
"AnimationName",
"computed::AnimationName::none()",
engines="gecko servo",
initial_specified_value="specified::AnimationName::none()",
vector=True,
need_index=True,
animation_value_type="none",
extra_prefixes=animation_extra_prefixes,
rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME,
spec="https://drafts.csswg.org/css-animations/#propdef-animation-name",
)}
${helpers.predefined_type(
"animation-duration",
"Time",
"computed::Time::zero()",
engines="gecko servo",
initial_specified_value="specified::Time::zero()",
parse_method="parse_non_negative",
vector=True,
need_index=True,
animation_value_type="none",
extra_prefixes=animation_extra_prefixes,
spec="https://drafts.csswg.org/css-transitions/#propdef-transition-duration",
)}
// animation-timing-function is the exception to the rule for allowed_in_keyframe_block:
// https://drafts.csswg.org/css-animations/#keyframes
${helpers.predefined_type(
"animation-timing-function",
"TimingFunction",
"computed::TimingFunction::ease()",
engines="gecko servo",
initial_specified_value="specified::TimingFunction::ease()",
vector=True,
need_index=True,
animation_value_type="none",
extra_prefixes=animation_extra_prefixes,
spec="https://drafts.csswg.org/css-transitions/#propdef-animation-timing-function",
)}
${helpers.predefined_type(
"animation-iteration-count",
"AnimationIterationCount",
"computed::AnimationIterationCount::one()",
engines="gecko servo",
initial_specified_value="specified::AnimationIterationCount::one()",
vector=True,
need_index=True,
animation_value_type="none",
extra_prefixes=animation_extra_prefixes,
rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME,
spec="https://drafts.csswg.org/css-animations/#propdef-animation-iteration-count",
)}
<% animation_direction_custom_consts = { "alternate-reverse": "Alternate_reverse" } %>
${helpers.single_keyword(
"animation-direction",
"normal reverse alternate alternate-reverse",
engines="gecko servo",
need_index=True,
animation_value_type="none",
vector=True,
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",
rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME,
)}
${helpers.single_keyword(
"animation-play-state",
"running paused",
engines="gecko servo",
need_index=True,
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",
rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME,
)}
${helpers.single_keyword(
"animation-fill-mode",
"none forwards backwards both",
engines="gecko servo",
need_index=True,
animation_value_type="none",
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",
rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME,
)}
${helpers.predefined_type(
"animation-delay",
"Time",
"computed::Time::zero()",
engines="gecko servo",
initial_specified_value="specified::Time::zero()",
vector=True,
need_index=True,
animation_value_type="none",
extra_prefixes=animation_extra_prefixes,
spec="https://drafts.csswg.org/css-animations/#propdef-animation-delay",
rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME,
)}
${helpers.predefined_type(
"animation-timeline",
"AnimationTimeline",
"computed::AnimationTimeline::auto()",
engines="gecko servo",
servo_pref="layout.unimplemented",
initial_specified_value="specified::AnimationTimeline::auto()",
vector=True,
need_index=True,
animation_value_type="none",
gecko_pref="layout.css.scroll-linked-animations.enabled",
spec="https://drafts.csswg.org/css-animations-2/#propdef-animation-timeline",
rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME,
)}

View file

@ -267,9 +267,6 @@ pub enum PropertyDeclaration {
% endfor
}
// There's one of these for each parsed declaration so it better be small.
size_of_test!(PropertyDeclaration, 32);
#[repr(C)]
struct PropertyDeclarationVariantRepr<T> {
tag: u16,
@ -477,10 +474,9 @@ impl NonCustomPropertyId {
self.0
}
/// Convert a `NonCustomPropertyId` into a `nsCSSPropertyID`.
#[cfg(feature = "gecko")]
#[inline]
pub fn to_nscsspropertyid(self) -> nsCSSPropertyID {
fn to_nscsspropertyid(self) -> nsCSSPropertyID {
// unsafe: guaranteed by static_assert_nscsspropertyid above.
unsafe { std::mem::transmute(self.0 as i32) }
}
@ -896,72 +892,6 @@ impl<'a> Iterator for LonghandIdSetIterator<'a> {
}
}
<%
CASCADE_GROUPS = {
# The writing-mode group has the most priority of all property groups, as
# sizes like font-size can depend on it.
"writing_mode": [
"writing-mode",
"direction",
"text-orientation",
],
# The fonts and colors group has the second priority, as all other lengths
# and colors depend on them.
#
# There are some interdependencies between these, but we fix them up in
# Cascade::fixup_font_stuff.
"fonts_and_color": [
# Needed to properly compute the zoomed font-size.
# FIXME(emilio): This could probably just be a cascade flag
# like IN_SVG_SUBTREE or such, and we could nuke this property.
"-x-text-zoom",
# Needed to do font-size computation in a language-dependent way.
"-x-lang",
# Needed for ruby to respect language-dependent min-font-size
# preferences properly, see bug 1165538.
"-moz-min-font-size-ratio",
# font-size depends on math-depth's computed value.
"math-depth",
# Needed to compute the first available font, in order to
# compute font-relative units correctly.
"font-size",
"font-weight",
"font-stretch",
"font-style",
"font-family",
# color-scheme affects how system colors resolve.
"color-scheme",
],
}
def in_late_group(p):
return p.name not in CASCADE_GROUPS["writing_mode"] and p.name not in CASCADE_GROUPS["fonts_and_color"]
def is_visited_dependent(p):
return p.name in [
"column-rule-color",
"text-emphasis-color",
"-webkit-text-fill-color",
"-webkit-text-stroke-color",
"text-decoration-color",
"fill",
"stroke",
"caret-color",
"background-color",
"border-top-color",
"border-right-color",
"border-bottom-color",
"border-left-color",
"border-block-start-color",
"border-inline-end-color",
"border-block-end-color",
"border-inline-start-color",
"outline-color",
"color",
]
%>
impl LonghandIdSet {
#[inline]
fn reset() -> &'static Self {
@ -990,7 +920,7 @@ impl LonghandIdSet {
/// Returns the set of longhands that are ignored when document colors are
/// disabled.
#[inline]
fn ignored_when_colors_disabled() -> &'static Self {
pub fn ignored_when_colors_disabled() -> &'static Self {
${static_longhand_id_set(
"IGNORED_WHEN_COLORS_DISABLED",
lambda p: p.ignored_when_colors_disabled
@ -998,48 +928,6 @@ impl LonghandIdSet {
&IGNORED_WHEN_COLORS_DISABLED
}
/// Only a few properties are allowed to depend on the visited state of
/// links. When cascading visited styles, we can save time by only
/// processing these properties.
fn visited_dependent() -> &'static Self {
${static_longhand_id_set(
"VISITED_DEPENDENT",
lambda p: is_visited_dependent(p)
)}
debug_assert!(Self::late_group().contains_all(&VISITED_DEPENDENT));
&VISITED_DEPENDENT
}
#[inline]
fn writing_mode_group() -> &'static Self {
${static_longhand_id_set(
"WRITING_MODE_GROUP",
lambda p: p.name in CASCADE_GROUPS["writing_mode"]
)}
&WRITING_MODE_GROUP
}
#[inline]
fn fonts_and_color_group() -> &'static Self {
${static_longhand_id_set(
"FONTS_AND_COLOR_GROUP",
lambda p: p.name in CASCADE_GROUPS["fonts_and_color"]
)}
&FONTS_AND_COLOR_GROUP
}
#[inline]
fn late_group_only_inherited() -> &'static Self {
${static_longhand_id_set("LATE_GROUP_ONLY_INHERITED", lambda p: p.style_struct.inherited and in_late_group(p))}
&LATE_GROUP_ONLY_INHERITED
}
#[inline]
fn late_group() -> &'static Self {
${static_longhand_id_set("LATE_GROUP", lambda p: in_late_group(p))}
&LATE_GROUP
}
/// Returns the set of properties that are declared as having no effect on
/// Gecko <scrollbar> elements or their descendant scrollbar parts.
#[cfg(debug_assertions)]
@ -1442,12 +1330,91 @@ impl LonghandId {
PropertyFlags::from_bits_truncate(FLAGS[self as usize])
}
/// Only a few properties are allowed to depend on the visited state of
/// links. When cascading visited styles, we can save time by only
/// processing these properties.
fn is_visited_dependent(&self) -> bool {
matches!(*self,
% if engine == "gecko":
LonghandId::ColumnRuleColor |
LonghandId::TextEmphasisColor |
LonghandId::WebkitTextFillColor |
LonghandId::WebkitTextStrokeColor |
LonghandId::TextDecorationColor |
LonghandId::Fill |
LonghandId::Stroke |
LonghandId::CaretColor |
% endif
LonghandId::BackgroundColor |
LonghandId::BorderTopColor |
LonghandId::BorderRightColor |
LonghandId::BorderBottomColor |
LonghandId::BorderLeftColor |
LonghandId::OutlineColor |
LonghandId::Color
)
}
/// Returns true if the property is one that is ignored when document
/// colors are disabled.
#[inline]
fn ignored_when_document_colors_disabled(self) -> bool {
LonghandIdSet::ignored_when_colors_disabled().contains(self)
}
/// The computed value of some properties depends on the (sometimes
/// computed) value of *other* properties.
///
/// So we classify properties into "early" and "other", such that the only
/// dependencies can be from "other" to "early".
///
/// Unfortunately, its not easy to check that this classification is
/// correct.
fn is_early_property(&self) -> bool {
matches!(*self,
% if engine == "gecko":
// Needed to properly compute the writing mode, to resolve logical
// properties, and similar stuff. In this block instead of along
// `WritingMode` and `Direction` just for convenience, since it's
// Gecko-only (for now at least).
//
// see WritingMode::new.
LonghandId::TextOrientation |
// Needed to properly compute the zoomed font-size.
//
// FIXME(emilio): This could probably just be a cascade flag like
// IN_SVG_SUBTREE or such, and we could nuke this property.
LonghandId::XTextZoom |
// Needed to do font-size computation in a language-dependent way.
LonghandId::XLang |
// Needed for ruby to respect language-dependent min-font-size
// preferences properly, see bug 1165538.
LonghandId::MozMinFontSizeRatio |
// font-size depends on math-depth's computed value.
LonghandId::MathDepth |
// color-scheme affects how system colors resolve.
LonghandId::ColorScheme |
% endif
// Needed to compute the first available font, in order to
// compute font-relative units correctly.
LonghandId::FontSize |
LonghandId::FontWeight |
LonghandId::FontStretch |
LonghandId::FontStyle |
LonghandId::FontFamily |
// Needed to properly compute the writing mode, to resolve logical
// properties, and similar stuff.
LonghandId::WritingMode |
LonghandId::Direction
)
}
}
/// An iterator over all the property ids that are enabled for a given
@ -2593,11 +2560,9 @@ impl PropertyDeclaration {
}
}
const SUB_PROPERTIES_ARRAY_CAP: usize =
${max(len(s.sub_properties) for s in data.shorthands_except_all()) \
if data.shorthands_except_all() else 0};
type SubpropertiesVec<T> = ArrayVec<T, SUB_PROPERTIES_ARRAY_CAP>;
type SubpropertiesVec<T> = ArrayVec<T, ${max(len(s.sub_properties) \
for s in data.shorthands_except_all()) \
if data.shorthands_except_all() else 0}>;
/// A stack-allocated vector of `PropertyDeclaration`
/// large enough to parse one CSS `key: value` declaration.
@ -2609,10 +2574,6 @@ pub struct SourcePropertyDeclaration {
all_shorthand: AllShorthand,
}
// This is huge, but we allocate it on the stack and then never move it,
// we only pass `&mut SourcePropertyDeclaration` references around.
size_of_test!(SourcePropertyDeclaration, 568);
impl SourcePropertyDeclaration {
/// Create one. Its big, try not to move it around.
#[inline]
@ -2657,7 +2618,11 @@ impl SourcePropertyDeclaration {
/// Return type of SourcePropertyDeclaration::drain
pub struct SourcePropertyDeclarationDrain<'a> {
declarations: ArrayVecDrain<'a, PropertyDeclaration, SUB_PROPERTIES_ARRAY_CAP>,
declarations: ArrayVecDrain<
'a, PropertyDeclaration,
${max(len(s.sub_properties) for s in data.shorthands_except_all()) \
if data.shorthands_except_all() else 0}
>,
all_shorthand: AllShorthand,
}
@ -2938,11 +2903,11 @@ pub mod style_structs {
% endif
% endfor
% if style_struct.name == "UI":
% if style_struct.name == "Box":
/// Returns whether there is any animation specified with
/// animation-name other than `none`.
pub fn specifies_animations(&self) -> bool {
self.animation_name_iter().any(|name| !name.is_none())
self.animation_name_iter().any(|name| name.0.is_some())
}
/// Returns whether there are any transitions specified.
@ -3070,7 +3035,7 @@ impl ComputedValues {
/// Returns whether this style's display value is equal to contents.
pub fn is_display_contents(&self) -> bool {
self.clone_display().is_contents()
self.get_box().clone_display().is_contents()
}
/// Gets a reference to the rule node. Panic if no rule node exists.
@ -3219,7 +3184,7 @@ impl ComputedValues {
/// style.resolve_color(style.get_border().clone_border_top_color());
#[inline]
pub fn resolve_color(&self, color: computed::Color) -> RGBA {
color.into_rgba(self.get_inherited_text().clone_color())
color.to_rgba(self.get_inherited_text().clone_color())
}
/// Returns which longhand properties have different values in the two
@ -4223,7 +4188,7 @@ macro_rules! css_properties_accessors {
/// Call the given macro with tokens like this for each longhand properties:
///
/// ```
/// { snake_case_ident }
/// { snake_case_ident, true }
/// ```
///
/// … where the boolean indicates whether the property value type
@ -4233,39 +4198,12 @@ macro_rules! longhand_properties_idents {
($macro_name: ident) => {
$macro_name! {
% for property in data.longhands:
{ ${property.ident} }
{ ${property.ident}, ${"true" if property.boxed else "false"} }
% endfor
}
}
}
// Large pages generate tens of thousands of ComputedValues.
size_of_test!(ComputedValues, 192);
// FFI relies on this.
size_of_test!(Option<Arc<ComputedValues>>, 8);
// There are two reasons for this test to fail:
//
// * Your changes made a specified value type for a given property go
// over the threshold. In that case, you should try to shrink it again
// or, if not possible, mark the property as boxed in the property
// definition.
//
// * Your changes made a specified value type smaller, so that it no
// longer needs to be boxed. In this case you just need to remove
// boxed=True from the property definition. Nice job!
#[cfg(target_pointer_width = "64")]
#[allow(dead_code)] // https://github.com/rust-lang/rust/issues/96952
const BOX_THRESHOLD: usize = 24;
% for longhand in data.longhands:
#[cfg(target_pointer_width = "64")]
% if longhand.boxed:
const_assert!(std::mem::size_of::<longhands::${longhand.ident}::SpecifiedValue>() > BOX_THRESHOLD);
% else:
const_assert!(std::mem::size_of::<longhands::${longhand.ident}::SpecifiedValue>() <= BOX_THRESHOLD);
% endif
% endfor
% if engine == "servo":
% for effect_name in ["repaint", "reflow_out_of_flow", "reflow", "rebuild_and_reflow_inline", "rebuild_and_reflow"]:
macro_rules! restyle_damage_${effect_name} {

View file

@ -24,6 +24,318 @@ ${helpers.two_properties_shorthand(
"(https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-clip-box)",
)}
macro_rules! try_parse_one {
($context: expr, $input: expr, $var: ident, $prop_module: ident) => {
if $var.is_none() {
if let Ok(value) = $input.try_parse(|i| {
$prop_module::single_value::parse($context, i)
}) {
$var = Some(value);
continue;
}
}
};
}
<%helpers:shorthand name="transition"
engines="gecko servo"
extra_prefixes="moz:layout.css.prefixes.transitions webkit"
sub_properties="transition-property transition-duration
transition-timing-function
transition-delay"
spec="https://drafts.csswg.org/css-transitions/#propdef-transition">
use crate::parser::Parse;
% for prop in "delay duration property timing_function".split():
use crate::properties::longhands::transition_${prop};
% endfor
use crate::values::specified::TransitionProperty;
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
struct SingleTransition {
% for prop in "duration timing_function delay".split():
transition_${prop}: transition_${prop}::SingleSpecifiedValue,
% endfor
// Unlike other properties, transition-property uses an Option<> to
// represent 'none' as `None`.
transition_property: Option<TransitionProperty>,
}
fn parse_one_transition<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<SingleTransition,ParseError<'i>> {
% for prop in "property duration timing_function delay".split():
let mut ${prop} = None;
% endfor
let mut parsed = 0;
loop {
parsed += 1;
try_parse_one!(context, input, duration, transition_duration);
try_parse_one!(context, input, timing_function, transition_timing_function);
try_parse_one!(context, input, delay, transition_delay);
// Must check 'transition-property' after 'transition-timing-function' since
// 'transition-property' accepts any keyword.
if property.is_none() {
if let Ok(value) = input.try_parse(|i| TransitionProperty::parse(context, i)) {
property = Some(Some(value));
continue;
}
if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
// 'none' is not a valid value for <single-transition-property>,
// so it's not acceptable in the function above.
property = Some(None);
continue;
}
}
parsed -= 1;
break
}
if parsed != 0 {
Ok(SingleTransition {
% for prop in "duration timing_function delay".split():
transition_${prop}: ${prop}.unwrap_or_else(transition_${prop}::single_value
::get_initial_specified_value),
% endfor
transition_property: property.unwrap_or(
Some(transition_property::single_value::get_initial_specified_value())),
})
} else {
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
}
}
% for prop in "property duration timing_function delay".split():
let mut ${prop}s = Vec::new();
% endfor
let results = input.parse_comma_separated(|i| parse_one_transition(context, i))?;
let multiple_items = results.len() >= 2;
for result in results {
if let Some(value) = result.transition_property {
propertys.push(value);
} else if multiple_items {
// If there is more than one item, and any of transitions has 'none',
// then it's invalid. Othersize, leave propertys to be empty (which
// means "transition-property: none");
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
% for prop in "duration timing_function delay".split():
${prop}s.push(result.transition_${prop});
% endfor
}
Ok(expanded! {
% for prop in "property duration timing_function delay".split():
transition_${prop}: transition_${prop}::SpecifiedValue(${prop}s.into()),
% endfor
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
let property_len = self.transition_property.0.len();
// There are two cases that we can do shorthand serialization:
// * when all value lists have the same length, or
// * when transition-property is none, and other value lists have exactly one item.
if property_len == 0 {
% for name in "duration delay timing_function".split():
if self.transition_${name}.0.len() != 1 {
return Ok(());
}
% endfor
} else {
% for name in "duration delay timing_function".split():
if self.transition_${name}.0.len() != property_len {
return Ok(());
}
% endfor
}
// Representative length.
let len = self.transition_duration.0.len();
for i in 0..len {
if i != 0 {
dest.write_str(", ")?;
}
if property_len == 0 {
dest.write_str("none")?;
} else {
self.transition_property.0[i].to_css(dest)?;
}
% for name in "duration timing_function delay".split():
dest.write_str(" ")?;
self.transition_${name}.0[i].to_css(dest)?;
% endfor
}
Ok(())
}
}
</%helpers:shorthand>
<%helpers:shorthand name="animation"
engines="gecko servo"
extra_prefixes="moz:layout.css.prefixes.animations webkit"
sub_properties="animation-name animation-duration
animation-timing-function animation-delay
animation-iteration-count animation-direction
animation-fill-mode animation-play-state animation-timeline"
rule_types_allowed="Style"
spec="https://drafts.csswg.org/css-animations/#propdef-animation">
<%
props = "name timeline duration timing_function delay iteration_count \
direction fill_mode play_state".split()
%>
% for prop in props:
use crate::properties::longhands::animation_${prop};
% endfor
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
struct SingleAnimation {
% for prop in props:
animation_${prop}: animation_${prop}::SingleSpecifiedValue,
% endfor
}
fn scroll_linked_animations_enabled() -> bool {
#[cfg(feature = "gecko")]
return static_prefs::pref!("layout.css.scroll-linked-animations.enabled");
#[cfg(feature = "servo")]
return false;
}
fn parse_one_animation<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<SingleAnimation, ParseError<'i>> {
% for prop in props:
let mut ${prop} = None;
% endfor
let mut parsed = 0;
// NB: Name must be the last one here so that keywords valid for other
// longhands are not interpreted as names.
//
// Also, duration must be before delay, see
// https://drafts.csswg.org/css-animations/#typedef-single-animation
loop {
parsed += 1;
try_parse_one!(context, input, duration, animation_duration);
try_parse_one!(context, input, timing_function, animation_timing_function);
try_parse_one!(context, input, delay, animation_delay);
try_parse_one!(context, input, iteration_count, animation_iteration_count);
try_parse_one!(context, input, direction, animation_direction);
try_parse_one!(context, input, fill_mode, animation_fill_mode);
try_parse_one!(context, input, play_state, animation_play_state);
try_parse_one!(context, input, name, animation_name);
if scroll_linked_animations_enabled() {
try_parse_one!(context, input, timeline, animation_timeline);
}
parsed -= 1;
break
}
// If nothing is parsed, this is an invalid entry.
if parsed == 0 {
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
} else {
Ok(SingleAnimation {
% for prop in props:
animation_${prop}: ${prop}.unwrap_or_else(animation_${prop}::single_value
::get_initial_specified_value),
% endfor
})
}
}
% for prop in props:
let mut ${prop}s = vec![];
% endfor
let results = input.parse_comma_separated(|i| parse_one_animation(context, i))?;
for result in results.into_iter() {
% for prop in props:
${prop}s.push(result.animation_${prop});
% endfor
}
Ok(expanded! {
% for prop in props:
animation_${prop}: animation_${prop}::SpecifiedValue(${prop}s.into()),
% endfor
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
let len = self.animation_name.0.len();
// There should be at least one declared value
if len == 0 {
return Ok(());
}
// If any value list length is differs then we don't do a shorthand serialization
// either.
% for name in props[2:]:
if len != self.animation_${name}.0.len() {
return Ok(())
}
% endfor
// If the preference of animation-timeline is disabled, `self.animation_timeline` is
// None.
if self.animation_timeline.map_or(false, |v| len != v.0.len()) {
return Ok(());
}
for i in 0..len {
if i != 0 {
dest.write_str(", ")?;
}
% for name in props[2:]:
self.animation_${name}.0[i].to_css(dest)?;
dest.write_str(" ")?;
% endfor
self.animation_name.0[i].to_css(dest)?;
// Based on the spec, the default values of other properties must be output in at
// least the cases necessary to distinguish an animation-name. The serialization
// order of animation-timeline is always later than animation-name, so it's fine
// to not serialize it if it is the default value. It's still possible to
// distinguish them (because we always serialize animation-name).
// https://drafts.csswg.org/css-animations-1/#animation
// https://drafts.csswg.org/css-animations-2/#typedef-single-animation
//
// Note: it's also fine to always serialize this. However, it seems Blink
// doesn't serialize default animation-timeline now, so we follow the same rule.
if let Some(ref timeline) = self.animation_timeline {
if !timeline.0[i].is_auto() {
dest.write_char(' ')?;
timeline.0[i].to_css(dest)?;
}
}
}
Ok(())
}
}
</%helpers:shorthand>
${helpers.two_properties_shorthand(
"overscroll-behavior",
"overscroll-behavior-x",
@ -33,45 +345,6 @@ ${helpers.two_properties_shorthand(
spec="https://wicg.github.io/overscroll-behavior/#overscroll-behavior-properties",
)}
<%helpers:shorthand
engines="gecko"
name="container"
sub_properties="container-name container-type"
gecko_pref="layout.css.container-queries.enabled",
spec="https://drafts.csswg.org/css-contain-3/#container-shorthand"
>
pub fn parse_value<'i>(
context: &ParserContext,
input: &mut Parser<'i, '_>,
) -> Result<Longhands, ParseError<'i>> {
use crate::parser::Parse;
use crate::values::specified::box_::{ContainerName, ContainerType};
// See https://github.com/w3c/csswg-drafts/issues/7180 for why we don't
// match the spec.
let container_name = ContainerName::parse(context, input)?;
let container_type = if input.try_parse(|input| input.expect_delim('/')).is_ok() {
ContainerType::parse(context, input)?
} else {
ContainerType::NONE
};
Ok(expanded! {
container_name: container_name,
container_type: container_type,
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
self.container_name.to_css(dest)?;
if !self.container_type.is_empty() {
dest.write_str(" / ")?;
self.container_type.to_css(dest)?;
}
Ok(())
}
}
</%helpers:shorthand>
<%helpers:shorthand
engines="gecko"
name="page-break-before"

View file

@ -1,317 +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 https://mozilla.org/MPL/2.0/. */
<%namespace name="helpers" file="/helpers.mako.rs" />
macro_rules! try_parse_one {
($context: expr, $input: expr, $var: ident, $prop_module: ident) => {
if $var.is_none() {
if let Ok(value) = $input.try_parse(|i| {
$prop_module::single_value::parse($context, i)
}) {
$var = Some(value);
continue;
}
}
};
}
<%helpers:shorthand name="transition"
engines="gecko servo"
extra_prefixes="moz:layout.css.prefixes.transitions webkit"
sub_properties="transition-property transition-duration
transition-timing-function
transition-delay"
spec="https://drafts.csswg.org/css-transitions/#propdef-transition">
use crate::parser::Parse;
% for prop in "delay duration property timing_function".split():
use crate::properties::longhands::transition_${prop};
% endfor
use crate::values::specified::TransitionProperty;
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
struct SingleTransition {
% for prop in "duration timing_function delay".split():
transition_${prop}: transition_${prop}::SingleSpecifiedValue,
% endfor
// Unlike other properties, transition-property uses an Option<> to
// represent 'none' as `None`.
transition_property: Option<TransitionProperty>,
}
fn parse_one_transition<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<SingleTransition,ParseError<'i>> {
% for prop in "property duration timing_function delay".split():
let mut ${prop} = None;
% endfor
let mut parsed = 0;
loop {
parsed += 1;
try_parse_one!(context, input, duration, transition_duration);
try_parse_one!(context, input, timing_function, transition_timing_function);
try_parse_one!(context, input, delay, transition_delay);
// Must check 'transition-property' after 'transition-timing-function' since
// 'transition-property' accepts any keyword.
if property.is_none() {
if let Ok(value) = input.try_parse(|i| TransitionProperty::parse(context, i)) {
property = Some(Some(value));
continue;
}
if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
// 'none' is not a valid value for <single-transition-property>,
// so it's not acceptable in the function above.
property = Some(None);
continue;
}
}
parsed -= 1;
break
}
if parsed != 0 {
Ok(SingleTransition {
% for prop in "duration timing_function delay".split():
transition_${prop}: ${prop}.unwrap_or_else(transition_${prop}::single_value
::get_initial_specified_value),
% endfor
transition_property: property.unwrap_or(
Some(transition_property::single_value::get_initial_specified_value())),
})
} else {
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
}
}
% for prop in "property duration timing_function delay".split():
let mut ${prop}s = Vec::new();
% endfor
let results = input.parse_comma_separated(|i| parse_one_transition(context, i))?;
let multiple_items = results.len() >= 2;
for result in results {
if let Some(value) = result.transition_property {
propertys.push(value);
} else if multiple_items {
// If there is more than one item, and any of transitions has 'none',
// then it's invalid. Othersize, leave propertys to be empty (which
// means "transition-property: none");
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
% for prop in "duration timing_function delay".split():
${prop}s.push(result.transition_${prop});
% endfor
}
Ok(expanded! {
% for prop in "property duration timing_function delay".split():
transition_${prop}: transition_${prop}::SpecifiedValue(${prop}s.into()),
% endfor
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
let property_len = self.transition_property.0.len();
// There are two cases that we can do shorthand serialization:
// * when all value lists have the same length, or
// * when transition-property is none, and other value lists have exactly one item.
if property_len == 0 {
% for name in "duration delay timing_function".split():
if self.transition_${name}.0.len() != 1 {
return Ok(());
}
% endfor
} else {
% for name in "duration delay timing_function".split():
if self.transition_${name}.0.len() != property_len {
return Ok(());
}
% endfor
}
// Representative length.
let len = self.transition_duration.0.len();
for i in 0..len {
if i != 0 {
dest.write_str(", ")?;
}
if property_len == 0 {
dest.write_str("none")?;
} else {
self.transition_property.0[i].to_css(dest)?;
}
% for name in "duration timing_function delay".split():
dest.write_str(" ")?;
self.transition_${name}.0[i].to_css(dest)?;
% endfor
}
Ok(())
}
}
</%helpers:shorthand>
<%helpers:shorthand name="animation"
engines="gecko servo"
extra_prefixes="moz:layout.css.prefixes.animations webkit"
sub_properties="animation-name animation-duration
animation-timing-function animation-delay
animation-iteration-count animation-direction
animation-fill-mode animation-play-state animation-timeline"
rule_types_allowed="Style"
spec="https://drafts.csswg.org/css-animations/#propdef-animation">
<%
props = "name timeline duration timing_function delay iteration_count \
direction fill_mode play_state".split()
%>
% for prop in props:
use crate::properties::longhands::animation_${prop};
% endfor
pub fn parse_value<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
struct SingleAnimation {
% for prop in props:
animation_${prop}: animation_${prop}::SingleSpecifiedValue,
% endfor
}
fn scroll_linked_animations_enabled() -> bool {
#[cfg(feature = "gecko")]
return static_prefs::pref!("layout.css.scroll-linked-animations.enabled");
#[cfg(feature = "servo")]
return false;
}
fn parse_one_animation<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<SingleAnimation, ParseError<'i>> {
% for prop in props:
let mut ${prop} = None;
% endfor
let mut parsed = 0;
// NB: Name must be the last one here so that keywords valid for other
// longhands are not interpreted as names.
//
// Also, duration must be before delay, see
// https://drafts.csswg.org/css-animations/#typedef-single-animation
loop {
parsed += 1;
try_parse_one!(context, input, duration, animation_duration);
try_parse_one!(context, input, timing_function, animation_timing_function);
try_parse_one!(context, input, delay, animation_delay);
try_parse_one!(context, input, iteration_count, animation_iteration_count);
try_parse_one!(context, input, direction, animation_direction);
try_parse_one!(context, input, fill_mode, animation_fill_mode);
try_parse_one!(context, input, play_state, animation_play_state);
try_parse_one!(context, input, name, animation_name);
if scroll_linked_animations_enabled() {
try_parse_one!(context, input, timeline, animation_timeline);
}
parsed -= 1;
break
}
// If nothing is parsed, this is an invalid entry.
if parsed == 0 {
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
} else {
Ok(SingleAnimation {
% for prop in props:
animation_${prop}: ${prop}.unwrap_or_else(animation_${prop}::single_value
::get_initial_specified_value),
% endfor
})
}
}
% for prop in props:
let mut ${prop}s = vec![];
% endfor
let results = input.parse_comma_separated(|i| parse_one_animation(context, i))?;
for result in results.into_iter() {
% for prop in props:
${prop}s.push(result.animation_${prop});
% endfor
}
Ok(expanded! {
% for prop in props:
animation_${prop}: animation_${prop}::SpecifiedValue(${prop}s.into()),
% endfor
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
let len = self.animation_name.0.len();
// There should be at least one declared value
if len == 0 {
return Ok(());
}
// If any value list length is differs then we don't do a shorthand serialization
// either.
% for name in props[2:]:
if len != self.animation_${name}.0.len() {
return Ok(())
}
% endfor
// If the preference of animation-timeline is disabled, `self.animation_timeline` is
// None.
if self.animation_timeline.map_or(false, |v| len != v.0.len()) {
return Ok(());
}
for i in 0..len {
if i != 0 {
dest.write_str(", ")?;
}
% for name in props[2:]:
self.animation_${name}.0[i].to_css(dest)?;
dest.write_str(" ")?;
% endfor
self.animation_name.0[i].to_css(dest)?;
// Based on the spec, the default values of other properties must be output in at
// least the cases necessary to distinguish an animation-name. The serialization
// order of animation-timeline is always later than animation-name, so it's fine
// to not serialize it if it is the default value. It's still possible to
// distinguish them (because we always serialize animation-name).
// https://drafts.csswg.org/css-animations-1/#animation
// https://drafts.csswg.org/css-animations-2/#typedef-single-animation
//
// Note: it's also fine to always serialize this. However, it seems Blink
// doesn't serialize default animation-timeline now, so we follow the same rule.
if let Some(ref timeline) = self.animation_timeline {
if !timeline.0[i].is_auto() {
dest.write_char(' ')?;
timeline.0[i].to_css(dest)?;
}
}
}
Ok(())
}
}
</%helpers:shorthand>