servo: Merge #13640 - Move some of the CSSStyleDeclaration logic to the style crate (from servo:CSSStyleDeclaration_in_style); r=mbrubeck
authorSimon Sapin <simon.sapin@exyr.org>
Wed, 12 Oct 2016 14:21:34 -0500
changeset 339901 69036a52f7c22bd95d219bb2024cf3b32582535c
parent 339900 d8c150f8f5b79ccca41879f99653bc6a017eb1c3
child 339902 442b914ad2ee1234e5886f2e58d3c68bb8b54417
push id31307
push usergszorc@mozilla.com
push dateSat, 04 Feb 2017 00:59:06 +0000
treeherdermozilla-central@94079d43835f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmbrubeck
servo: Merge #13640 - Move some of the CSSStyleDeclaration logic to the style crate (from servo:CSSStyleDeclaration_in_style); r=mbrubeck <!-- Please describe your changes on the following line: --> … so that Stylo can re-use it. Fixes https://bugzilla.mozilla.org/show_bug.cgi?id=1295865 --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [ ] These changes fix #__ (github issue number if applicable). <!-- Either: --> - [ ] There are tests for these changes OR - [x] These changes do not require tests because refator <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> Source-Repo: https://github.com/servo/servo Source-Revision: 626d093245066755c751510d2c09a9b864b129fd
servo/components/script/dom/cssstyledeclaration.rs
servo/components/script/dom/element.rs
servo/components/style/properties/declaration_block.rs
servo/components/style/properties/properties.mako.rs
--- a/servo/components/script/dom/cssstyledeclaration.rs
+++ b/servo/components/script/dom/cssstyledeclaration.rs
@@ -12,17 +12,17 @@ use dom::bindings::str::DOMString;
 use dom::element::Element;
 use dom::node::{Node, NodeDamage, window_from_node};
 use dom::window::Window;
 use parking_lot::RwLock;
 use std::ascii::AsciiExt;
 use std::sync::Arc;
 use string_cache::Atom;
 use style::parser::ParserContextExtraData;
-use style::properties::{Shorthand, Importance};
+use style::properties::{Shorthand, Importance, PropertyDeclarationBlock};
 use style::properties::{is_supported_property, parse_one_declaration, parse_style_attribute};
 use style::selector_impl::PseudoElement;
 
 // http://dev.w3.org/csswg/cssom/#the-cssstyledeclaration-interface
 #[dom_struct]
 pub struct CSSStyleDeclaration {
     reflector_: Reflector,
     owner: JS<Element>,
@@ -86,163 +86,150 @@ impl CSSStyleDeclaration {
     }
 }
 
 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 declarations) => declarations.read().declarations.len(),
+            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 {
-        let owner = &self.owner;
-
-        // Step 1
-        property.make_ascii_lowercase();
-        let property = Atom::from(property);
-
         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());
         }
 
