diff options
Diffstat (limited to 'components/script/dom/cssstyledeclaration.rs')
-rw-r--r-- | components/script/dom/cssstyledeclaration.rs | 370 |
1 files changed, 213 insertions, 157 deletions
diff --git a/components/script/dom/cssstyledeclaration.rs b/components/script/dom/cssstyledeclaration.rs index c6280597b17..7cd63aa12b0 100644 --- a/components/script/dom/cssstyledeclaration.rs +++ b/components/script/dom/cssstyledeclaration.rs @@ -3,6 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use dom::bindings::codegen::Bindings::CSSStyleDeclarationBinding::{self, CSSStyleDeclarationMethods}; +use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use dom::bindings::error::{Error, ErrorResult, Fallible}; use dom::bindings::inheritance::Castable; use dom::bindings::js::{JS, Root}; @@ -12,12 +13,11 @@ use dom::element::Element; use dom::node::{Node, NodeDamage, window_from_node}; use dom::window::Window; use parking_lot::RwLock; -use servo_atoms::Atom; use std::ascii::AsciiExt; use std::sync::Arc; use style::parser::ParserContextExtraData; -use style::properties::{Shorthand, Importance, PropertyDeclarationBlock}; -use style::properties::{is_supported_property, parse_one_declaration, parse_style_attribute}; +use style::properties::{Importance, PropertyDeclarationBlock, PropertyId, LonghandId, ShorthandId}; +use style::properties::{parse_one_declaration, parse_style_attribute}; use style::selector_parser::PseudoElement; use style_traits::ToCss; @@ -25,11 +25,59 @@ use style_traits::ToCss; #[dom_struct] pub struct CSSStyleDeclaration { reflector_: Reflector, - owner: JS<Element>, + owner: CSSStyleOwner, readonly: bool, pseudo: Option<PseudoElement>, } +#[derive(HeapSizeOf, JSTraceable)] +#[must_root] +pub enum CSSStyleOwner { + Element(JS<Element>), + CSSStyleRule(JS<Window>, + #[ignore_heap_size_of = "Arc"] + Arc<RwLock<PropertyDeclarationBlock>>), +} + +impl CSSStyleOwner { + fn style_attribute(&self) -> Option<Arc<RwLock<PropertyDeclarationBlock>>> { + match *self { + CSSStyleOwner::Element(ref el) => { + if let Some(ref pdb) = *el.style_attribute().borrow() { + Some(pdb.clone()) + } else { + None + } + } + CSSStyleOwner::CSSStyleRule(_, ref pdb) => { + Some(pdb.clone()) + } + } + } + + fn window(&self) -> Root<Window> { + match *self { + CSSStyleOwner::Element(ref el) => window_from_node(&**el), + CSSStyleOwner::CSSStyleRule(ref window, _) => Root::from_ref(&**window), + } + } + + fn flush_style(&self, pdb: &PropertyDeclarationBlock) { + if let CSSStyleOwner::Element(ref el) = *self { + el.set_style_attr(pdb.to_css_string()); + } + } + + fn dirty(&self) { + match *self { + CSSStyleOwner::Element(ref el) => + el.upcast::<Node>().dirty(NodeDamage::NodeStyleDamaged), + CSSStyleOwner::CSSStyleRule(ref window, _) => + window.Document().invalidate_stylesheets(), + } + } +} + #[derive(PartialEq, HeapSizeOf)] pub enum CSSModificationAccess { ReadWrite, @@ -37,33 +85,35 @@ pub enum CSSModificationAccess { } macro_rules! css_properties( - ( $([$getter:ident, $setter:ident, $cssprop:expr]),* ) => ( + ( $([$getter:ident, $setter:ident, $id:expr],)* ) => ( $( fn $getter(&self) -> DOMString { - self.GetPropertyValue(DOMString::from($cssprop)) + self.get_property_value($id) } fn $setter(&self, value: DOMString) -> ErrorResult { - self.SetPropertyValue(DOMString::from($cssprop), value) + self.set_property($id, value, DOMString::new()) } )* ); ); impl CSSStyleDeclaration { - pub fn new_inherited(owner: &Element, + #[allow(unrooted_must_root)] + pub fn new_inherited(owner: CSSStyleOwner, pseudo: Option<PseudoElement>, modification_access: CSSModificationAccess) -> CSSStyleDeclaration { CSSStyleDeclaration { reflector_: Reflector::new(), - owner: JS::from_ref(owner), + owner: owner, readonly: modification_access == CSSModificationAccess::Readonly, pseudo: pseudo, } } + #[allow(unrooted_must_root)] pub fn new(global: &Window, - owner: &Element, + owner: CSSStyleOwner, pseudo: Option<PseudoElement>, modification_access: CSSModificationAccess) -> Root<CSSStyleDeclaration> { @@ -74,108 +124,59 @@ impl CSSStyleDeclaration { CSSStyleDeclarationBinding::Wrap) } - fn get_computed_style(&self, property: &Atom) -> Option<DOMString> { - let node = self.owner.upcast::<Node>(); - if !node.is_in_doc() { - // TODO: Node should be matched against the style rules of this window. - // Firefox is currently the only browser to implement this. - return None; + fn get_computed_style(&self, property: PropertyId) -> DOMString { + match self.owner { + CSSStyleOwner::CSSStyleRule(..) => + panic!("get_computed_style called on CSSStyleDeclaration with a CSSStyleRule owner"), + CSSStyleOwner::Element(ref el) => { + let node = el.upcast::<Node>(); + if !node.is_in_doc() { + // TODO: Node should be matched against the style rules of this window. + // Firefox is currently the only browser to implement this. + return DOMString::new(); + } + let addr = node.to_trusted_node_address(); + window_from_node(node).resolved_style_query(addr, self.pseudo.clone(), property) + } } - let addr = node.to_trusted_node_address(); - window_from_node(&*self.owner).resolved_style_query(addr, self.pseudo.clone(), property) - } -} - -impl CSSStyleDeclarationMethods for CSSStyleDeclaration { - // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-length - fn Length(&self) -> u32 { - let elem = self.owner.upcast::<Element>(); - let len = match *elem.style_attribute().borrow() { - Some(ref lock) => lock.read().declarations.len(), - None => 0, - }; - len as u32 - } - - // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-item - fn Item(&self, index: u32) -> DOMString { - self.IndexedGetter(index).unwrap_or_default() } - // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertyvalue - fn GetPropertyValue(&self, mut property: DOMString) -> DOMString { + fn get_property_value(&self, id: PropertyId) -> DOMString { if self.readonly { // Readonly style declarations are used for getComputedStyle. - property.make_ascii_lowercase(); - let property = Atom::from(property); - return self.get_computed_style(&property).unwrap_or(DOMString::new()); + return self.get_computed_style(id); } - let style_attribute = self.owner.style_attribute().borrow(); - let style_attribute = if let Some(ref lock) = *style_attribute { - lock.read() + if let Some(ref lock) = self.owner.style_attribute() { + let mut string = String::new(); + lock.read().property_value_to_css(&id, &mut string).unwrap(); + DOMString::from(string) } else { // No style attribute is like an empty style attribute: no matching declaration. - return DOMString::new() - }; - - let mut string = String::new(); - style_attribute.property_value_to_css(&property, &mut string).unwrap(); - DOMString::from(string) - } - - // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertypriority - fn GetPropertyPriority(&self, property: DOMString) -> DOMString { - let style_attribute = self.owner.style_attribute().borrow(); - let style_attribute = if let Some(ref lock) = *style_attribute { - lock.read() - } else { - // No style attribute is like an empty style attribute: no matching declaration. - return DOMString::new() - }; - - if style_attribute.property_priority(&property).important() { - DOMString::from("important") - } else { - // Step 4 DOMString::new() } } - // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-setproperty - fn SetProperty(&self, - property: DOMString, - value: DOMString, - priority: DOMString) - -> ErrorResult { + fn set_property(&self, id: PropertyId, value: DOMString, priority: DOMString) -> ErrorResult { // Step 1 if self.readonly { return Err(Error::NoModificationAllowed); } - // Step 3 - if !is_supported_property(&property) { - return Ok(()); - } - - let mut style_attribute = self.owner.style_attribute().borrow_mut(); - if value.is_empty() { // Step 4 - let empty; - { - let mut style_attribute = if let Some(ref lock) = *style_attribute { - lock.write() + let empty = { + if let Some(ref lock) = self.owner.style_attribute() { + let mut style_attribute = lock.write(); + style_attribute.remove_property(&id); + style_attribute.declarations.is_empty() } else { // No style attribute is like an empty style attribute: nothing to remove. return Ok(()) - }; - - style_attribute.remove_property(&property); - empty = style_attribute.declarations.is_empty() - } - if empty { - *style_attribute = None; + } + }; + if let (&CSSStyleOwner::Element(ref el), true) = (&self.owner, empty) { + *el.style_attribute().borrow_mut() = None; } } else { // Step 5 @@ -186,29 +187,28 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration { }; // Step 6 - let window = window_from_node(&*self.owner); + let window = self.owner.window(); let declarations = - parse_one_declaration(&property, &value, &window.get_url(), window.css_error_reporter(), + parse_one_declaration(id, &value, &window.get_url(), window.css_error_reporter(), ParserContextExtraData::default()); // Step 7 - let declarations = if let Ok(declarations) = declarations { - declarations - } else { - return Ok(()); + let declarations = match declarations { + Ok(declarations) => declarations, + Err(_) => return Ok(()) }; // Step 8 // Step 9 - match *style_attribute { + match self.owner.style_attribute() { Some(ref lock) => { let mut style_attribute = lock.write(); for declaration in declarations { style_attribute.set_parsed_declaration(declaration, importance); } - self.owner.set_style_attr(style_attribute.to_css_string()); + self.owner.flush_style(&style_attribute); } - ref mut option @ None => { + None => { let important_count = if importance.important() { declarations.len() as u32 } else { @@ -218,16 +218,80 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration { declarations: declarations.into_iter().map(|d| (d, importance)).collect(), important_count: important_count, }; - self.owner.set_style_attr(block.to_css_string()); - *option = Some(Arc::new(RwLock::new(block))); + if let CSSStyleOwner::Element(ref el) = self.owner { + el.set_style_attr(block.to_css_string()); + *el.style_attribute().borrow_mut() = Some(Arc::new(RwLock::new(block))); + } else { + panic!("set_property called on a CSSStyleDeclaration with a non-Element owner"); + } } } } - let node = self.owner.upcast::<Node>(); - node.dirty(NodeDamage::NodeStyleDamaged); + self.owner.dirty(); Ok(()) } +} + +impl CSSStyleDeclarationMethods for CSSStyleDeclaration { + // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-length + fn Length(&self) -> u32 { + self.owner.style_attribute().as_ref().map_or(0, |lock| lock.read().declarations.len() as u32) + } + + // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-item + fn Item(&self, index: u32) -> DOMString { + self.IndexedGetter(index).unwrap_or_default() + } + + // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertyvalue + fn GetPropertyValue(&self, property: DOMString) -> DOMString { + let id = if let Ok(id) = PropertyId::parse(property.into()) { + id + } else { + // Unkwown property + return DOMString::new() + }; + self.get_property_value(id) + } + + // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertypriority + fn GetPropertyPriority(&self, property: DOMString) -> DOMString { + let id = if let Ok(id) = PropertyId::parse(property.into()) { + id + } else { + // Unkwown property + return DOMString::new() + }; + + if let Some(ref lock) = self.owner.style_attribute() { + if lock.read().property_priority(&id).important() { + DOMString::from("important") + } else { + // Step 4 + DOMString::new() + } + } else { + // No style attribute is like an empty style attribute: no matching declaration. + DOMString::new() + } + } + + // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-setproperty + fn SetProperty(&self, + property: DOMString, + value: DOMString, + priority: DOMString) + -> ErrorResult { + // Step 3 + let id = if let Ok(id) = PropertyId::parse(property.into()) { + id + } else { + // Unknown property + return Ok(()) + }; + self.set_property(id, value, priority) + } // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-setpropertypriority fn SetPropertyPriority(&self, property: DOMString, priority: DOMString) -> ErrorResult { @@ -237,9 +301,12 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration { } // Step 2 & 3 - if !is_supported_property(&property) { - return Ok(()); - } + let id = if let Ok(id) = PropertyId::parse(property.into()) { + id + } else { + // Unkwown property + return Ok(()) + }; // Step 4 let importance = match &*priority { @@ -248,19 +315,14 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration { _ => return Ok(()), }; - let style_attribute = self.owner.style_attribute().borrow(); - if let Some(ref lock) = *style_attribute { + if let Some(ref lock) = self.owner.style_attribute() { let mut style_attribute = lock.write(); // Step 5 & 6 - match Shorthand::from_name(&property) { - Some(shorthand) => style_attribute.set_importance(shorthand.longhands(), importance), - None => style_attribute.set_importance(&[&*property], importance), - } + style_attribute.set_importance(&id, importance); - self.owner.set_style_attr(style_attribute.to_css_string()); - let node = self.owner.upcast::<Node>(); - node.dirty(NodeDamage::NodeStyleDamaged); + self.owner.flush_style(&style_attribute); + self.owner.dirty(); } Ok(()) } @@ -277,31 +339,33 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration { return Err(Error::NoModificationAllowed); } - let mut style_attribute = self.owner.style_attribute().borrow_mut(); + let id = if let Ok(id) = PropertyId::parse(property.into()) { + id + } else { + // Unkwown property, cannot be there to remove. + return Ok(DOMString::new()) + }; + let mut string = String::new(); - let empty; - { - let mut style_attribute = if let Some(ref lock) = *style_attribute { - lock.write() + let empty = { + if let Some(ref lock) = self.owner.style_attribute() { + let mut style_attribute = lock.write(); + // Step 3 + style_attribute.property_value_to_css(&id, &mut string).unwrap(); + + // Step 4 & 5 + style_attribute.remove_property(&id); + self.owner.flush_style(&style_attribute); + style_attribute.declarations.is_empty() } else { // No style attribute is like an empty style attribute: nothing to remove. return Ok(DOMString::new()) - }; - - // Step 3 - style_attribute.property_value_to_css(&property, &mut string).unwrap(); - - // Step 4 & 5 - style_attribute.remove_property(&property); - self.owner.set_style_attr(style_attribute.to_css_string()); - empty = style_attribute.declarations.is_empty() - } - if empty { - *style_attribute = None; + } + }; + if let (&CSSStyleOwner::Element(ref el), true) = (&self.owner, empty) { + *el.style_attribute().borrow_mut() = None; } - - let node = self.owner.upcast::<Node>(); - node.dirty(NodeDamage::NodeStyleDamaged); + self.owner.dirty(); // Step 6 Ok(DOMString::from(string)) @@ -319,11 +383,8 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration { // https://dev.w3.org/csswg/cssom/#the-cssstyledeclaration-interface fn IndexedGetter(&self, index: u32) -> Option<DOMString> { - let index = index as usize; - let elem = self.owner.upcast::<Element>(); - let style_attribute = elem.style_attribute().borrow(); - style_attribute.as_ref().and_then(|lock| { - lock.read().declarations.get(index).map(|entry| { + self.owner.style_attribute().as_ref().and_then(|lock| { + lock.read().declarations.get(index as usize).map(|entry| { let (ref declaration, importance) = *entry; let mut css = declaration.to_css_string(); if importance.important() { @@ -336,19 +397,13 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration { // https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-csstext fn CssText(&self) -> DOMString { - let elem = self.owner.upcast::<Element>(); - let style_attribute = elem.style_attribute().borrow(); - - if let Some(lock) = style_attribute.as_ref() { - DOMString::from(lock.read().to_css_string()) - } else { - DOMString::new() - } + self.owner.style_attribute().as_ref().map_or(DOMString::new(), |lock| + DOMString::from(lock.read().to_css_string())) } // https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-csstext fn SetCssText(&self, value: DOMString) -> ErrorResult { - let window = window_from_node(self.owner.upcast::<Node>()); + let window = self.owner.window(); // Step 1 if self.readonly { @@ -358,15 +413,16 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration { // Step 3 let decl_block = parse_style_attribute(&value, &window.get_url(), window.css_error_reporter(), ParserContextExtraData::default()); - *self.owner.style_attribute().borrow_mut() = if decl_block.declarations.is_empty() { - self.owner.set_style_attr(String::new()); - None // Step 2 - } else { - self.owner.set_style_attr(decl_block.to_css_string()); - Some(Arc::new(RwLock::new(decl_block))) - }; - let node = self.owner.upcast::<Node>(); - node.dirty(NodeDamage::NodeStyleDamaged); + if let CSSStyleOwner::Element(ref el) = self.owner { + *el.style_attribute().borrow_mut() = if decl_block.declarations.is_empty() { + el.set_style_attr(String::new()); + None // Step 2 + } else { + el.set_style_attr(decl_block.to_css_string()); + Some(Arc::new(RwLock::new(decl_block))) + }; + } + self.owner.dirty(); Ok(()) } |