diff --git a/components/style/gecko/arc_types.rs b/components/style/gecko/arc_types.rs index 32294ad1ce1e..6619059d54c8 100644 --- a/components/style/gecko/arc_types.rs +++ b/components/style/gecko/arc_types.rs @@ -8,20 +8,23 @@ #![allow(non_snake_case, missing_docs)] -use gecko_bindings::bindings::{RawServoMediaList, RawServoMediaRule, RawServoNamespaceRule, RawServoPageRule}; +use gecko_bindings::bindings::{RawServoKeyframe, RawServoKeyframesRule}; +use gecko_bindings::bindings::{RawServoMediaList, RawServoMediaRule}; +use gecko_bindings::bindings::{RawServoNamespaceRule, RawServoPageRule}; use gecko_bindings::bindings::{RawServoRuleNode, RawServoRuleNodeStrong, RawServoDocumentRule}; use gecko_bindings::bindings::{RawServoStyleSheet, RawServoImportRule, RawServoSupportsRule}; use gecko_bindings::bindings::{ServoComputedValues, ServoCssRules}; use gecko_bindings::structs::{RawServoDeclarationBlock, RawServoStyleRule}; use gecko_bindings::structs::RawServoAnimationValue; use gecko_bindings::sugar::ownership::{HasArcFFI, HasFFI}; +use keyframes::Keyframe; use media_queries::MediaList; use properties::{ComputedValues, PropertyDeclarationBlock}; use properties::animated_properties::AnimationValue; use rule_tree::StrongRuleNode; use shared_lock::Locked; use std::{mem, ptr}; -use stylesheets::{CssRules, Stylesheet, StyleRule, ImportRule, MediaRule}; +use stylesheets::{CssRules, Stylesheet, StyleRule, ImportRule, KeyframesRule, MediaRule}; use stylesheets::{NamespaceRule, PageRule, SupportsRule, DocumentRule}; macro_rules! impl_arc_ffi { @@ -64,6 +67,12 @@ impl_arc_ffi!(Locked => RawServoImportRule impl_arc_ffi!(AnimationValue => RawServoAnimationValue [Servo_AnimationValue_AddRef, Servo_AnimationValue_Release]); +impl_arc_ffi!(Locked => RawServoKeyframe + [Servo_Keyframe_AddRef, Servo_Keyframe_Release]); + +impl_arc_ffi!(Locked => RawServoKeyframesRule + [Servo_KeyframesRule_AddRef, Servo_KeyframesRule_Release]); + impl_arc_ffi!(Locked => RawServoMediaList [Servo_MediaList_AddRef, Servo_MediaList_Release]); diff --git a/components/style/gecko/generated/bindings.rs b/components/style/gecko/generated/bindings.rs index ee99ffb4fe0c..f086bbbb49f2 100644 --- a/components/style/gecko/generated/bindings.rs +++ b/components/style/gecko/generated/bindings.rs @@ -229,6 +229,16 @@ pub struct RawServoImportRule(RawServoImportRuleVoid); pub type RawServoAnimationValueStrong = ::gecko_bindings::sugar::ownership::Strong; pub type RawServoAnimationValueBorrowed<'a> = &'a RawServoAnimationValue; pub type RawServoAnimationValueBorrowedOrNull<'a> = Option<&'a RawServoAnimationValue>; +pub type RawServoKeyframeStrong = ::gecko_bindings::sugar::ownership::Strong; +pub type RawServoKeyframeBorrowed<'a> = &'a RawServoKeyframe; +pub type RawServoKeyframeBorrowedOrNull<'a> = Option<&'a RawServoKeyframe>; +enum RawServoKeyframeVoid { } +pub struct RawServoKeyframe(RawServoKeyframeVoid); +pub type RawServoKeyframesRuleStrong = ::gecko_bindings::sugar::ownership::Strong; +pub type RawServoKeyframesRuleBorrowed<'a> = &'a RawServoKeyframesRule; +pub type RawServoKeyframesRuleBorrowedOrNull<'a> = Option<&'a RawServoKeyframesRule>; +enum RawServoKeyframesRuleVoid { } +pub struct RawServoKeyframesRule(RawServoKeyframesRuleVoid); pub type RawServoMediaListStrong = ::gecko_bindings::sugar::ownership::Strong; pub type RawServoMediaListBorrowed<'a> = &'a RawServoMediaList; pub type RawServoMediaListBorrowedOrNull<'a> = Option<&'a RawServoMediaList>; @@ -395,6 +405,18 @@ extern "C" { extern "C" { pub fn Servo_AnimationValue_Release(ptr: RawServoAnimationValueBorrowed); } +extern "C" { + pub fn Servo_Keyframe_AddRef(ptr: RawServoKeyframeBorrowed); +} +extern "C" { + pub fn Servo_Keyframe_Release(ptr: RawServoKeyframeBorrowed); +} +extern "C" { + pub fn Servo_KeyframesRule_AddRef(ptr: RawServoKeyframesRuleBorrowed); +} +extern "C" { + pub fn Servo_KeyframesRule_Release(ptr: RawServoKeyframesRuleBorrowed); +} extern "C" { pub fn Servo_MediaList_AddRef(ptr: RawServoMediaListBorrowed); } @@ -1804,6 +1826,28 @@ extern "C" { pub fn Servo_StyleRule_GetCssText(rule: RawServoStyleRuleBorrowed, result: *mut nsAString); } +extern "C" { + pub fn Servo_Keyframe_Debug(rule: RawServoKeyframeBorrowed, + result: *mut nsACString); +} +extern "C" { + pub fn Servo_Keyframe_GetCssText(rule: RawServoKeyframeBorrowed, + result: *mut nsAString); +} +extern "C" { + pub fn Servo_CssRules_GetKeyframesRuleAt(rules: ServoCssRulesBorrowed, + index: u32, line: *mut u32, + column: *mut u32) + -> RawServoKeyframesRuleStrong; +} +extern "C" { + pub fn Servo_KeyframesRule_Debug(rule: RawServoKeyframesRuleBorrowed, + result: *mut nsACString); +} +extern "C" { + pub fn Servo_KeyframesRule_GetCssText(rule: RawServoKeyframesRuleBorrowed, + result: *mut nsAString); +} extern "C" { pub fn Servo_CssRules_GetMediaRuleAt(rules: ServoCssRulesBorrowed, index: u32, line: *mut u32, @@ -1909,6 +1953,54 @@ extern "C" { pub fn Servo_StyleRule_GetSelectorText(rule: RawServoStyleRuleBorrowed, result: *mut nsAString); } +extern "C" { + pub fn Servo_Keyframe_GetKeyText(keyframe: RawServoKeyframeBorrowed, + result: *mut nsAString); +} +extern "C" { + pub fn Servo_Keyframe_SetKeyText(keyframe: RawServoKeyframeBorrowed, + text: *const nsACString) -> bool; +} +extern "C" { + pub fn Servo_Keyframe_GetStyle(keyframe: RawServoKeyframeBorrowed) + -> RawServoDeclarationBlockStrong; +} +extern "C" { + pub fn Servo_Keyframe_SetStyle(keyframe: RawServoKeyframeBorrowed, + declarations: + RawServoDeclarationBlockBorrowed); +} +extern "C" { + pub fn Servo_KeyframesRule_GetName(rule: RawServoKeyframesRuleBorrowed) + -> *mut nsIAtom; +} +extern "C" { + pub fn Servo_KeyframesRule_SetName(rule: RawServoKeyframesRuleBorrowed, + name: *mut nsIAtom); +} +extern "C" { + pub fn Servo_KeyframesRule_GetCount(rule: RawServoKeyframesRuleBorrowed) + -> u32; +} +extern "C" { + pub fn Servo_KeyframesRule_GetKeyframe(rule: + RawServoKeyframesRuleBorrowed, + index: u32) + -> RawServoKeyframeStrong; +} +extern "C" { + pub fn Servo_KeyframesRule_FindRule(rule: RawServoKeyframesRuleBorrowed, + key: *const nsACString) -> u32; +} +extern "C" { + pub fn Servo_KeyframesRule_AppendRule(rule: RawServoKeyframesRuleBorrowed, + sheet: RawServoStyleSheetBorrowed, + css: *const nsACString) -> bool; +} +extern "C" { + pub fn Servo_KeyframesRule_DeleteRule(rule: RawServoKeyframesRuleBorrowed, + index: u32); +} extern "C" { pub fn Servo_MediaRule_GetMedia(rule: RawServoMediaRuleBorrowed) -> RawServoMediaListStrong; diff --git a/components/style/keyframes.rs b/components/style/keyframes.rs index dc4a75806a59..ab797b191f33 100644 --- a/components/style/keyframes.rs +++ b/components/style/keyframes.rs @@ -71,8 +71,21 @@ impl KeyframePercentage { /// A keyframes selector is a list of percentages or from/to symbols, which are /// converted at parse time to percentages. -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] pub struct KeyframeSelector(Vec); + +impl ToCss for KeyframeSelector { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + let mut iter = self.0.iter(); + iter.next().unwrap().to_css(dest)?; + for percentage in iter { + write!(dest, ", ")?; + percentage.to_css(dest)?; + } + Ok(()) + } +} + impl KeyframeSelector { /// Return the list of percentages this selector contains. #[inline] @@ -108,12 +121,7 @@ pub struct Keyframe { impl ToCssWithGuard for Keyframe { fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result where W: fmt::Write { - let mut iter = self.selector.percentages().iter(); - try!(iter.next().unwrap().to_css(dest)); - for percentage in iter { - try!(write!(dest, ", ")); - try!(percentage.to_css(dest)); - } + self.selector.to_css(dest)?; try!(dest.write_str(" { ")); try!(self.block.read_with(guard).to_css(dest)); try!(dest.write_str(" }")); @@ -121,7 +129,6 @@ impl ToCssWithGuard for Keyframe { } } - impl Keyframe { /// Parse a CSS keyframe. pub fn parse(css: &str, parent_stylesheet: &Stylesheet) diff --git a/components/style/stylesheets.rs b/components/style/stylesheets.rs index 97907d7082b5..2c55c73fd8dd 100644 --- a/components/style/stylesheets.rs +++ b/components/style/stylesheets.rs @@ -25,7 +25,7 @@ pub use gecko::rules::{CounterStyleRule, FontFaceRule}; use gecko_bindings::structs::URLExtraData; #[cfg(feature = "gecko")] use gecko_bindings::sugar::refptr::RefPtr; -use keyframes::{Keyframe, parse_keyframe_list}; +use keyframes::{Keyframe, KeyframeSelector, parse_keyframe_list}; use media_queries::{Device, MediaList, parse_media_query_list}; use parking_lot::RwLock; use parser::{PARSING_MODE_DEFAULT, Parse, ParserContext, log_css_error}; @@ -565,6 +565,8 @@ pub struct KeyframesRule { pub keyframes: Vec>>, /// Vendor prefix type the @keyframes has. pub vendor_prefix: Option, + /// The line and column of the rule's source code. + pub source_location: SourceLocation, } impl ToCssWithGuard for KeyframesRule { @@ -573,18 +575,32 @@ impl ToCssWithGuard for KeyframesRule { where W: fmt::Write { try!(dest.write_str("@keyframes ")); try!(self.name.to_css(dest)); - try!(dest.write_str(" { ")); + try!(dest.write_str(" {")); let iter = self.keyframes.iter(); - let mut first = true; for lock in iter { - if !first { - try!(dest.write_str(" ")); - } - first = false; + try!(dest.write_str("\n")); let keyframe = lock.read_with(&guard); try!(keyframe.to_css(guard, dest)); } - dest.write_str(" }") + dest.write_str("\n}") + } +} + +impl KeyframesRule { + /// Returns the index of the last keyframe that matches the given selector. + /// If the selector is not valid, or no keyframe is found, returns None. + /// + /// Related spec: + /// https://drafts.csswg.org/css-animations-1/#interface-csskeyframesrule-findrule + pub fn find_rule(&self, guard: &SharedRwLockReadGuard, selector: &str) -> Option { + if let Ok(selector) = Parser::new(selector).parse_entirely(KeyframeSelector::parse) { + for (i, keyframe) in self.keyframes.iter().enumerate().rev() { + if keyframe.read_with(guard).selector == selector { + return Some(i); + } + } + } + None } } @@ -1032,7 +1048,7 @@ enum AtRulePrelude { /// A @viewport rule prelude. Viewport, /// A @keyframes rule, with its animation name and vendor prefix if exists. - Keyframes(KeyframesName, Option), + Keyframes(KeyframesName, Option, SourceLocation), /// A @page rule prelude. Page(SourceLocation), /// A @document rule, with its conditional. @@ -1257,7 +1273,7 @@ impl<'a, 'b> AtRuleParser for NestedRuleParser<'a, 'b> { } let name = KeyframesName::parse(self.context, input)?; - Ok(AtRuleType::WithBlock(AtRulePrelude::Keyframes(name, prefix))) + Ok(AtRuleType::WithBlock(AtRulePrelude::Keyframes(name, prefix, location))) }, "page" => { if cfg!(feature = "gecko") { @@ -1311,12 +1327,13 @@ impl<'a, 'b> AtRuleParser for NestedRuleParser<'a, 'b> { Ok(CssRule::Viewport(Arc::new(self.shared_lock.wrap( try!(ViewportRule::parse(&context, input)))))) } - AtRulePrelude::Keyframes(name, prefix) => { + AtRulePrelude::Keyframes(name, prefix, location) => { let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::Keyframes)); Ok(CssRule::Keyframes(Arc::new(self.shared_lock.wrap(KeyframesRule { name: name, keyframes: parse_keyframe_list(&context, input, self.shared_lock), vendor_prefix: prefix, + source_location: location, })))) } AtRulePrelude::Page(location) => { diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index d04625727aa5..c56d9ebb39f8 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -29,6 +29,8 @@ use style::gecko_bindings::bindings; use style::gecko_bindings::bindings::{RawGeckoKeyframeListBorrowed, RawGeckoKeyframeListBorrowedMut}; use style::gecko_bindings::bindings::{RawServoDeclarationBlockBorrowed, RawServoDeclarationBlockStrong}; use style::gecko_bindings::bindings::{RawServoDocumentRule, RawServoDocumentRuleBorrowed}; +use style::gecko_bindings::bindings::{RawServoKeyframe, RawServoKeyframeBorrowed, RawServoKeyframeStrong}; +use style::gecko_bindings::bindings::{RawServoKeyframesRule, RawServoKeyframesRuleBorrowed}; use style::gecko_bindings::bindings::{RawServoMediaList, RawServoMediaListBorrowed, RawServoMediaListStrong}; use style::gecko_bindings::bindings::{RawServoMediaRule, RawServoMediaRuleBorrowed}; use style::gecko_bindings::bindings::{RawServoNamespaceRule, RawServoNamespaceRuleBorrowed}; @@ -74,7 +76,7 @@ use style::gecko_bindings::sugar::ownership::{FFIArcHelpers, HasFFI, HasArcFFI, use style::gecko_bindings::sugar::ownership::{HasSimpleFFI, Strong}; use style::gecko_bindings::sugar::refptr::RefPtr; use style::gecko_properties::{self, style_structs}; -use style::keyframes::KeyframesStepValue; +use style::keyframes::{Keyframe, KeyframeSelector, KeyframesStepValue}; use style::media_queries::{MediaList, parse_media_query_list}; use style::parallel; use style::parser::{PARSING_MODE_DEFAULT, ParserContext}; @@ -92,7 +94,7 @@ use style::string_cache::Atom; use style::style_adjuster::StyleAdjuster; use style::stylearc::Arc; use style::stylesheets::{CssRule, CssRules, CssRuleType, CssRulesHelpers}; -use style::stylesheets::{ImportRule, MediaRule, NamespaceRule, Origin}; +use style::stylesheets::{ImportRule, KeyframesRule, MediaRule, NamespaceRule, Origin}; use style::stylesheets::{PageRule, Stylesheet, StyleRule, SupportsRule, DocumentRule}; use style::stylesheets::StylesheetLoader as StyleStylesheetLoader; use style::supports::parse_condition_or_declaration; @@ -100,6 +102,7 @@ use style::thread_state; use style::timer::Timer; use style::traversal::{ANIMATION_ONLY, FOR_RECONSTRUCT, UNSTYLED_CHILDREN_ONLY}; use style::traversal::{resolve_style, DomTraversal, TraversalDriver, TraversalFlags}; +use style::values::{CustomIdent, KeyframesName}; use style_traits::ToCss; use super::stylesheet_loader::StylesheetLoader; @@ -774,6 +777,28 @@ pub extern "C" fn Servo_CssRules_DeleteRule(rules: ServoCssRulesBorrowed, index: }) } +macro_rules! impl_basic_rule_funcs_without_getter { + { ($rule_type:ty, $raw_type:ty), + debug: $debug:ident, + to_css: $to_css:ident, + } => { + #[no_mangle] + pub extern "C" fn $debug(rule: &$raw_type, result: *mut nsACString) { + read_locked_arc(rule, |rule: &$rule_type| { + write!(unsafe { result.as_mut().unwrap() }, "{:?}", *rule).unwrap(); + }) + } + + #[no_mangle] + pub extern "C" fn $to_css(rule: &$raw_type, result: *mut nsAString) { + let global_style_data = &*GLOBAL_STYLE_DATA; + let guard = global_style_data.shared_lock.read(); + let rule = Locked::<$rule_type>::as_arc(&rule); + rule.read_with(&guard).to_css(&guard, unsafe { result.as_mut().unwrap() }).unwrap(); + } + } +} + macro_rules! impl_basic_rule_funcs { { ($name:ident, $rule_type:ty, $raw_type:ty), getter: $getter:ident, @@ -801,19 +826,9 @@ macro_rules! impl_basic_rule_funcs { } } - #[no_mangle] - pub extern "C" fn $debug(rule: &$raw_type, result: *mut nsACString) { - read_locked_arc(rule, |rule: &$rule_type| { - write!(unsafe { result.as_mut().unwrap() }, "{:?}", *rule).unwrap(); - }) - } - - #[no_mangle] - pub extern "C" fn $to_css(rule: &$raw_type, result: *mut nsAString) { - let global_style_data = &*GLOBAL_STYLE_DATA; - let guard = global_style_data.shared_lock.read(); - let rule = Locked::<$rule_type>::as_arc(&rule); - rule.read_with(&guard).to_css(&guard, unsafe { result.as_mut().unwrap() }).unwrap(); + impl_basic_rule_funcs_without_getter! { ($rule_type, $raw_type), + debug: $debug, + to_css: $to_css, } } } @@ -840,6 +855,17 @@ impl_basic_rule_funcs! { (Style, StyleRule, RawServoStyleRule), to_css: Servo_StyleRule_GetCssText, } +impl_basic_rule_funcs_without_getter! { (Keyframe, RawServoKeyframe), + debug: Servo_Keyframe_Debug, + to_css: Servo_Keyframe_GetCssText, +} + +impl_basic_rule_funcs! { (Keyframes, KeyframesRule, RawServoKeyframesRule), + getter: Servo_CssRules_GetKeyframesRuleAt, + debug: Servo_KeyframesRule_Debug, + to_css: Servo_KeyframesRule_GetCssText, +} + impl_group_rule_funcs! { (Media, MediaRule, RawServoMediaRule), get_rules: Servo_MediaRule_GetRules, getter: Servo_CssRules_GetMediaRuleAt, @@ -918,6 +944,99 @@ pub extern "C" fn Servo_StyleRule_GetSelectorText(rule: RawServoStyleRuleBorrowe }) } +#[no_mangle] +pub extern "C" fn Servo_Keyframe_GetKeyText(keyframe: RawServoKeyframeBorrowed, result: *mut nsAString) { + read_locked_arc(keyframe, |keyframe: &Keyframe| { + keyframe.selector.to_css(unsafe { result.as_mut().unwrap() }).unwrap() + }) +} + +#[no_mangle] +pub extern "C" fn Servo_Keyframe_SetKeyText(keyframe: RawServoKeyframeBorrowed, text: *const nsACString) -> bool { + let text = unsafe { text.as_ref().unwrap().as_str_unchecked() }; + if let Ok(selector) = Parser::new(&text).parse_entirely(KeyframeSelector::parse) { + write_locked_arc(keyframe, |keyframe: &mut Keyframe| { + keyframe.selector = selector; + }); + true + } else { + false + } +} + +#[no_mangle] +pub extern "C" fn Servo_Keyframe_GetStyle(keyframe: RawServoKeyframeBorrowed) -> RawServoDeclarationBlockStrong { + read_locked_arc(keyframe, |keyframe: &Keyframe| keyframe.block.clone().into_strong()) +} + +#[no_mangle] +pub extern "C" fn Servo_Keyframe_SetStyle(keyframe: RawServoKeyframeBorrowed, + declarations: RawServoDeclarationBlockBorrowed) { + let declarations = Locked::::as_arc(&declarations); + write_locked_arc(keyframe, |keyframe: &mut Keyframe| { + keyframe.block = declarations.clone(); + }) +} + +#[no_mangle] +pub extern "C" fn Servo_KeyframesRule_GetName(rule: RawServoKeyframesRuleBorrowed) -> *mut nsIAtom { + read_locked_arc(rule, |rule: &KeyframesRule| rule.name.as_atom().as_ptr()) +} + +#[no_mangle] +pub extern "C" fn Servo_KeyframesRule_SetName(rule: RawServoKeyframesRuleBorrowed, name: *mut nsIAtom) { + write_locked_arc(rule, |rule: &mut KeyframesRule| { + rule.name = KeyframesName::Ident(CustomIdent(unsafe { Atom::from_addrefed(name) })); + }) +} + +#[no_mangle] +pub extern "C" fn Servo_KeyframesRule_GetCount(rule: RawServoKeyframesRuleBorrowed) -> u32 { + read_locked_arc(rule, |rule: &KeyframesRule| rule.keyframes.len() as u32) +} + +#[no_mangle] +pub extern "C" fn Servo_KeyframesRule_GetKeyframe(rule: RawServoKeyframesRuleBorrowed, index: u32) + -> RawServoKeyframeStrong { + read_locked_arc(rule, |rule: &KeyframesRule| { + rule.keyframes[index as usize].clone().into_strong() + }) +} + +#[no_mangle] +pub extern "C" fn Servo_KeyframesRule_FindRule(rule: RawServoKeyframesRuleBorrowed, + key: *const nsACString) -> u32 { + let key = unsafe { key.as_ref().unwrap().as_str_unchecked() }; + let global_style_data = &*GLOBAL_STYLE_DATA; + let guard = global_style_data.shared_lock.read(); + Locked::::as_arc(&rule).read_with(&guard) + .find_rule(&guard, key).map(|index| index as u32) + .unwrap_or(u32::max_value()) +} + +#[no_mangle] +pub extern "C" fn Servo_KeyframesRule_AppendRule(rule: RawServoKeyframesRuleBorrowed, + sheet: RawServoStyleSheetBorrowed, + css: *const nsACString) -> bool { + let css = unsafe { css.as_ref().unwrap().as_str_unchecked() }; + let sheet = Stylesheet::as_arc(&sheet); + if let Ok(keyframe) = Keyframe::parse(css, sheet) { + write_locked_arc(rule, |rule: &mut KeyframesRule| { + rule.keyframes.push(keyframe); + }); + true + } else { + false + } +} + +#[no_mangle] +pub extern "C" fn Servo_KeyframesRule_DeleteRule(rule: RawServoKeyframesRuleBorrowed, index: u32) { + write_locked_arc(rule, |rule: &mut KeyframesRule| { + rule.keyframes.remove(index as usize); + }) +} + #[no_mangle] pub extern "C" fn Servo_MediaRule_GetMedia(rule: RawServoMediaRuleBorrowed) -> RawServoMediaListStrong { read_locked_arc(rule, |rule: &MediaRule| { diff --git a/tests/unit/style/stylesheets.rs b/tests/unit/style/stylesheets.rs index 44c02f6c1f29..7ca36677429e 100644 --- a/tests/unit/style/stylesheets.rs +++ b/tests/unit/style/stylesheets.rs @@ -246,6 +246,10 @@ fn test_parse_stylesheet() { })), ], vendor_prefix: None, + source_location: SourceLocation { + line: 16, + column: 19, + }, }))) ], &stylesheet.shared_lock),