-        // Step 2
-        if let Some(shorthand) = Shorthand::from_name(&property) {
-            let style_attribute = owner.style_attribute().borrow();
-            let style_attribute = if let Some(ref style_attribute) = *style_attribute {
-                style_attribute.read()
-            } else {
-                // shorthand.longhands() is never empty, so with no style attribute
-                // step 2.2.2 would do this:
-                return DOMString::new()
-            };
-
-            // Step 2.1
-            let mut list = vec![];
-
-            // Step 2.2
-            for longhand in shorthand.longhands() {
-                // Step 2.2.1
-                let declaration = style_attribute.get(longhand);
+        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()
+        };
 
-                // Step 2.2.2 & 2.2.3
-                match declaration {
-                    Some(&(ref declaration, _importance)) => list.push(declaration),
-                    None => return DOMString::new(),
-                }
-            }
-
-            // Step 2.3
-            // TODO: important is hardcoded to false because method does not implement it yet
-            let serialized_value = shorthand.serialize_shorthand_value_to_string(
-                list, Importance::Normal);
-            return DOMString::from(serialized_value);
-        }
-
-        // Step 3 & 4
-        owner.get_inline_style_declaration(&property, |d| match d {
-            Some(declaration) => DOMString::from(declaration.0.value()),
-            None => 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, mut property: DOMString) -> DOMString {
-        // Step 1
-        property.make_ascii_lowercase();
-        let property = Atom::from(property);
-
-        // Step 2
-        if let Some(shorthand) = Shorthand::from_name(&property) {
-            // Step 2.1 & 2.2 & 2.3
-            if shorthand.longhands().iter()
-                                    .map(|&longhand| self.GetPropertyPriority(DOMString::from(longhand)))
-                                    .all(|priority| priority == "important") {
-                return DOMString::from("important");
-            }
+    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 {
-            // Step 3
-            return self.owner.get_inline_style_declaration(&property, |d| {
-                if let Some(decl) = d {
-                    if decl.1.important() {
-                        return DOMString::from("important");
-                    }
-                }
+            // No style attribute is like an empty style attribute: no matching declaration.
+            return DOMString::new()
+        };
 
-                // Step 4
-                DOMString::new()
-            })
+        if style_attribute.property_priority(&property).important() {
+            DOMString::from("important")
+        } else {
+            // Step 4
+            DOMString::new()
         }
-
-        // Step 4
-        DOMString::new()
     }
 
     // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-setproperty
     fn SetProperty(&self,
-                   mut property: DOMString,
+                   property: DOMString,
                    value: DOMString,
                    priority: DOMString)
                    -> ErrorResult {
         // Step 1
         if self.readonly {
             return Err(Error::NoModificationAllowed);
         }
 
-        // Step 2
-        property.make_ascii_lowercase();
-
         // Step 3
         if !is_supported_property(&property) {
             return Ok(());
         }
 
-        // Step 4
+        let mut style_attribute = self.owner.style_attribute().borrow_mut();
+
         if value.is_empty() {
-            return self.RemoveProperty(property).map(|_| ());
+            // Step 4
+            let empty;
+            {
+                let mut style_attribute = if let Some(ref lock) = *style_attribute {
+                    lock.write()
+                } 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;
+            }
+        } else {
+            // Step 5
+            let importance = match &*priority {
+                "" => Importance::Normal,
+                p if p.eq_ignore_ascii_case("important") => Importance::Important,
+                _ => return Ok(()),
+            };
+
+            // Step 6
+            let window = window_from_node(&*self.owner);
+            let declarations =
+                parse_one_declaration(&property, &value, &window.get_url(), window.css_error_reporter(),
+                                      ParserContextExtraData::default());
+
+            // Step 7
+            let declarations = if let Ok(declarations) = declarations {
+                declarations
+            } else {
+                return Ok(());
+            };
+
+            // Step 8
+            // Step 9
+            match *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());
+                }
+                ref mut option @ None => {
+                    let important_count = if importance.important() {
+                        declarations.len() as u32
+                    } else {
+                        0
+                    };
+                    let block = PropertyDeclarationBlock {
+                        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)));
+                }
+            }
         }
 
