diff --git a/components/script/dom/cssrule.rs b/components/script/dom/cssrule.rs index e46738bb1b4..7ed26bce2c7 100644 --- a/components/script/dom/cssrule.rs +++ b/components/script/dom/cssrule.rs @@ -73,6 +73,12 @@ impl CSSRule { StyleCssRule::Viewport(s) => Root::upcast(CSSViewportRule::new(window, parent, s)), } } + + /// Sets owner sheet/rule to null + pub fn disown(&self) { + self.parent.set(None); + // should set parent rule to None when we add parent rule support + } } impl CSSRuleMethods for CSSRule { diff --git a/components/script/dom/cssrulelist.rs b/components/script/dom/cssrulelist.rs index 1c383403a00..f8cf59ad670 100644 --- a/components/script/dom/cssrulelist.rs +++ b/components/script/dom/cssrulelist.rs @@ -2,14 +2,19 @@ * 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::cell::DOMRefCell; use dom::bindings::codegen::Bindings::CSSRuleListBinding; use dom::bindings::codegen::Bindings::CSSRuleListBinding::CSSRuleListMethods; +use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; +use dom::bindings::error::{Error, ErrorResult, Fallible}; use dom::bindings::js::{JS, MutNullableHeap, Root}; use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object}; use dom::cssrule::CSSRule; use dom::cssstylesheet::CSSStyleSheet; use dom::window::Window; -use style::stylesheets::CssRules; +use style::parser::ParserContextExtraData; +use style::stylesheets::{CssRules, Origin}; +use style::stylesheets::CssRule as StyleCssRule; no_jsmanaged_fields!(CssRules); @@ -19,7 +24,7 @@ pub struct CSSRuleList { sheet: JS, #[ignore_heap_size_of = "Arc"] rules: CssRules, - dom_rules: Vec>> + dom_rules: DOMRefCell>>> } impl CSSRuleList { @@ -30,7 +35,7 @@ impl CSSRuleList { reflector_: Reflector::new(), sheet: JS::from_ref(sheet), rules: rules, - dom_rules: dom_rules, + dom_rules: DOMRefCell::new(dom_rules), } } @@ -40,12 +45,83 @@ impl CSSRuleList { window, CSSRuleListBinding::Wrap) } + + // https://drafts.csswg.org/cssom/#insert-a-css-rule + pub fn insert_rule(&self, rule: &str, idx: u32) -> Fallible { + /// Insert an item into a vector, appending if it is out of bounds + fn insert(vec: &mut Vec, index: usize, item: T) { + if index >= vec.len() { + vec.push(item); + } else { + vec.insert(index, item); + } + } + let global = self.global(); + let window = global.as_window(); + let doc = window.Document(); + let index = idx as usize; + + // Step 1, 2 + // XXXManishearth get url from correct location + // XXXManishearth should we also store the namespace map? + let new_rule = try!(StyleCssRule::from_str(&rule, Origin::Author, + doc.url().clone(), + ParserContextExtraData::default()) + .map_err(|_| Error::Syntax)); + + { + let rules = self.rules.0.read(); + // Step 3, 4 + if index > rules.len() { + return Err(Error::IndexSize); + } + + // XXXManishearth Step 5 (throw HierarchyRequestError in invalid situations) + + // Step 6 + if let StyleCssRule::Namespace(..) = new_rule { + if !CssRules::only_ns_or_import(&rules) { + return Err(Error::InvalidState); + } + } + } + + insert(&mut self.rules.0.write(), index, new_rule.clone()); + let dom_rule = CSSRule::new_specific(&window, &self.sheet, new_rule); + insert(&mut self.dom_rules.borrow_mut(), + index, MutNullableHeap::new(Some(&*dom_rule))); + Ok((idx)) + } + + // https://drafts.csswg.org/cssom/#remove-a-css-rule + pub fn remove_rule(&self, index: u32) -> ErrorResult { + let index = index as usize; + + { + let rules = self.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); + } + } + } + + let mut dom_rules = self.dom_rules.borrow_mut(); + self.rules.0.write().remove(index); + dom_rules[index].get().map(|r| r.disown()); + dom_rules.remove(index); + Ok(()) + } } impl CSSRuleListMethods for CSSRuleList { // https://drafts.csswg.org/cssom/#ref-for-dom-cssrulelist-item-1 fn Item(&self, idx: u32) -> Option> { - self.dom_rules.get(idx as usize).map(|rule| { + self.dom_rules.borrow().get(idx as usize).map(|rule| { rule.or_init(|| { CSSRule::new_specific(self.global().as_window(), &self.sheet, @@ -56,7 +132,7 @@ impl CSSRuleListMethods for CSSRuleList { // https://drafts.csswg.org/cssom/#dom-cssrulelist-length fn Length(&self) -> u32 { - self.dom_rules.len() as u32 + self.dom_rules.borrow().len() as u32 } // check-tidy: no specs after this line diff --git a/components/script/dom/cssstylesheet.rs b/components/script/dom/cssstylesheet.rs index d5a9342a75a..079199c2e6e 100644 --- a/components/script/dom/cssstylesheet.rs +++ b/components/script/dom/cssstylesheet.rs @@ -4,6 +4,7 @@ use dom::bindings::codegen::Bindings::CSSStyleSheetBinding; use dom::bindings::codegen::Bindings::CSSStyleSheetBinding::CSSStyleSheetMethods; +use dom::bindings::error::{ErrorResult, Fallible}; use dom::bindings::js::{JS, Root, MutNullableHeap}; use dom::bindings::reflector::{reflect_dom_object, Reflectable}; use dom::bindings::str::DOMString; @@ -40,12 +41,31 @@ impl CSSStyleSheet { window, CSSStyleSheetBinding::Wrap) } + + fn rulelist(&self) -> Root { + self.rulelist.or_init(|| CSSRuleList::new(self.global().as_window(), + self, + self.style_stylesheet.rules.clone())) + } } impl CSSStyleSheetMethods for CSSStyleSheet { // https://drafts.csswg.org/cssom/#dom-cssstylesheet-cssrules fn CssRules(&self) -> Root { - self.rulelist.or_init(|| CSSRuleList::new(self.global().as_window(), self, self.style_stylesheet.rules.clone())) + // XXXManishearth check origin clean flag + self.rulelist() + } + + // https://drafts.csswg.org/cssom/#dom-cssstylesheet-insertrule + fn InsertRule(&self, rule: DOMString, index: u32) -> Fallible { + // XXXManishearth check origin clean flag + self.rulelist().insert_rule(&rule, index) + } + + // https://drafts.csswg.org/cssom/#dom-cssstylesheet-deleterule + fn DeleteRule(&self, index: u32) -> ErrorResult { + // XXXManishearth check origin clean flag + self.rulelist().remove_rule(index) } } diff --git a/components/script/dom/webidls/CSSStyleSheet.webidl b/components/script/dom/webidls/CSSStyleSheet.webidl index 99ab3234705..6fba0cf983a 100644 --- a/components/script/dom/webidls/CSSStyleSheet.webidl +++ b/components/script/dom/webidls/CSSStyleSheet.webidl @@ -7,6 +7,6 @@ interface CSSStyleSheet : StyleSheet { // readonly attribute CSSRule? ownerRule; [SameObject] readonly attribute CSSRuleList cssRules; - // unsigned long insertRule(DOMString rule, unsigned long index); - // void deleteRule(unsigned long index); + [Throws] unsigned long insertRule(DOMString rule, unsigned long index); + [Throws] void deleteRule(unsigned long index); }; diff --git a/components/style/stylesheets.rs b/components/style/stylesheets.rs index 7e8388591a8..648a4f6218d 100644 --- a/components/style/stylesheets.rs +++ b/components/style/stylesheets.rs @@ -6,7 +6,7 @@ use {Atom, Prefix, Namespace}; use cssparser::{AtRuleParser, Parser, QualifiedRuleParser, decode_stylesheet_bytes}; -use cssparser::{AtRuleType, RuleListParser, Token}; +use cssparser::{AtRuleType, RuleListParser, SourcePosition, Token}; use cssparser::ToCss as ParserToCss; use encoding::EncodingRef; use error_reporting::ParseErrorReporter; @@ -60,6 +60,18 @@ impl From> for CssRules { } } +impl CssRules { + // used in CSSOM + pub fn only_ns_or_import(rules: &[CssRule]) -> bool { + rules.iter().all(|r| { + match *r { + CssRule::Namespace(..) /* | CssRule::Import(..) */ => true, + _ => false + } + }) + } +} + #[derive(Debug)] pub struct Stylesheet { /// List of rules in the order they were found (important for @@ -92,6 +104,21 @@ pub enum CssRule { Keyframes(Arc>), } +/// Error reporter which silently forgets errors +struct MemoryHoleReporter; + +impl ParseErrorReporter for MemoryHoleReporter { + fn report_error(&self, + _: &mut Parser, + _: SourcePosition, + _: &str) { + // do nothing + } + fn clone(&self) -> Box { + Box::new(MemoryHoleReporter) + } +} + impl CssRule { /// Call `f` with the slice of rules directly contained inside this rule. /// @@ -114,6 +141,28 @@ impl CssRule { } } } + + pub fn from_str(css: &str, origin: Origin, + base_url: Url, extra_data: ParserContextExtraData) -> Result { + let error_reporter = Box::new(MemoryHoleReporter); + let mut namespaces = Namespaces::default(); + let rule_parser = TopLevelRuleParser { + context: ParserContext::new_with_extra_data(origin, &base_url, error_reporter.clone(), + extra_data), + state: Cell::new(State::Start), + namespaces: &mut namespaces, + }; + let mut input = Parser::new(css); + let mut iter = RuleListParser::new_for_stylesheet(&mut input, rule_parser); + if let Some(Ok(rule)) = iter.next() { + if iter.next().is_some() { + return Err(()); + } + return Ok(rule); + } else { + return Err(()); + } + } } impl ToCss for CssRule { @@ -220,7 +269,6 @@ impl ToCss for StyleRule { } } - impl Stylesheet { pub fn from_bytes(bytes: &[u8], base_url: ServoUrl,