From 64442090baaeb47d01fb5c58886b4604eb6d9808 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Thu, 17 Nov 2016 15:23:05 -0800 Subject: [PATCH] Add CSSKeyframesRule.{findRule, deleteRule, appendRule} --- components/script/dom/cssgroupingrule.rs | 3 +- components/script/dom/csskeyframerule.rs | 4 +- components/script/dom/csskeyframesrule.rs | 49 ++++++++++- components/script/dom/cssrulelist.rs | 85 +++++++++++++------ .../dom/webidls/CSSKeyframesRule.webidl | 6 +- components/style/keyframes.rs | 33 ++++++- components/style/stylesheets.rs | 4 +- 7 files changed, 145 insertions(+), 39 deletions(-) diff --git a/components/script/dom/cssgroupingrule.rs b/components/script/dom/cssgroupingrule.rs index 4c08727b229..cd55cd358b6 100644 --- a/components/script/dom/cssgroupingrule.rs +++ b/components/script/dom/cssgroupingrule.rs @@ -2,14 +2,13 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use dom::bindings::reflector::Reflectable; use dom::bindings::codegen::Bindings::CSSGroupingRuleBinding; use dom::bindings::codegen::Bindings::CSSGroupingRuleBinding::CSSGroupingRuleMethods; use dom::bindings::codegen::Bindings::CSSRuleBinding::CSSRuleBinding::CSSRuleMethods; use dom::bindings::error::{ErrorResult, Fallible}; use dom::bindings::inheritance::Castable; use dom::bindings::js::{JS, MutNullableHeap, Root}; -use dom::bindings::reflector::reflect_dom_object; +use dom::bindings::reflector::{Reflectable, reflect_dom_object}; use dom::bindings::str::DOMString; use dom::cssrule::CSSRule; use dom::cssrulelist::{CSSRuleList, RulesSource}; diff --git a/components/script/dom/csskeyframerule.rs b/components/script/dom/csskeyframerule.rs index 6062c40ca25..e09099e1135 100644 --- a/components/script/dom/csskeyframerule.rs +++ b/components/script/dom/csskeyframerule.rs @@ -12,6 +12,7 @@ use dom::window::Window; use parking_lot::RwLock; use std::sync::Arc; use style::keyframes::Keyframe; +use style_traits::ToCss; #[dom_struct] pub struct CSSKeyframeRule { @@ -44,7 +45,6 @@ impl SpecificCSSRule for CSSKeyframeRule { } fn get_css(&self) -> DOMString { - // self.keyframerule.read().to_css_string().into() - "".into() + self.keyframerule.read().to_css_string().into() } } diff --git a/components/script/dom/csskeyframesrule.rs b/components/script/dom/csskeyframesrule.rs index 444ae8a5424..b7baf6b85a9 100644 --- a/components/script/dom/csskeyframesrule.rs +++ b/components/script/dom/csskeyframesrule.rs @@ -2,20 +2,25 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use cssparser::Parser; use dom::bindings::codegen::Bindings::CSSKeyframesRuleBinding; use dom::bindings::codegen::Bindings::CSSKeyframesRuleBinding::CSSKeyframesRuleMethods; use dom::bindings::codegen::Bindings::CSSRuleBinding::CSSRuleMethods; +use dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods; use dom::bindings::inheritance::Castable; use dom::bindings::js::{JS, MutNullableHeap, Root}; use dom::bindings::reflector::{Reflectable, reflect_dom_object}; use dom::bindings::str::DOMString; +use dom::csskeyframerule::CSSKeyframeRule; use dom::cssrule::{CSSRule, SpecificCSSRule}; use dom::cssrulelist::{CSSRuleList, RulesSource}; use dom::cssstylesheet::CSSStyleSheet; use dom::window::Window; use parking_lot::RwLock; use std::sync::Arc; -use style::stylesheets::KeyframesRule; +use style::keyframes::{Keyframe, KeyframeSelector}; +use style::parser::ParserContextExtraData; +use style::stylesheets::{KeyframesRule, Origin}; use style_traits::ToCss; #[dom_struct] @@ -52,6 +57,21 @@ impl CSSKeyframesRule { RulesSource::Keyframes(self.keyframesrule.clone())) }) } + + /// Given a keyframe selector, finds the index of the first corresponding rule if any + fn find_rule(&self, selector: &str) -> Option { + let mut input = Parser::new(selector); + if let Ok(sel) = KeyframeSelector::parse(&mut input) { + // This finds the *last* element matching a selector + // because that's the rule that applies. Thus, rposition + self.keyframesrule.read() + .keyframes.iter().rposition(|frame| { + frame.read().selector == sel + }) + } else { + None + } + } } impl CSSKeyframesRuleMethods for CSSKeyframesRule { @@ -59,6 +79,33 @@ impl CSSKeyframesRuleMethods for CSSKeyframesRule { fn CssRules(&self) -> Root { self.rulelist() } + + // https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-appendrule + fn AppendRule(&self, rule: DOMString) { + let global = self.global(); + let window = global.as_window(); + let doc = window.Document(); + let rule = Keyframe::parse(&rule, Origin::Author, doc.url().clone(), + ParserContextExtraData::default()); + if let Ok(rule) = rule { + self.keyframesrule.write().keyframes.push(rule); + self.rulelist().append_lazy_dom_rule(); + } + } + + // https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-deleterule + fn DeleteRule(&self, selector: DOMString) { + if let Some(idx) = self.find_rule(&selector) { + let _ = self.rulelist().remove_rule(idx as u32); + } + } + + // https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-findrule + fn FindRule(&self, selector: DOMString) -> Option> { + self.find_rule(&selector).and_then(|idx| { + self.rulelist().item(idx as u32) + }).and_then(Root::downcast) + } } impl SpecificCSSRule for CSSKeyframesRule { diff --git a/components/script/dom/cssrulelist.rs b/components/script/dom/cssrulelist.rs index 225f04a560d..deb6b93e8b1 100644 --- a/components/script/dom/cssrulelist.rs +++ b/components/script/dom/cssrulelist.rs @@ -64,7 +64,10 @@ impl CSSRuleList { CSSRuleListBinding::Wrap) } - // https://drafts.csswg.org/cssom/#insert-a-css-rule + /// https://drafts.csswg.org/cssom/#insert-a-css-rule + /// + /// Should only be called for CssRules-backed rules. Use append_lazy_rule + /// for keyframes-backed rules. pub fn insert_rule(&self, rule: &str, idx: u32, nested: bool) -> Fallible { use style::stylesheets::SingleRuleParseError; /// Insert an item into a vector, appending if it is out of bounds @@ -143,33 +146,49 @@ impl CSSRuleList { } // https://drafts.csswg.org/cssom/#remove-a-css-rule + // https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-deleterule + // In case of a keyframe rule, index must be valid. pub fn remove_rule(&self, index: u32) -> ErrorResult { let index = index as usize; - let css_rules = if let RulesSource::Rules(ref rules) = self.rules { - rules - } else { - panic!("Called remove_rule on non-CssRule-backed CSSRuleList"); - }; + match self.rules { + RulesSource::Rules(ref css_rules) => { + // https://drafts.csswg.org/cssom/#remove-a-css-rule + { + let rules = css_rules.0.read(); - { - let rules = css_rules.0.read(); - if index >= rules.len() { - return Err(Error::IndexSize); - } - let ref rule = rules[index]; - if let StyleCssRule::Namespace(..) = *rule { - if !CssRules::only_ns_or_import(&rules) { - return Err(Error::InvalidState); + // Step 1, 2 + if index >= rules.len() { + return Err(Error::IndexSize); + } + + // Step 3 + let ref rule = rules[index]; + + // Step 4 + if let StyleCssRule::Namespace(..) = *rule { + if !CssRules::only_ns_or_import(&rules) { + return Err(Error::InvalidState); + } + } } + + // Step 5, 6 + let mut dom_rules = self.dom_rules.borrow_mut(); + css_rules.0.write().remove(index); + dom_rules[index].get().map(|r| r.detach()); + dom_rules.remove(index); + Ok(()) + } + RulesSource::Keyframes(ref kf) => { + // https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-deleterule + let mut dom_rules = self.dom_rules.borrow_mut(); + dom_rules[index].get().map(|r| r.detach()); + dom_rules.remove(index); + kf.write().keyframes.remove(index); + Ok(()) } } - - let mut dom_rules = self.dom_rules.borrow_mut(); - css_rules.0.write().remove(index); - dom_rules[index].get().map(|r| r.detach()); - dom_rules.remove(index); - Ok(()) } // Remove parent stylesheets from all children @@ -178,11 +197,8 @@ impl CSSRuleList { rule.get().map(|r| Root::upcast(r).deparent()); } } -} -impl CSSRuleListMethods for CSSRuleList { - // https://drafts.csswg.org/cssom/#ref-for-dom-cssrulelist-item-1 - fn Item(&self, idx: u32) -> Option> { + pub fn item(&self, idx: u32) -> Option> { self.dom_rules.borrow().get(idx as usize).map(|rule| { rule.or_init(|| { let sheet = self.sheet.get(); @@ -206,6 +222,25 @@ impl CSSRuleListMethods for CSSRuleList { }) } + /// Add a rule to the list of DOM rules. This list is lazy, + /// so we just append a placeholder. + /// + /// Should only be called for keyframes-backed rules, use insert_rule + /// for CssRules-backed rules + pub fn append_lazy_dom_rule(&self) { + if let RulesSource::Rules(..) = self.rules { + panic!("Can only call append_lazy_rule with keyframes-backed CSSRules"); + } + self.dom_rules.borrow_mut().push(MutNullableHeap::new(None)); + } +} + +impl CSSRuleListMethods for CSSRuleList { + // https://drafts.csswg.org/cssom/#ref-for-dom-cssrulelist-item-1 + fn Item(&self, idx: u32) -> Option> { + self.item(idx) + } + // https://drafts.csswg.org/cssom/#dom-cssrulelist-length fn Length(&self) -> u32 { self.dom_rules.borrow().len() as u32 diff --git a/components/script/dom/webidls/CSSKeyframesRule.webidl b/components/script/dom/webidls/CSSKeyframesRule.webidl index 3e7c69f6d08..34d45e1a357 100644 --- a/components/script/dom/webidls/CSSKeyframesRule.webidl +++ b/components/script/dom/webidls/CSSKeyframesRule.webidl @@ -8,7 +8,7 @@ interface CSSKeyframesRule : CSSRule { // attribute DOMString name; readonly attribute CSSRuleList cssRules; - // void appendRule(DOMString rule); - // void deleteRule(DOMString select); - // CSSKeyframeRule? findRule(DOMString select); + void appendRule(DOMString rule); + void deleteRule(DOMString select); + CSSKeyframeRule? findRule(DOMString select); }; diff --git a/components/style/keyframes.rs b/components/style/keyframes.rs index c13cd1b5431..b74dd73a4de 100644 --- a/components/style/keyframes.rs +++ b/components/style/keyframes.rs @@ -3,14 +3,16 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use cssparser::{AtRuleParser, Parser, QualifiedRuleParser, RuleListParser}; -use cssparser::{DeclarationListParser, DeclarationParser}; +use cssparser::{DeclarationListParser, DeclarationParser, parse_one_rule}; use parking_lot::RwLock; -use parser::{ParserContext, log_css_error}; +use parser::{ParserContext, ParserContextExtraData, log_css_error}; use properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock}; use properties::PropertyDeclarationParseResult; use properties::animated_properties::TransitionProperty; use std::fmt; use std::sync::Arc; +use stylesheets::{MemoryHoleReporter, Origin}; +use servo_url::ServoUrl; use style_traits::ToCss; /// A number from 1 to 100, indicating the percentage of the animation where @@ -68,6 +70,11 @@ impl KeyframeSelector { pub fn new_for_unit_testing(percentages: Vec) -> KeyframeSelector { KeyframeSelector(percentages) } + + pub fn parse(input: &mut Parser) -> Result { + input.parse_comma_separated(KeyframePercentage::parse) + .map(KeyframeSelector) + } } /// A keyframe. @@ -99,6 +106,24 @@ impl ToCss for Keyframe { } } + +impl Keyframe { + pub fn parse(css: &str, origin: Origin, + base_url: ServoUrl, + extra_data: ParserContextExtraData) -> Result>, ()> { + let error_reporter = Box::new(MemoryHoleReporter); + let context = ParserContext::new_with_extra_data(origin, &base_url, + error_reporter, + extra_data); + let mut input = Parser::new(css); + + let mut rule_parser = KeyframeListParser { + context: &context, + }; + parse_one_rule(&mut input, &mut rule_parser) + } +} + /// A keyframes step value. This can be a synthetised keyframes animation, that /// is, one autogenerated from the current computed values, or a list of /// declarations to apply. @@ -260,8 +285,8 @@ impl<'a> QualifiedRuleParser for KeyframeListParser<'a> { fn parse_prelude(&mut self, input: &mut Parser) -> Result { let start = input.position(); - match input.parse_comma_separated(|input| KeyframePercentage::parse(input)) { - Ok(percentages) => Ok(KeyframeSelector(percentages)), + match KeyframeSelector::parse(input) { + Ok(sel) => Ok(sel), Err(()) => { let message = format!("Invalid keyframe rule: '{}'", input.slice_from(start)); log_css_error(input, start, &message, self.context); diff --git a/components/style/stylesheets.rs b/components/style/stylesheets.rs index 3d56d92f924..ba5e5a43989 100644 --- a/components/style/stylesheets.rs +++ b/components/style/stylesheets.rs @@ -135,7 +135,7 @@ pub enum CssRule { } /// Error reporter which silently forgets errors -struct MemoryHoleReporter; +pub struct MemoryHoleReporter; impl ParseErrorReporter for MemoryHoleReporter { fn report_error(&self, @@ -180,7 +180,7 @@ impl CssRule { // input state is None for a nested rule // Returns a parsed CSS rule and the final state of the parser pub fn parse(css: &str, origin: Origin, - base_url: Url, + base_url: ServoUrl, extra_data: ParserContextExtraData, state: Option) -> Result<(Self, State), SingleRuleParseError> { let error_reporter = Box::new(MemoryHoleReporter);