diff --git a/components/script/dom/cssstyledeclaration.rs b/components/script/dom/cssstyledeclaration.rs index a2eb7e152d6..062ecdc0c81 100644 --- a/components/script/dom/cssstyledeclaration.rs +++ b/components/script/dom/cssstyledeclaration.rs @@ -3,16 +3,20 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use dom::bindings::codegen::Bindings::CSSStyleDeclarationBinding::CSSStyleDeclarationMethods; -use dom::bindings::codegen::InheritTypes::ElementCast; +use dom::bindings::codegen::InheritTypes::{NodeCast, ElementCast}; use dom::bindings::error::{ErrorResult, Fallible}; use dom::bindings::js::{JS, JSRef, OptionalRootedRootable}; use dom::bindings::utils::{Reflectable, Reflector}; +use dom::document::DocumentHelpers; use dom::element::{Element, ElementHelpers}; use dom::htmlelement::HTMLElement; +use dom::node::{document_from_node, NodeDamage, Node}; use servo_util::str::DOMString; -use string_cache::atom::Atom; -use style::longhands_from_shorthand; +use string_cache::Atom; +use style::{is_supported_property, longhands_from_shorthand, parse_style_attribute}; use style::PropertyDeclaration; +use url::Url; + use std::ascii::AsciiExt; #[dom_struct] @@ -31,7 +35,7 @@ fn serialize_list(list: &Vec) -> DOMString { } fn serialize_value(declaration: &PropertyDeclaration) -> DOMString { - declaration.value().unwrap() + declaration.value() } impl CSSStyleDeclaration { @@ -51,15 +55,7 @@ impl<'a> PrivateCSSStyleDeclarationHelpers for JSRef<'a, CSSStyleDeclaration> { fn get_declaration(self, property: &Atom) -> Option { self.owner.root().and_then(|owner| { let element: JSRef = ElementCast::from_ref(*owner); - let inline_declarations = element.style_attribute().borrow(); - inline_declarations.as_ref().and_then(|declarations| { - for declaration in declarations.normal.iter().chain(declarations.important.iter()) { - if declaration.matches(property.as_slice()) { - return Some(declaration.clone()); - } - } - None - }) + element.get_inline_style_declaration(property).map(|decl| decl.clone()) }) } } @@ -131,7 +127,60 @@ impl<'a> CSSStyleDeclarationMethods for JSRef<'a, CSSStyleDeclaration> { Ok(()) } - fn SetPropertyValue(self, _property: DOMString, _value: DOMString) -> ErrorResult { + fn SetPropertyValue(self, property: DOMString, value: DOMString) -> ErrorResult { + // 1. If the readonly flag is set, throw a NoModificationAllowedError exception + // and terminate these steps. + //TODO + + // 2. Let property be property converted to ASCII lowercase. + let property = Atom::from_slice(property.as_slice().to_ascii_lower().as_slice()); + + // 3. If property is not a case-sensitive match for a supported CSS property, + // terminate this algorithm. + if !is_supported_property(property.as_slice()) { + return Ok(()); + } + + // 4. If value is the empty string, invoke removeProperty() with property as argument + // and terminate this algorithm. + if value.is_empty() { + //TODO: self.RemoveProperty(property) + return Ok(()); + } + + // 5. Let component value list be the result of parsing value for property property. + let mut synthesized_declaration = property.as_slice().to_string(); + synthesized_declaration.push_str(": "); + synthesized_declaration.push_str(value.as_slice()); + //XXXjdm need page url + let decl_block = parse_style_attribute(synthesized_declaration.as_slice(), + &Url::parse("http://localhost").unwrap()); + + // 6. If component value list is null terminate these steps. + if decl_block.normal.len() == 0 { + return Ok(()); + } + + let owner = self.owner.root(); + let element: JSRef = ElementCast::from_ref(**owner.as_ref().unwrap()); + + assert!(decl_block.important.len() == 0); + for decl in decl_block.normal.iter() { + // 7. If property is a shorthand property, then for each longhand property + // longhand that property maps to, in canonical order, set the CSS + // declaration value longhand to the appropriate value(s) from component + // value list, and with the list of declarations being the declarations. + + // 8. Otherwise, set the CSS declaration value property to the + // value component value list, and with the list of declarations + // being the declarations. + + element.update_inline_style(decl.clone()); + } + + let document = document_from_node(element).root(); + let node: JSRef = NodeCast::from_ref(element); + document.content_changed(node, NodeDamage::NodeStyleDamaged); Ok(()) } diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index ec97ee451e5..385585b6f95 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -60,6 +60,7 @@ use std::ascii::AsciiExt; use std::cell::{Ref, RefMut}; use std::default::Default; use std::mem; +use std::sync::Arc; use string_cache::{Atom, Namespace, QualName}; use url::UrlParser; @@ -465,6 +466,8 @@ pub trait ElementHelpers<'a> { fn style_attribute(self) -> &'a DOMRefCell>; fn summarize(self) -> Vec; fn is_void(self) -> bool; + fn update_inline_style(self, property_decl: style::PropertyDeclaration); + fn get_inline_style_declaration(self, property: &Atom) -> Option; } impl<'a> ElementHelpers<'a> for JSRef<'a, Element> { @@ -522,6 +525,40 @@ impl<'a> ElementHelpers<'a> for JSRef<'a, Element> { _ => false } } + + fn update_inline_style(self, property_decl: style::PropertyDeclaration) { + let mut inline_declarations = self.style_attribute.borrow_mut(); + let exists = inline_declarations.is_some(); + if exists { + let declarations = inline_declarations.as_mut().unwrap(); + for declaration in declarations.normal.make_unique() + .iter_mut() + .chain(declarations.important.make_unique().iter_mut()) { + if declaration.name().as_slice() == property_decl.name().as_slice() { + *declaration = property_decl; + return; + } + } + declarations.normal.make_unique().push(property_decl); + } else { + *inline_declarations = Some(style::PropertyDeclarationBlock { + important: Arc::new(vec!()), + normal: Arc::new(vec!(property_decl)), + }); + } + } + + fn get_inline_style_declaration(self, property: &Atom) -> Option { + let mut inline_declarations = self.style_attribute.borrow(); + inline_declarations.as_ref().and_then(|declarations| { + for declaration in declarations.normal.iter().chain(declarations.important.iter()) { + if declaration.matches(property.as_slice()) { + return Some(declaration.clone()); + } + } + None + }) + } } pub trait AttributeHandlers { diff --git a/components/style/lib.rs b/components/style/lib.rs index f3cf3c843e2..fec6c2d5b61 100644 --- a/components/style/lib.rs +++ b/components/style/lib.rs @@ -43,6 +43,7 @@ pub use selector_matching::{matches, matches_simple_selector, common_style_affec pub use selector_matching::{rare_style_affecting_attributes}; pub use selector_matching::{RECOMMENDED_SELECTOR_BLOOM_FILTER_SIZE, SELECTOR_WHITESPACE}; pub use properties::{cascade, cascade_anonymous, computed, longhands_from_shorthand}; +pub use properties::is_supported_property; pub use properties::{PropertyDeclaration, ComputedValues, computed_values, style_structs}; pub use properties::{PropertyDeclarationBlock, parse_style_attribute}; // Style attributes pub use properties::{CSSFloat, DeclaredValue, PropertyDeclarationParseResult}; diff --git a/components/style/properties/common_types.rs b/components/style/properties/common_types.rs index 52309ad41b6..245654bd118 100644 --- a/components/style/properties/common_types.rs +++ b/components/style/properties/common_types.rs @@ -650,6 +650,15 @@ pub mod computed { Percentage(CSSFloat), Auto, } + impl fmt::Show for LengthOrPercentageOrAuto { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + &LengthOrPercentageOrAuto::Length(length) => write!(f, "{}", length), + &LengthOrPercentageOrAuto::Percentage(percentage) => write!(f, "{}%", percentage), + &LengthOrPercentageOrAuto::Auto => write!(f, "auto"), + } + } + } #[allow(non_snake_case)] pub fn compute_LengthOrPercentageOrAuto(value: specified::LengthOrPercentageOrAuto, context: &Context) -> LengthOrPercentageOrAuto { @@ -669,6 +678,15 @@ pub mod computed { Percentage(CSSFloat), None, } + impl fmt::Show for LengthOrPercentageOrNone { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + &LengthOrPercentageOrNone::Length(length) => write!(f, "{}", length), + &LengthOrPercentageOrNone::Percentage(percentage) => write!(f, "{}%", percentage), + &LengthOrPercentageOrNone::None => write!(f, "none"), + } + } + } #[allow(non_snake_case)] pub fn compute_LengthOrPercentageOrNone(value: specified::LengthOrPercentageOrNone, context: &Context) -> LengthOrPercentageOrNone { @@ -689,6 +707,15 @@ pub mod computed { LinearGradient(LinearGradient), } + impl fmt::Show for Image { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + &Image::Url(ref url) => write!(f, "url({})", url), + &Image::LinearGradient(ref grad) => write!(f, "linear-gradient({})", grad), + } + } + } + /// Computed values for a CSS linear gradient. #[deriving(Clone, PartialEq)] pub struct LinearGradient { @@ -699,6 +726,16 @@ pub mod computed { pub stops: Vec, } + impl fmt::Show for LinearGradient { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let _ = write!(f, "{}", self.angle_or_corner); + for stop in self.stops.iter() { + let _ = write!(f, ", {}", stop); + } + Ok(()) + } + } + /// Computed values for one color stop in a linear gradient. #[deriving(Clone, PartialEq)] pub struct ColorStop { @@ -710,6 +747,16 @@ pub mod computed { pub position: Option, } + impl fmt::Show for ColorStop { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let _ = write!(f, "{}", self.color); + self.position.map(|pos| { + let _ = write!(f, " {}", pos); + }); + Ok(()) + } + } + impl LinearGradient { pub fn compute(value: specified::LinearGradient, context: &Context) -> LinearGradient { let specified::LinearGradient { diff --git a/components/style/properties/mod.rs.mako b/components/style/properties/mod.rs.mako index 9bb5b8a49fc..003800069e6 100644 --- a/components/style/properties/mod.rs.mako +++ b/components/style/properties/mod.rs.mako @@ -593,12 +593,22 @@ pub mod longhands { } pub mod computed_value { use super::super::{Au, CSSFloat}; + use std::fmt; #[deriving(PartialEq, Clone)] pub enum T { Normal, Length(Au), Number(CSSFloat), } + impl fmt::Show for T { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + &T::Normal => write!(f, "normal"), + &T::Length(length) => write!(f, "{}%", length), + &T::Number(number) => write!(f, "{}", number), + } + } + } } #[inline] pub fn get_initial_value() -> computed_value::T { T::Normal } @@ -656,6 +666,7 @@ pub mod longhands { } pub mod computed_value { use super::super::{Au, CSSFloat}; + use std::fmt; #[allow(non_camel_case_types)] #[deriving(PartialEq, Clone)] pub enum T { @@ -665,6 +676,17 @@ pub mod longhands { Length(Au), Percentage(CSSFloat), } + impl fmt::Show for T { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + % for keyword in vertical_align_keywords: + &T::${to_rust_ident(keyword)} => write!(f, "${keyword}"), + % endfor + &T::Length(length) => write!(f, "{}", length), + &T::Percentage(number) => write!(f, "{}%", number), + } + } + } } #[inline] pub fn get_initial_value() -> computed_value::T { T::baseline } @@ -1143,12 +1165,22 @@ pub mod longhands { } } pub mod computed_value { + use std::fmt; #[deriving(PartialEq, Clone)] pub enum T { % for weight in range(100, 901, 100): Weight${weight}, % endfor } + impl fmt::Show for T { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + % for weight in range(100, 901, 100): + &T::Weight${weight} => write!(f, "{}", ${weight}i), + % endfor + } + } + } impl T { pub fn is_bold(self) -> bool { match self { @@ -1672,6 +1704,7 @@ pub mod longhands { pub mod computed_value { use super::super::Au; use super::super::super::computed; + use std::fmt; pub type T = Vec; @@ -1684,6 +1717,17 @@ pub mod longhands { pub color: computed::CSSColor, pub inset: bool, } + + impl fmt::Show for BoxShadow { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if self.inset { + let _ = write!(f, "inset "); + } + let _ = write!(f, "{} {} {} {} {}", self.offset_x, self.offset_y, + self.blur_radius, self.spread_radius, self.color); + Ok(()) + } + } } #[inline] @@ -2456,7 +2500,8 @@ impl DeclaredValue { pub fn specified_value(&self) -> Option { match self { &DeclaredValue::SpecifiedValue(ref inner) => Some(format!("{}", inner)), - _ => None, + &DeclaredValue::Initial => None, + &DeclaredValue::Inherit => Some("inherit".to_string()), } } } @@ -2488,14 +2533,16 @@ impl PropertyDeclaration { } } - pub fn value(&self) -> Option { + pub fn value(&self) -> String { match self { % for property in LONGHANDS: % if property.derived_from is None: - &PropertyDeclaration::${property.camel_case}Declaration(ref value) => value.specified_value(), + &PropertyDeclaration::${property.camel_case}Declaration(ref value) => + value.specified_value() + .unwrap_or_else(|| format!("{}", longhands::${property.ident}::get_initial_value())), % endif % endfor - _ => None, + decl => panic!("unsupported property declaration: {}", decl.name()), } } @@ -3144,6 +3191,18 @@ pub fn cascade_anonymous(parent_style: &ComputedValues) -> ComputedValues { result } +pub fn is_supported_property(property: &str) -> bool { + match property { + % for property in SHORTHANDS: + "${property.name}" => true, + % endfor + % for property in LONGHANDS: + "${property.name}" => true, + % endfor + _ => false, + } +} + pub fn longhands_from_shorthand(shorthand: &str) -> Option> { match shorthand { % for property in SHORTHANDS: diff --git a/tests/html/test_style.html b/tests/html/test_style.html index 362dae2e0dc..a9226ade0d5 100644 --- a/tests/html/test_style.html +++ b/tests/html/test_style.html @@ -1,6 +1,11 @@ -
+
test text!