Auto merge of #13640 - servo:CSSStyleDeclaration_in_style, r=mbrubeck

Move some of the CSSStyleDeclaration logic to the style crate

<!-- 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. -->

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/13640)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2016-10-12 14:21:34 -05:00 committed by GitHub
commit 626d093245
4 changed files with 271 additions and 285 deletions

View file

@ -17,7 +17,7 @@ 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;
@ -91,7 +91,7 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
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
@ -104,92 +104,47 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
// 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()
};
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.1
let mut list = vec![];
// Step 2.2
for longhand in shorthand.longhands() {
// Step 2.2.1
let declaration = style_attribute.get(longhand);
// 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 {
@ -198,46 +153,78 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
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(())
}
@ -255,24 +242,26 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
}
// 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),
}
let node = element.upcast::<Node>();
node.dirty(NodeDamage::NodeStyleDamaged);
self.owner.set_style_attr(style_attribute.to_css_string());
let node = self.owner.upcast::<Node>();
node.dirty(NodeDamage::NodeStyleDamaged);
}
Ok(())
}
@ -282,36 +271,40 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
}
// 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();
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())
};
// Step 3
let value = self.GetPropertyValue(property.clone());
// Step 3
style_attribute.property_value_to_css(&property, &mut string).unwrap();
let element = self.owner.upcast::<Element>();
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 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
@ -329,8 +322,8 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
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() {
@ -346,8 +339,8 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
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()
}
@ -356,7 +349,6 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
// 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 {
@ -366,13 +358,14 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
// 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(())
}

View file

@ -5,7 +5,7 @@
//! 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};
@ -735,14 +735,8 @@ impl Element {
// 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);
@ -764,125 +758,6 @@ impl Element {
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,

View file

@ -6,6 +6,7 @@ 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;
@ -66,16 +67,140 @@ impl PropertyDeclarationBlock {
pub fn get(&self, property_name: &str) -> Option< &(PropertyDeclaration, Importance)> {
self.declarations.iter().find(|&&(ref decl, _)| decl.matches(property_name))
}
}
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 {
/// 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)
}
}
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)
}
_ => {
@ -84,7 +209,7 @@ impl PropertyDeclarationBlock {
-> &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)
}

View file

@ -410,15 +410,6 @@ impl Shorthand {
}
}
/// 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,
@ -1983,6 +1974,8 @@ pub fn modify_style_for_inline_absolute_hypothetical_fragment(style: &mut Arc<Co
}
}
// 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: