mirror of
https://github.com/servo/servo.git
synced 2025-08-04 05:00:08 +01:00
Converted serialization methods to implement the to_css trait, writing to string buffers to save string allocations for every result
This commit is contained in:
parent
0985d7563f
commit
51e642e875
4 changed files with 132 additions and 46 deletions
|
@ -10,7 +10,7 @@ use dom::bindings::js::{JS, Root};
|
||||||
use dom::bindings::reflector::{Reflector, reflect_dom_object};
|
use dom::bindings::reflector::{Reflector, reflect_dom_object};
|
||||||
use dom::bindings::str::DOMString;
|
use dom::bindings::str::DOMString;
|
||||||
use dom::element::{Element, StylePriority};
|
use dom::element::{Element, StylePriority};
|
||||||
use dom::node::{Node, NodeDamage, window_from_node};
|
use dom::node::{Node, window_from_node};
|
||||||
use dom::window::Window;
|
use dom::window::Window;
|
||||||
use std::ascii::AsciiExt;
|
use std::ascii::AsciiExt;
|
||||||
use string_cache::Atom;
|
use string_cache::Atom;
|
||||||
|
@ -147,7 +147,8 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
|
||||||
|
|
||||||
// Step 2.3
|
// Step 2.3
|
||||||
let list = list.iter().map(|x| &*x).collect::<Vec<_>>();
|
let list = list.iter().map(|x| &*x).collect::<Vec<_>>();
|
||||||
return DOMString::from(shorthand.serialize_shorthand(&list));
|
let serialized_value = shorthand.serialize_shorthand_to_string(&list);
|
||||||
|
return DOMString::from(serialized_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 3 & 4
|
// Step 3 & 4
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
//! Element nodes.
|
//! Element nodes.
|
||||||
|
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use cssparser::Color;
|
use cssparser::{Color, ToCss};
|
||||||
use devtools_traits::AttrInfo;
|
use devtools_traits::AttrInfo;
|
||||||
use dom::activation::Activatable;
|
use dom::activation::Activatable;
|
||||||
use dom::attr::AttrValue;
|
use dom::attr::AttrValue;
|
||||||
|
@ -699,12 +699,10 @@ impl Element {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sync_property_with_attrs_style(&self) {
|
fn sync_property_with_attrs_style(&self) {
|
||||||
let style_str = if let &Some(ref declarations) = &*self.style_attribute().borrow() {
|
let mut style_str = String::new();
|
||||||
declarations.serialize()
|
if let &Some(ref declarations) = &*self.style_attribute().borrow() {
|
||||||
|
declarations.to_css(&mut style_str).unwrap();
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
String::new()
|
|
||||||
};
|
|
||||||
|
|
||||||
let new_style = AttrValue::String(style_str);
|
let new_style = AttrValue::String(style_str);
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ use std::ascii::AsciiExt;
|
||||||
use std::boxed::Box as StdBox;
|
use std::boxed::Box as StdBox;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::fmt::Write;
|
||||||
use std::intrinsics;
|
use std::intrinsics;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -266,11 +267,12 @@ pub struct PropertyDeclarationBlock {
|
||||||
pub normal: Arc<Vec<PropertyDeclaration>>,
|
pub normal: Arc<Vec<PropertyDeclaration>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PropertyDeclarationBlock {
|
impl ToCss for PropertyDeclarationBlock {
|
||||||
// https://drafts.csswg.org/cssom/#serialize-a-css-declaration-block
|
// https://drafts.csswg.org/cssom/#serialize-a-css-declaration-block
|
||||||
pub fn serialize(&self) -> String {
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||||
// Step 1
|
let mut is_first_serialization = true; // trailing serializations should have a prepended space
|
||||||
let mut result_list = String::new();
|
|
||||||
|
// Step 1 -> dest = result list
|
||||||
|
|
||||||
// Step 2
|
// Step 2
|
||||||
let mut already_serialized = Vec::new();
|
let mut already_serialized = Vec::new();
|
||||||
|
@ -326,16 +328,15 @@ impl PropertyDeclarationBlock {
|
||||||
|
|
||||||
// TODO: serialize shorthand does not take is_important into account currently
|
// TODO: serialize shorthand does not take is_important into account currently
|
||||||
// Substep 5
|
// Substep 5
|
||||||
let value = shorthand.serialize_shorthand(¤t_longhands[..]);
|
let was_serialized =
|
||||||
|
try!(shorthand.serialize_shorthand_to_buffer(dest, ¤t_longhands[..], &mut is_first_serialization));
|
||||||
|
// If serialization occured, Substep 7 & 8 will have been completed
|
||||||
|
|
||||||
// Substep 6
|
// Substep 6
|
||||||
if value.is_empty() {
|
if !was_serialized {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Substep 7 & 8
|
|
||||||
result_list.push_str(&format!("{}: {}; ", &shorthand.name(), value));
|
|
||||||
|
|
||||||
for current_longhand in current_longhands {
|
for current_longhand in current_longhands {
|
||||||
// Substep 9
|
// Substep 9
|
||||||
already_serialized.push(current_longhand.name());
|
already_serialized.push(current_longhand.name());
|
||||||
|
@ -353,24 +354,63 @@ impl PropertyDeclarationBlock {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 3.3.5
|
// Steps 3.3.5, 3.3.6 & 3.3.7
|
||||||
let mut value = declaration.value();
|
let append_important = self.important.contains(declaration);
|
||||||
if self.important.contains(declaration) {
|
try!(append_serialization(dest,
|
||||||
value.push_str(" !important");
|
&property.to_string(),
|
||||||
}
|
AppendableValue::Declaration(declaration),
|
||||||
// Steps 3.3.6 & 3.3.7
|
append_important,
|
||||||
result_list.push_str(&format!("{}: {}; ", &property, value));
|
&mut is_first_serialization));
|
||||||
|
|
||||||
// Step 3.3.8
|
// Step 3.3.8
|
||||||
already_serialized.push(property);
|
already_serialized.push(property);
|
||||||
}
|
}
|
||||||
|
|
||||||
result_list.pop(); // remove trailling whitespace
|
|
||||||
// Step 4
|
// Step 4
|
||||||
result_list
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum AppendableValue<'a> {
|
||||||
|
Declaration(&'a PropertyDeclaration),
|
||||||
|
Css(&'a str)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn append_serialization<W>(dest: &mut W,
|
||||||
|
property_name: &str,
|
||||||
|
appendable_value: AppendableValue,
|
||||||
|
is_important: bool,
|
||||||
|
is_first_serialization: &mut bool) -> fmt::Result where W: fmt::Write
|
||||||
|
|
||||||
|
// after first serialization(key: value;) add whitespace between the pairs
|
||||||
|
if !*is_first_serialization {
|
||||||
|
try!(write!(dest, " "));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
*is_first_serialization = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try!(write!(dest, "{}:", property_name));
|
||||||
|
|
||||||
|
match appendable_value {
|
||||||
|
AppendableValue::Declaration(decl) => {
|
||||||
|
if !decl.value_is_unparsed() {
|
||||||
|
// for normal parsed values, add a space between key: and value
|
||||||
|
try!(write!(dest, " "));
|
||||||
|
}
|
||||||
|
|
||||||
|
try!(decl.to_css(dest));
|
||||||
|
},
|
||||||
|
AppendableValue::Css(css) => try!(write!(dest, "{}", css))
|
||||||
|
};
|
||||||
|
|
||||||
|
if is_important {
|
||||||
|
try!(write!(dest, " !important"));
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(dest, ";")
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_style_attribute(input: &str, base_url: &Url, error_reporter: StdBox<ParseErrorReporter + Send>,
|
pub fn parse_style_attribute(input: &str, base_url: &Url, error_reporter: StdBox<ParseErrorReporter + Send>,
|
||||||
extra_data: ParserContextExtraData)
|
extra_data: ParserContextExtraData)
|
||||||
-> PropertyDeclarationBlock {
|
-> PropertyDeclarationBlock {
|
||||||
|
@ -509,8 +549,6 @@ pub enum Shorthand {
|
||||||
% endfor
|
% endfor
|
||||||
}
|
}
|
||||||
|
|
||||||
use util::str::str_join;
|
|
||||||
|
|
||||||
impl Shorthand {
|
impl Shorthand {
|
||||||
pub fn from_name(name: &str) -> Option<Shorthand> {
|
pub fn from_name(name: &str) -> Option<Shorthand> {
|
||||||
match_ignore_ascii_case! { name,
|
match_ignore_ascii_case! { name,
|
||||||
|
@ -544,25 +582,48 @@ impl Shorthand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn serialize_shorthand(self, declarations: &[&PropertyDeclaration]) -> String {
|
/// Serializes possible shorthand value to String.
|
||||||
|
pub fn serialize_shorthand_to_string(self, declarations: &[&PropertyDeclaration]) -> String {
|
||||||
|
let mut result = String::new();
|
||||||
|
self.serialize_shorthand_to_buffer(&mut result, declarations, &mut true).unwrap();
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serializes possible shorthand 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<W>(self,
|
||||||
|
dest: &mut W,
|
||||||
|
declarations: &[&PropertyDeclaration],
|
||||||
|
is_first_serialization: &mut bool)
|
||||||
|
-> Result<bool, fmt::Error> where W: Write {
|
||||||
|
|
||||||
|
let property_name = self.name();
|
||||||
|
|
||||||
// https://drafts.csswg.org/css-variables/#variables-in-shorthands
|
// https://drafts.csswg.org/css-variables/#variables-in-shorthands
|
||||||
if let Some(css) = declarations[0].with_variables_from_shorthand(self) {
|
if let Some(css) = declarations[0].with_variables_from_shorthand(self) {
|
||||||
if declarations[1..]
|
if declarations[1..]
|
||||||
.iter()
|
.iter()
|
||||||
.all(|d| d.with_variables_from_shorthand(self) == Some(css)) {
|
.all(|d| d.with_variables_from_shorthand(self) == Some(css)) {
|
||||||
css.to_owned()
|
|
||||||
|
append_serialization(
|
||||||
|
dest, property_name, AppendableValue::Css(css), false, is_first_serialization
|
||||||
|
).and_then(|_| Ok(true))
|
||||||
} else {
|
} else {
|
||||||
String::new()
|
Ok(false)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if declarations.iter().any(|d| d.with_variables()) {
|
if declarations.iter().any(|d| d.with_variables()) {
|
||||||
String::new()
|
Ok(false)
|
||||||
} else {
|
} else {
|
||||||
let str_iter = declarations.iter().map(|d| d.value());
|
for declaration in declarations.iter() {
|
||||||
|
try!(append_serialization(
|
||||||
|
dest, property_name, AppendableValue::Declaration(declaration), false, is_first_serialization
|
||||||
|
));
|
||||||
|
}
|
||||||
// FIXME: this needs property-specific code, which probably should be in style/
|
// FIXME: this needs property-specific code, which probably should be in style/
|
||||||
// "as appropriate according to the grammar of shorthand "
|
// "as appropriate according to the grammar of shorthand "
|
||||||
// https://drafts.csswg.org/cssom/#serialize-a-css-value
|
// https://drafts.csswg.org/cssom/#serialize-a-css-value
|
||||||
str_join(str_iter, " ")
|
Ok(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -647,6 +708,22 @@ impl fmt::Display for PropertyDeclarationName {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl ToCss for PropertyDeclaration {
|
||||||
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||||
|
match *self {
|
||||||
|
% for property in LONGHANDS:
|
||||||
|
% if property.derived_from is None:
|
||||||
|
PropertyDeclaration::${property.camel_case}(ref value) =>
|
||||||
|
value.to_css(dest),
|
||||||
|
% endif
|
||||||
|
% endfor
|
||||||
|
PropertyDeclaration::Custom(_, ref value) => value.to_css(dest),
|
||||||
|
% if any(property.derived_from for property in data.longhands):
|
||||||
|
_ => Err(fmt::Error),
|
||||||
|
% endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PropertyDeclaration {
|
impl PropertyDeclaration {
|
||||||
pub fn name(&self) -> PropertyDeclarationName {
|
pub fn name(&self) -> PropertyDeclarationName {
|
||||||
|
@ -666,17 +743,12 @@ impl PropertyDeclaration {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn value(&self) -> String {
|
pub fn value(&self) -> String {
|
||||||
match *self {
|
let mut value = String::new();
|
||||||
% for property in data.longhands:
|
if let Err(_) = self.to_css(&mut value) {
|
||||||
PropertyDeclaration::${property.camel_case}
|
panic!("unsupported property declaration: {}", self.name());
|
||||||
% if not property.derived_from:
|
|
||||||
(ref value) => value.to_css_string(),
|
|
||||||
% else:
|
|
||||||
(_) => panic!("unsupported property declaration: ${property.name}"),
|
|
||||||
% endif
|
|
||||||
% endfor
|
|
||||||
PropertyDeclaration::Custom(_, ref value) => value.to_css_string(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
value
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If this is a pending-substitution value from the given shorthand, return that value
|
/// If this is a pending-substitution value from the given shorthand, return that value
|
||||||
|
@ -714,6 +786,20 @@ impl PropertyDeclaration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return whether the value is stored as it was in the CSS source, preserving whitespace
|
||||||
|
/// (as opposed to being parsed into a more abstract data structure).
|
||||||
|
/// This is the case of custom properties and values that contain unsubstituted variables.
|
||||||
|
pub fn value_is_unparsed(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
% for property in LONGHANDS:
|
||||||
|
PropertyDeclaration::${property.camel_case}(ref value) => {
|
||||||
|
matches!(*value, DeclaredValue::WithVariables { .. })
|
||||||
|
},
|
||||||
|
% endfor
|
||||||
|
PropertyDeclaration::Custom(..) => true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn matches(&self, name: &str) -> bool {
|
pub fn matches(&self, name: &str) -> bool {
|
||||||
match *self {
|
match *self {
|
||||||
% for property in data.longhands:
|
% for property in data.longhands:
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use cssparser::ToCss;
|
||||||
use rustc_serialize::json::Json;
|
use rustc_serialize::json::Json;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs::{File, remove_file};
|
use std::fs::{File, remove_file};
|
||||||
|
@ -83,7 +84,7 @@ fn property_declaration_block_should_serialize_correctly() {
|
||||||
important: Arc::new(important)
|
important: Arc::new(important)
|
||||||
};
|
};
|
||||||
|
|
||||||
let css_string = block.serialize();
|
let css_string = block.to_css_string();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
css_string,
|
css_string,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue