mirror of
https://github.com/servo/servo.git
synced 2025-08-07 22:45:34 +01:00
This reverts commit 8e15389cae
.
This commit is contained in:
parent
8e15389cae
commit
d6ae8dc112
152 changed files with 4622 additions and 5862 deletions
|
@ -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 {
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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,
|
||||
)}
|
||||
|
|
|
@ -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, it’s 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. It’s 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} {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
Loading…
Add table
Add a link
Reference in a new issue