-        // Step 5
-        let priority = match &*priority {
-            "" => Importance::Normal,
-            p if p.eq_ignore_ascii_case("important") => Importance::Important,
-            _ => return Ok(()),
-        };
-
-        // Step 6
-        let window = window_from_node(&*self.owner);
-        let declarations =
-            parse_one_declaration(&property, &value, &window.get_url(), window.css_error_reporter(),
-                                  ParserContextExtraData::default());
-
-        // Step 7
-        let declarations = if let Ok(declarations) = declarations {
-            declarations
-        } else {
-            return Ok(());
-        };
-
-        let element = self.owner.upcast::<Element>();
-
-        // Step 8
-        // Step 9
-        element.update_inline_style(declarations, priority);
-
-        let node = element.upcast::<Node>();
+        let node = self.owner.upcast::<Node>();
         node.dirty(NodeDamage::NodeStyleDamaged);
         Ok(())
     }
 
     // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-setpropertypriority
     fn SetPropertyPriority(&self, property: DOMString, priority: DOMString) -> ErrorResult {
         // Step 1
         if self.readonly {
@@ -250,73 +237,79 @@ impl CSSStyleDeclarationMethods for CSSS
         }
 
         // Step 2 & 3
         if !is_supported_property(&property) {
             return Ok(());
         }
 
         // Step 4
-        let priority = match &*priority {
+        let importance = match &*priority {
             "" => Importance::Normal,
             p if p.eq_ignore_ascii_case("important") => Importance::Important,
             _ => return Ok(()),
         };
 
-        let element = self.owner.upcast::<Element>();
+        let style_attribute = self.owner.style_attribute().borrow();
+        if let Some(ref lock) = *style_attribute {
+            let mut style_attribute = lock.write();
 
-        // Step 5 & 6
-        match Shorthand::from_name(&property) {
-            Some(shorthand) => {
-                element.set_inline_style_property_priority(shorthand.longhands(), priority)
+            // Step 5 & 6
+            match Shorthand::from_name(&property) {
+                Some(shorthand) => style_attribute.set_importance(shorthand.longhands(), importance),
+                None => style_attribute.set_importance(&[&*property], importance),
             }
-            None => element.set_inline_style_property_priority(&[&*property], priority),
+
+            self.owner.set_style_attr(style_attribute.to_css_string());
+            let node = self.owner.upcast::<Node>();
+            node.dirty(NodeDamage::NodeStyleDamaged);
         }
-
-        let node = element.upcast::<Node>();
-        node.dirty(NodeDamage::NodeStyleDamaged);
         Ok(())
     }
 
     // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-setpropertyvalue
     fn SetPropertyValue(&self, property: DOMString, value: DOMString) -> ErrorResult {
         self.SetProperty(property, value, DOMString::new())
     }
 
     // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-removeproperty
-    fn RemoveProperty(&self, mut property: DOMString) -> Fallible<DOMString> {
+    fn RemoveProperty(&self, property: DOMString) -> Fallible<DOMString> {
         // Step 1
         if self.readonly {
             return Err(Error::NoModificationAllowed);
         }
 
-        // Step 2
-        property.make_ascii_lowercase();
-
-        // Step 3
-        let value = self.GetPropertyValue(property.clone());
-
-        let element = self.owner.upcast::<Element>();
+        let mut style_attribute = self.owner.style_attribute().borrow_mut();
+        let mut string = String::new();
+        let empty;
+        {
+            let mut style_attribute = if let Some(ref lock) = *style_attribute {
+                lock.write()
+            } else {
+                // No style attribute is like an empty style attribute: nothing to remove.
+                return Ok(DOMString::new())
+            };
 
-        match Shorthand::from_name(&property) {
-            // Step 4
-            Some(shorthand) => {
-                for longhand in shorthand.longhands() {
-                    element.remove_inline_style_property(longhand)
-                }
-            }
-            // Step 5
-            None => element.remove_inline_style_property(&property),
+            // 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;
         }
 
-        let node = element.upcast::<Node>();
+        let node = self.owner.upcast::<Node>();
         node.dirty(NodeDamage::NodeStyleDamaged);
 
         // Step 6
-        Ok(value)
+        Ok(DOMString::from(string))
     }
 
     // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-cssfloat
     fn CssFloat(&self) -> DOMString {
         self.GetPropertyValue(DOMString::from("float"))
     }
 
     // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-cssfloat
@@ -324,59 +317,59 @@ impl CSSStyleDeclarationMethods for CSSS
         self.SetPropertyValue(DOMString::from("float"), value)
     }
 
     // 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(|declarations| {
-            declarations.read().declarations.get(index).map(|entry| {
+        style_attribute.as_ref().and_then(|lock| {
+            lock.read().declarations.get(index).map(|entry| {
                 let (ref declaration, importance) = *entry;
                 let mut css = declaration.to_css_string();
                 if importance.important() {
                     css += " !important";
                 }
                 DOMString::from(css)
             })
         })
     }
 
     // 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(declarations) = style_attribute.as_ref() {
-            DOMString::from(declarations.read().to_css_string())
+        if let Some(lock) = style_attribute.as_ref() {
+            DOMString::from(lock.read().to_css_string())
         } else {
             DOMString::new()
         }
     }
 
     // https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-csstext
     fn SetCssText(&self, value: DOMString) -> ErrorResult {
         let window = window_from_node(self.owner.upcast::<Node>());
-        let element = self.owner.upcast::<Element>();
 
         // Step 1
         if self.readonly {
             return Err(Error::NoModificationAllowed);
         }
 
         // Step 3
         let decl_block = parse_style_attribute(&value, &window.get_url(), window.css_error_reporter(),
                                                ParserContextExtraData::default());
-        *element.style_attribute().borrow_mut() = if decl_block.declarations.is_empty() {
+        *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)))
         };
-        element.sync_property_with_attrs_style();
-        let node = element.upcast::<Node>();
+        let node = self.owner.upcast::<Node>();
         node.dirty(NodeDamage::NodeStyleDamaged);
         Ok(())
     }
 
     // https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-_camel_cased_attribute
     css_properties_accessors!(css_properties);
 }
--- a/servo/components/script/dom/element.rs
+++ b/servo/components/script/dom/element.rs
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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/. */
 
 //! Element nodes.
 
 use app_units::Au;
-use cssparser::{Color, ToCss};
+use cssparser::Color;
 use devtools_traits::AttrInfo;
 use dom::activation::Activatable;
 use dom::attr::{Attr, AttrHelpersForLayout};
 use dom::bindings::cell::DOMRefCell;
 use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
 use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
 use dom::bindings::codegen::Bindings::ElementBinding;
 use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
@@ -730,24 +730,18 @@ impl Element {
             atom!("input") | atom!("keygen") | atom!("link") | atom!("menuitem") | atom!("meta") |
             atom!("param") | atom!("source") | atom!("track") | atom!("wbr") => true,
             _ => false
         }
     }
 
     // this sync method is called upon modification of the style_attribute property,
     // therefore, it should not trigger subsequent mutation events
-    pub fn sync_property_with_attrs_style(&self) {
-        let style_str = if let &Some(ref declarations) = &*self.style_attribute().borrow() {
-            declarations.read().to_css_string()
-        } else {
-            String::new()
-        };
-
-        let mut new_style = AttrValue::String(style_str);
+    pub fn set_style_attr(&self, new_value: String) {
+        let mut new_style = AttrValue::String(new_value);
 
         if let Some(style_attr) = self.attrs.borrow().iter().find(|a| a.name() == &atom!("style")) {
             style_attr.swap_value(&mut new_style);
             return;
         }
 
         // explicitly not calling the push_new_attribute convenience method
         // in order to avoid triggering mutation events
@@ -759,135 +753,16 @@ impl Element {
                              ns!(),
                              Some(atom!("style")),
                              Some(self));
 
          assert!(attr.GetOwnerElement().r() == Some(self));
          self.attrs.borrow_mut().push(JS::from_ref(&attr));
     }
 
-    pub fn remove_inline_style_property(&self, property: &str) {
-        fn remove(element: &Element, property: &str) {
-            let mut inline_declarations = element.style_attribute.borrow_mut();
-            if let &mut Some(ref mut declarations) = &mut *inline_declarations {
-                let mut importance = None;
-                let index = declarations.read().declarations.iter().position(|&(ref decl, i)| {
-                    let matching = decl.matches(property);
-                    if matching {
-                        importance = Some(i)
-                    }
-                    matching
-                });
-                if let Some(index) = index {
-                    let mut declarations = declarations.write();
-                    declarations.declarations.remove(index);
-                    if importance.unwrap().important() {
-                        declarations.important_count -= 1;
-                    }
-                }
-            }
-        }
-
-        remove(self, property);
-        self.sync_property_with_attrs_style();
-    }
-
-    pub fn update_inline_style(&self,
-                               declarations: Vec<PropertyDeclaration>,
-                               importance: Importance) {
-        fn update(element: &Element, declarations: Vec<PropertyDeclaration>,
-                  importance: Importance) {
-            let mut inline_declarations = element.style_attribute().borrow_mut();
-            if let &mut Some(ref mut declaration_block) = &mut *inline_declarations {
-                {
-                    let mut declaration_block = declaration_block.write();
-                    let declaration_block = &mut *declaration_block;
-                    let existing_declarations = &mut declaration_block.declarations;
-
-                    'outer: for incoming_declaration in declarations {
-                        for existing_declaration in &mut *existing_declarations {
-                            if existing_declaration.0.name() == incoming_declaration.name() {
-                                match (existing_declaration.1, importance) {
-                                    (Importance::Normal, Importance::Important) => {
-                                        declaration_block.important_count += 1;
-                                    }
-                                    (Importance::Important, Importance::Normal) => {
-                                        declaration_block.important_count -= 1;
-                                    }
-                                    _ => {}
-                                }
-                                *existing_declaration = (incoming_declaration, importance);
-                                continue 'outer;
-                            }
-                        }
-                        existing_declarations.push((incoming_declaration, importance));
-                        if importance.important() {
-                            declaration_block.important_count += 1;
-                        }
-                    }
-                }
-                return;
-            }
-
-            let important_count = if importance.important() {
-                declarations.len() as u32
-            } else {
-                0
-            };
-
-            *inline_declarations = Some(Arc::new(RwLock::new(PropertyDeclarationBlock {
-                declarations: declarations.into_iter().map(|d| (d, importance)).collect(),
-                important_count: important_count,
-            })));
-        }
-
-        update(self, declarations, importance);
-        self.sync_property_with_attrs_style();
-    }
-
-    pub fn set_inline_style_property_priority(&self,
-                                              properties: &[&str],
-                                              new_importance: Importance) {
-        {
-            let mut inline_declarations = self.style_attribute().borrow_mut();
-            if let &mut Some(ref mut block) = &mut *inline_declarations {
-                let mut block = block.write();
-                let block = &mut *block;
-                let declarations = &mut block.declarations;
-                for &mut (ref declaration, ref mut importance) in declarations {
-                    if properties.iter().any(|p| declaration.name() == **p) {
-                        match (*importance, new_importance) {
-                            (Importance::Normal, Importance::Important) => {
-                                block.important_count += 1;
-                            }
-                            (Importance::Important, Importance::Normal) => {
-                                block.important_count -= 1;
-                            }
-                            _ => {}
-                        }
-                        *importance = new_importance;
-                    }
-                }
-            }
-        }
-
-        self.sync_property_with_attrs_style();
-    }
-
-    pub fn get_inline_style_declaration<F, R>(&self, property: &str, f: F) -> R
-    where F: FnOnce(Option<&(PropertyDeclaration, Importance)>) -> R {
-        let style_attr = self.style_attribute.borrow();
-        if let Some(ref block) = *style_attr {
-            let block = block.read();
-            f(block.get(property))
-        } else {
-            f(None)
-        }
-    }
-
     pub fn serialize(&self, traversal_scope: TraversalScope) -> Fallible<DOMString> {
         let mut writer = vec![];
         match serialize(&mut writer,
                         &self.upcast::<Node>(),
                         SerializeOpts {
                             traversal_scope: traversal_scope,
                             ..Default::default()
                         }) {
--- a/servo/components/style/properties/declaration_block.rs
+++ b/servo/components/style/properties/declaration_block.rs
@@ -1,16 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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::{DeclarationListParser, parse_important, ToCss};
 use cssparser::{Parser, AtRuleParser, DeclarationParser, Delimiter};
 use error_reporting::ParseErrorReporter;
 use parser::{ParserContext, ParserContextExtraData, log_css_error};
+use std::ascii::AsciiExt;
 use std::boxed::Box as StdBox;
 use std::fmt;
 use stylesheets::Origin;
 use super::*;
 use url::Url;
 
 
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@@ -61,35 +62,159 @@ impl PropertyDeclarationBlock {
     // FIXME: make fields private and maintain it here in methods?
     pub fn any_normal(&self) -> bool {
         self.declarations.len() > self.important_count as usize
     }
 
     pub fn get(&self, property_name: &str) -> Option< &(PropertyDeclaration, Importance)> {
         self.declarations.iter().find(|&&(ref decl, _)| decl.matches(property_name))
     }
-}
+
+    /// Find the value of the given property in this block and serialize it
+    ///
+    /// https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertyvalue
+    pub fn property_value_to_css<W>(&self, property_name: &str, dest: &mut W) -> fmt::Result
+    where W: fmt::Write {
+        // Step 1
+        let property = property_name.to_ascii_lowercase();
+
+        // Step 2
+        if let Some(shorthand) = Shorthand::from_name(&property) {
+            // Step 2.1
+            let mut list = Vec::new();
+
+            // Step 2.2
+            for longhand in shorthand.longhands() {
+                // Step 2.2.1
+                let declaration = self.get(longhand);
+
+                // Step 2.2.2 & 2.2.3
+                match declaration {
+                    Some(&(ref declaration, _importance)) => list.push(declaration),
+                    None => return Ok(()),
+                }
+            }
+
+            // Step 2.3
+            // TODO: importance is hardcoded because method does not implement it yet
+            let importance = Importance::Normal;
+            let appendable_value = shorthand.get_shorthand_appendable_value(list).unwrap();
+            return append_declaration_value(dest, appendable_value, importance)
+        }
+
+        if let Some(&(ref value, _importance)) = self.get(property_name) {
+            // Step 3
+            value.to_css(dest)
+        } else {
+            // Step 4
+            Ok(())
+        }
+    }
+
+    /// https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertypriority
+    pub fn property_priority(&self, property_name: &str) -> Importance {
+        // Step 1
+        let property = property_name.to_ascii_lowercase();
+
+        // Step 2
+        if let Some(shorthand) = Shorthand::from_name(&property) {
+            // Step 2.1 & 2.2 & 2.3
+            if shorthand.longhands().iter().all(|l| {
+                self.get(l).map_or(false, |&(_, importance)| importance.important())
+            }) {
+                Importance::Important
+            } else {
+                Importance::Normal
+            }
+        } else {
+            // Step 3
+            self.get(&property).map_or(Importance::Normal, |&(_, importance)| importance)
+        }
+    }
 
-impl PropertyDeclarationBlock {
-    // Take a declaration block known to contain a single property,
-    // and serialize it
-    pub fn to_css_single_value<W>(&self, dest: &mut W, name: &str)
-        -> fmt::Result where W: fmt::Write {
+    pub fn set_parsed_declaration(&mut self, declaration: PropertyDeclaration,
+                                  importance: Importance) {
+        for slot in &mut *self.declarations {
+            if slot.0.name() == declaration.name() {
+                match (slot.1, importance) {
+                    (Importance::Normal, Importance::Important) => {
+                        self.important_count += 1;
+                    }
+                    (Importance::Important, Importance::Normal) => {
+                        self.important_count -= 1;
+                    }
+                    _ => {}
+                }
+                *slot = (declaration, importance);
+                return
+            }
+        }
+
+        self.declarations.push((declaration, importance));
+        if importance.important() {
+            self.important_count += 1;
+        }
+    }
+
+    pub fn set_importance(&mut self, property_names: &[&str], new_importance: Importance) {
+        for &mut (ref declaration, ref mut importance) in &mut self.declarations {
+            if property_names.iter().any(|p| declaration.matches(p)) {
+                match (*importance, new_importance) {
+                    (Importance::Normal, Importance::Important) => {
+                        self.important_count += 1;
+                    }
+                    (Importance::Important, Importance::Normal) => {
+                        self.important_count -= 1;
+                    }
+                    _ => {}
+                }
+                *importance = new_importance;
+            }
+        }
+    }
+
+    /// https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-removeproperty
+    pub fn remove_property(&mut self, property_name: &str) {
+        // Step 2
+        let property = property_name.to_ascii_lowercase();
+
+        match Shorthand::from_name(&property) {
+            // Step 4
+            Some(shorthand) => self.remove_longhands(shorthand.longhands()),
+            // Step 5
+            None => self.remove_longhands(&[&*property]),
+        }
+    }
+
+    fn remove_longhands(&mut self, names: &[&str]) {
+        let important_count = &mut self.important_count;
+        self.declarations.retain(|&(ref declaration, importance)| {
+            let retain = !names.iter().any(|n| declaration.matches(n));
+            if !retain && importance.important() {
+                *important_count -= 1
+            }
+            retain
+        })
+    }
+
+    /// Take a declaration block known to contain a single property and serialize it.
+    pub fn single_value_to_css<W>(&self, property_name: &str, dest: &mut W) -> fmt::Result
+    where W: fmt::Write {
         match self.declarations.len() {
             0 => Err(fmt::Error),
-            1 if self.declarations[0].0.name().eq_str_ignore_ascii_case(name) => {
+            1 if self.declarations[0].0.name().eq_str_ignore_ascii_case(property_name) => {
                 self.declarations[0].0.to_css(dest)
             }
             _ => {
                 // we use this function because a closure won't be `Clone`
                 fn get_declaration(dec: &(PropertyDeclaration, Importance))
                     -> &PropertyDeclaration {
                     &dec.0
                 }
-                let shorthand = try!(Shorthand::from_name(name).ok_or(fmt::Error));
+                let shorthand = try!(Shorthand::from_name(property_name).ok_or(fmt::Error));
                 if !self.declarations.iter().all(|decl| decl.0.shorthands().contains(&shorthand)) {
                     return Err(fmt::Error)
                 }
                 let success = try!(shorthand.serialize_shorthand_to_buffer(
                     dest,
                     self.declarations.iter()
                                      .map(get_declaration as fn(_) -> _),
                     &mut true)
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -405,25 +405,16 @@ impl Shorthand {
                         Ok(longhands) => longhands.to_css(dest),
                         Err(_) => Err(fmt::Error)
                     }
                 },
             % endfor
         }
     }
 
-    /// Serializes possible shorthand value to String.
-    pub fn serialize_shorthand_value_to_string<'a, I>(self, declarations: I, importance: Importance) -> String
-    where I: IntoIterator<Item=&'a PropertyDeclaration>, I::IntoIter: Clone {
-        let appendable_value = self.get_shorthand_appendable_value(declarations).unwrap();
-        let mut result = String::new();
-        append_declaration_value(&mut result, appendable_value, importance).unwrap();
-        result
-    }
-
     /// Serializes possible shorthand name with value to input buffer given a list of longhand declarations.
     /// On success, returns true if shorthand value is written and false if no shorthand value is present.
     pub fn serialize_shorthand_to_buffer<'a, W, I>(self,
                                                    dest: &mut W,
                                                    declarations: I,
                                                    is_first_serialization: &mut bool)
                                                    -> Result<bool, fmt::Error>
     where W: Write, I: IntoIterator<Item=&'a PropertyDeclaration>, I::IntoIter: Clone {
@@ -1978,16 +1969,18 @@ pub fn modify_style_for_text(style: &mut
 pub fn modify_style_for_inline_absolute_hypothetical_fragment(style: &mut Arc<ComputedValues>) {
     if style.get_effects().clip.0.is_some() {
         let mut style = Arc::make_mut(style);
         let effects_style = Arc::make_mut(&mut style.effects);
         effects_style.clip.0 = None
     }
 }
 
+
+// FIXME: https://github.com/w3c/csswg-drafts/issues/580
 pub fn is_supported_property(property: &str) -> bool {
     match_ignore_ascii_case! { property,
         % for property in data.shorthands + data.longhands:
             "${property.name}" => true,
         % endfor
         _ => property.starts_with("--")
     }
 }