servo: Merge #18355 - Reduce usage of fmt in serialization and error reporting (from servo:no-fmt); r=emilio
authorSimon Sapin <simon.sapin@exyr.org>
Mon, 04 Sep 2017 13:14:44 -0500
changeset 428277 61598569fcdf491c5ccbf24aa59683dc5e0e958e
parent 428276 fc5fc58f42a3ebab01c6e83901a2dde2435b0933
child 428278 8f120e9b0a45f0bf03920f00241d68e02852a8a4
push id7761
push userjlund@mozilla.com
push dateFri, 15 Sep 2017 00:19:52 +0000
treeherdermozilla-beta@c38455951db4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
milestone57.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
servo: Merge #18355 - Reduce usage of fmt in serialization and error reporting (from servo:no-fmt); r=emilio `format!` and `write!` create a somewhat-heavyweight `Formatting` struct and use dynamic dispatch to call into impls of `Dispaly` and related traits. The former also allocates an intermediate string that is sometimes unnecessary. I started looking into this from https://bugzilla.mozilla.org/show_bug.cgi?id=1355599, but I expect the impact there will be small to insignificant. It might be a slightly less so on parsing (error reporting). Source-Repo: https://github.com/servo/servo Source-Revision: c60dd53210745d9d8e7d3a5ca0310370a33553f4
servo/components/script_layout_interface/reporter.rs
servo/components/style/counter_style/mod.rs
servo/components/style/error_reporting.rs
servo/components/style/gecko/media_queries.rs
servo/components/style/gecko/selector_parser.rs
servo/components/style/macros.rs
servo/components/style/media_queries.rs
servo/components/style/properties/longhand/box.mako.rs
servo/components/style/properties/longhand/font.mako.rs
servo/components/style/properties/shorthand/box.mako.rs
servo/components/style/servo/media_queries.rs
servo/components/style/stylesheets/font_feature_values_rule.rs
servo/components/style/stylesheets/keyframes_rule.rs
servo/components/style/stylesheets/viewport_rule.rs
servo/components/style/values/generics/mod.rs
servo/components/style/values/specified/length.rs
servo/components/style_traits/values.rs
servo/components/style_traits/viewport.rs
servo/ports/geckolib/error_reporter.rs
servo/tests/unit/style/rule_tree/bench.rs
servo/tests/unit/style/stylesheets.rs
servo/tests/unit/style/viewport.rs
servo/tests/unit/stylo/lib.rs
servo/tests/unit/stylo/sanity_checks.rs
servo/tests/unit/stylo/specified_values.rs
--- a/servo/components/script_layout_interface/reporter.rs
+++ b/servo/components/script_layout_interface/reporter.rs
@@ -26,17 +26,17 @@ impl ParseErrorReporter for CSSErrorRepo
                     url: &ServoUrl,
                     location: SourceLocation,
                     error: ContextualParseError) {
         if log_enabled!(log::LogLevel::Info) {
             info!("Url:\t{}\n{}:{} {}",
                   url.as_str(),
                   location.line,
                   location.column,
-                  error.to_string())
+                  error)
         }
 
         //TODO: report a real filename
         let _ = self.script_chan.lock().unwrap().send(
             ConstellationControlMsg::ReportCSSError(self.pipelineid,
                                                     "".to_owned(),
                                                     location.line,
                                                     location.column,
--- a/servo/components/style/counter_style/mod.rs
+++ b/servo/components/style/counter_style/mod.rs
@@ -322,17 +322,18 @@ impl ToCss for System {
         match *self {
             System::Cyclic => dest.write_str("cyclic"),
             System::Numeric => dest.write_str("numeric"),
             System::Alphabetic => dest.write_str("alphabetic"),
             System::Symbolic => dest.write_str("symbolic"),
             System::Additive => dest.write_str("additive"),
             System::Fixed { first_symbol_value } => {
                 if let Some(value) = first_symbol_value {
-                    write!(dest, "fixed {}", value)
+                    dest.write_str("fixed ")?;
+                    value.to_css(dest)
                 } else {
                     dest.write_str("fixed")
                 }
             }
             System::Extends(ref other) => {
                 dest.write_str("extends ")?;
                 other.to_css(dest)
             }
@@ -450,17 +451,17 @@ fn range_to_css<W>(range: &Range<Option<
 where W: fmt::Write {
     bound_to_css(range.start, dest)?;
     dest.write_char(' ')?;
     bound_to_css(range.end, dest)
 }
 
 fn bound_to_css<W>(range: Option<i32>, dest: &mut W) -> fmt::Result where W: fmt::Write {
     if let Some(finite) = range {
-        write!(dest, "{}", finite)
+        finite.to_css(dest)
     } else {
         dest.write_str("infinite")
     }
 }
 
 /// https://drafts.csswg.org/css-counter-styles/#counter-style-pad
 #[derive(Clone, Debug, ToCss)]
 pub struct Pad(pub u32, pub Symbol);
--- a/servo/components/style/error_reporting.rs
+++ b/servo/components/style/error_reporting.rs
@@ -4,16 +4,17 @@
 
 //! Types used to report parsing errors.
 
 #![deny(missing_docs)]
 
 use cssparser::{BasicParseError, Token, SourceLocation};
 use cssparser::ParseError as CssParseError;
 use log;
+use std::fmt;
 use style_traits::ParseError;
 use stylesheets::UrlExtraData;
 
 /// Errors that can be encountered while parsing CSS.
 pub enum ContextualParseError<'a> {
     /// A property declaration was not recognized.
     UnsupportedPropertyDeclaration(&'a str, ParseError<'a>),
     /// A font face descriptor was not recognized.
@@ -41,114 +42,137 @@ pub enum ContextualParseError<'a> {
     /// A counter style rule did not have additive-symbols.
     InvalidCounterStyleWithoutAdditiveSymbols,
     /// A counter style rule had extends with symbols.
     InvalidCounterStyleExtendsWithSymbols,
     /// A counter style rule had extends with additive-symbols.
     InvalidCounterStyleExtendsWithAdditiveSymbols
 }
 
-impl<'a> ContextualParseError<'a> {
-    /// Turn a parse error into a string representation.
-    pub fn to_string(&self) -> String {
-        fn token_to_str(t: &Token) -> String {
+impl<'a> fmt::Display for ContextualParseError<'a> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        fn token_to_str(t: &Token, f: &mut fmt::Formatter) -> fmt::Result {
             match *t {
-                Token::Ident(ref i) => format!("identifier {}", i),
-                Token::AtKeyword(ref kw) => format!("keyword @{}", kw),
-                Token::Hash(ref h) => format!("hash #{}", h),
-                Token::IDHash(ref h) => format!("id selector #{}", h),
-                Token::QuotedString(ref s) => format!("quoted string \"{}\"", s),
-                Token::UnquotedUrl(ref u) => format!("url {}", u),
-                Token::Delim(ref d) => format!("delimiter {}", d),
-                Token::Number { int_value: Some(i), .. } => format!("number {}", i),
-                Token::Number { value, .. } => format!("number {}", value),
-                Token::Percentage { int_value: Some(i), .. } => format!("percentage {}", i),
-                Token::Percentage { unit_value, .. } => format!("percentage {}", unit_value * 100.),
-                Token::Dimension { value, ref unit, .. } => format!("dimension {}{}", value, unit),
-                Token::WhiteSpace(_) => format!("whitespace"),
-                Token::Comment(_) => format!("comment"),
-                Token::Colon => format!("colon (:)"),
-                Token::Semicolon => format!("semicolon (;)"),
-                Token::Comma => format!("comma (,)"),
-                Token::IncludeMatch => format!("include match (~=)"),
-                Token::DashMatch => format!("dash match (|=)"),
-                Token::PrefixMatch => format!("prefix match (^=)"),
-                Token::SuffixMatch => format!("suffix match ($=)"),
-                Token::SubstringMatch => format!("substring match (*=)"),
-                Token::Column => format!("column (||)"),
-                Token::CDO => format!("CDO (<!--)"),
-                Token::CDC => format!("CDC (-->)"),
-                Token::Function(ref f) => format!("function {}", f),
-                Token::ParenthesisBlock => format!("parenthesis ("),
-                Token::SquareBracketBlock => format!("square bracket ["),
-                Token::CurlyBracketBlock => format!("curly bracket {{"),
-                Token::BadUrl(ref _u) => format!("bad url parse error"),
-                Token::BadString(ref _s) => format!("bad string parse error"),
-                Token::CloseParenthesis => format!("unmatched close parenthesis"),
-                Token::CloseSquareBracket => format!("unmatched close square bracket"),
-                Token::CloseCurlyBracket => format!("unmatched close curly bracket"),
+                Token::Ident(ref i) => write!(f, "identifier {}", i),
+                Token::AtKeyword(ref kw) => write!(f, "keyword @{}", kw),
+                Token::Hash(ref h) => write!(f, "hash #{}", h),
+                Token::IDHash(ref h) => write!(f, "id selector #{}", h),
+                Token::QuotedString(ref s) => write!(f, "quoted string \"{}\"", s),
+                Token::UnquotedUrl(ref u) => write!(f, "url {}", u),
+                Token::Delim(ref d) => write!(f, "delimiter {}", d),
+                Token::Number { int_value: Some(i), .. } => write!(f, "number {}", i),
+                Token::Number { value, .. } => write!(f, "number {}", value),
+                Token::Percentage { int_value: Some(i), .. } => write!(f, "percentage {}", i),
+                Token::Percentage { unit_value, .. } => write!(f, "percentage {}", unit_value * 100.),
+                Token::Dimension { value, ref unit, .. } => write!(f, "dimension {}{}", value, unit),
+                Token::WhiteSpace(_) => write!(f, "whitespace"),
+                Token::Comment(_) => write!(f, "comment"),
+                Token::Colon => write!(f, "colon (:)"),
+                Token::Semicolon => write!(f, "semicolon (;)"),
+                Token::Comma => write!(f, "comma (,)"),
+                Token::IncludeMatch => write!(f, "include match (~=)"),
+                Token::DashMatch => write!(f, "dash match (|=)"),
+                Token::PrefixMatch => write!(f, "prefix match (^=)"),
+                Token::SuffixMatch => write!(f, "suffix match ($=)"),
+                Token::SubstringMatch => write!(f, "substring match (*=)"),
+                Token::Column => write!(f, "column (||)"),
+                Token::CDO => write!(f, "CDO (<!--)"),
+                Token::CDC => write!(f, "CDC (-->)"),
+                Token::Function(ref name) => write!(f, "function {}", name),
+                Token::ParenthesisBlock => write!(f, "parenthesis ("),
+                Token::SquareBracketBlock => write!(f, "square bracket ["),
+                Token::CurlyBracketBlock => write!(f, "curly bracket {{"),
+                Token::BadUrl(ref _u) => write!(f, "bad url parse error"),
+                Token::BadString(ref _s) => write!(f, "bad string parse error"),
+                Token::CloseParenthesis => write!(f, "unmatched close parenthesis"),
+                Token::CloseSquareBracket => write!(f, "unmatched close square bracket"),
+                Token::CloseCurlyBracket => write!(f, "unmatched close curly bracket"),
             }
         }
 
-        fn parse_error_to_str(err: &ParseError) -> String {
+        fn parse_error_to_str(err: &ParseError, f: &mut fmt::Formatter) -> fmt::Result {
             match *err {
-                CssParseError::Basic(BasicParseError::UnexpectedToken(ref t)) =>
-                    format!("found unexpected {}", token_to_str(t)),
-                CssParseError::Basic(BasicParseError::EndOfInput) =>
-                    format!("unexpected end of input"),
-                CssParseError::Basic(BasicParseError::AtRuleInvalid(ref i)) =>
-                    format!("@ rule invalid: {}", i),
-                CssParseError::Basic(BasicParseError::AtRuleBodyInvalid) =>
-                    format!("@ rule invalid"),
-                CssParseError::Basic(BasicParseError::QualifiedRuleInvalid) =>
-                    format!("qualified rule invalid"),
-                CssParseError::Custom(ref err) =>
-                    format!("{:?}", err)
+                CssParseError::Basic(BasicParseError::UnexpectedToken(ref t)) => {
+                    write!(f, "found unexpected ")?;
+                    token_to_str(t, f)
+                }
+                CssParseError::Basic(BasicParseError::EndOfInput) => {
+                    write!(f, "unexpected end of input")
+                }
+                CssParseError::Basic(BasicParseError::AtRuleInvalid(ref i)) => {
+                    write!(f, "@ rule invalid: {}", i)
+                }
+                CssParseError::Basic(BasicParseError::AtRuleBodyInvalid) => {
+                    write!(f, "@ rule invalid")
+                }
+                CssParseError::Basic(BasicParseError::QualifiedRuleInvalid) => {
+                    write!(f, "qualified rule invalid")
+                }
+                CssParseError::Custom(ref err) => {
+                    write!(f, "{:?}", err)
+                }
             }
         }
 
         match *self {
-            ContextualParseError::UnsupportedPropertyDeclaration(decl, ref err) =>
-                format!("Unsupported property declaration: '{}', {}", decl,
-                        parse_error_to_str(err)),
-            ContextualParseError::UnsupportedFontFaceDescriptor(decl, ref err) =>
-                format!("Unsupported @font-face descriptor declaration: '{}', {}", decl,
-                        parse_error_to_str(err)),
-            ContextualParseError::UnsupportedFontFeatureValuesDescriptor(decl, ref err) =>
-            format!("Unsupported @font-feature-values descriptor declaration: '{}', {}", decl,
-                    parse_error_to_str(err)),
-            ContextualParseError::InvalidKeyframeRule(rule, ref err) =>
-                format!("Invalid keyframe rule: '{}', {}", rule,
-                        parse_error_to_str(err)),
-            ContextualParseError::InvalidFontFeatureValuesRule(rule, ref err) =>
-            format!("Invalid font feature value rule: '{}', {}", rule,
-                    parse_error_to_str(err)),
-            ContextualParseError::UnsupportedKeyframePropertyDeclaration(decl, ref err) =>
-                format!("Unsupported keyframe property declaration: '{}', {}", decl,
-                        parse_error_to_str(err)),
-            ContextualParseError::InvalidRule(rule, ref err) =>
-                format!("Invalid rule: '{}', {}", rule, parse_error_to_str(err)),
-            ContextualParseError::UnsupportedRule(rule, ref err) =>
-                format!("Unsupported rule: '{}', {}", rule, parse_error_to_str(err)),
-            ContextualParseError::UnsupportedViewportDescriptorDeclaration(decl, ref err) =>
-                format!("Unsupported @viewport descriptor declaration: '{}', {}", decl,
-                        parse_error_to_str(err)),
-            ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(decl, ref err) =>
-                format!("Unsupported @counter-style descriptor declaration: '{}', {}", decl,
-                        parse_error_to_str(err)),
-            ContextualParseError::InvalidCounterStyleWithoutSymbols(ref system) =>
-                format!("Invalid @counter-style rule: 'system: {}' without 'symbols'", system),
-            ContextualParseError::InvalidCounterStyleNotEnoughSymbols(ref system) =>
-                format!("Invalid @counter-style rule: 'system: {}' less than two 'symbols'", system),
-            ContextualParseError::InvalidCounterStyleWithoutAdditiveSymbols =>
-                "Invalid @counter-style rule: 'system: additive' without 'additive-symbols'".into(),
-            ContextualParseError::InvalidCounterStyleExtendsWithSymbols =>
-                "Invalid @counter-style rule: 'system: extends …' with 'symbols'".into(),
-            ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols =>
-                "Invalid @counter-style rule: 'system: extends …' with 'additive-symbols'".into(),
+            ContextualParseError::UnsupportedPropertyDeclaration(decl, ref err) => {
+                write!(f, "Unsupported property declaration: '{}', ", decl)?;
+                parse_error_to_str(err, f)
+            }
+            ContextualParseError::UnsupportedFontFaceDescriptor(decl, ref err) => {
+                write!(f, "Unsupported @font-face descriptor declaration: '{}', ", decl)?;
+                parse_error_to_str(err, f)
+            }
+            ContextualParseError::UnsupportedFontFeatureValuesDescriptor(decl, ref err) => {
+                write!(f, "Unsupported @font-feature-values descriptor declaration: '{}', ", decl)?;
+                parse_error_to_str(err, f)
+            }
+            ContextualParseError::InvalidKeyframeRule(rule, ref err) => {
+                write!(f, "Invalid keyframe rule: '{}', ", rule)?;
+                parse_error_to_str(err, f)
+            }
+            ContextualParseError::InvalidFontFeatureValuesRule(rule, ref err) => {
+                write!(f, "Invalid font feature value rule: '{}', ", rule)?;
+                parse_error_to_str(err, f)
+            }
+            ContextualParseError::UnsupportedKeyframePropertyDeclaration(decl, ref err) => {
+                write!(f, "Unsupported keyframe property declaration: '{}', ", decl)?;
+                parse_error_to_str(err, f)
+            }
+            ContextualParseError::InvalidRule(rule, ref err) => {
+                write!(f, "Invalid rule: '{}', ", rule)?;
+                parse_error_to_str(err, f)
+            }
+            ContextualParseError::UnsupportedRule(rule, ref err) => {
+                write!(f, "Unsupported rule: '{}', ", rule)?;
+                parse_error_to_str(err, f)
+            }
+            ContextualParseError::UnsupportedViewportDescriptorDeclaration(decl, ref err) => {
+                write!(f, "Unsupported @viewport descriptor declaration: '{}', ", decl)?;
+                parse_error_to_str(err, f)
+            }
+            ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(decl, ref err) => {
+                write!(f, "Unsupported @counter-style descriptor declaration: '{}', ", decl)?;
+                parse_error_to_str(err, f)
+            }
+            ContextualParseError::InvalidCounterStyleWithoutSymbols(ref system) => {
+                write!(f, "Invalid @counter-style rule: 'system: {}' without 'symbols'", system)
+            }
+            ContextualParseError::InvalidCounterStyleNotEnoughSymbols(ref system) => {
+                write!(f, "Invalid @counter-style rule: 'system: {}' less than two 'symbols'", system)
+            }
+            ContextualParseError::InvalidCounterStyleWithoutAdditiveSymbols => {
+                write!(f, "Invalid @counter-style rule: 'system: additive' without 'additive-symbols'")
+            }
+            ContextualParseError::InvalidCounterStyleExtendsWithSymbols => {
+                write!(f, "Invalid @counter-style rule: 'system: extends …' with 'symbols'")
+            }
+            ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols => {
+                write!(f, "Invalid @counter-style rule: 'system: extends …' with 'additive-symbols'")
+            }
         }
     }
 }
 
 /// A generic trait for an error reporter.
 pub trait ParseErrorReporter {
     /// Called when the style engine detects an error.
     ///
@@ -169,17 +193,17 @@ pub trait ParseErrorReporter {
 pub struct RustLogReporter;
 
 impl ParseErrorReporter for RustLogReporter {
     fn report_error(&self,
                     url: &UrlExtraData,
                     location: SourceLocation,
                     error: ContextualParseError) {
         if log_enabled!(log::LogLevel::Info) {
-            info!("Url:\t{}\n{}:{} {}", url.as_str(), location.line, location.column, error.to_string())
+            info!("Url:\t{}\n{}:{} {}", url.as_str(), location.line, location.column, error)
         }
     }
 }
 
 /// Error reporter which silently forgets errors
 pub struct NullReporter;
 
 impl ParseErrorReporter for NullReporter {
--- a/servo/components/style/gecko/media_queries.rs
+++ b/servo/components/style/gecko/media_queries.rs
@@ -394,17 +394,19 @@ impl MediaExpressionValue {
         match *self {
             MediaExpressionValue::Length(ref l) => l.to_css(dest),
             MediaExpressionValue::Integer(v) => v.to_css(dest),
             MediaExpressionValue::Float(v) => v.to_css(dest),
             MediaExpressionValue::BoolInteger(v) => {
                 dest.write_str(if v { "1" } else { "0" })
             },
             MediaExpressionValue::IntRatio(a, b) => {
-                write!(dest, "{}/{}", a, b)
+                a.to_css(dest)?;
+                dest.write_char('/')?;
+                b.to_css(dest)
             },
             MediaExpressionValue::Resolution(ref r) => r.to_css(dest),
             MediaExpressionValue::Ident(ref ident) => {
                 CssStringWriter::new(dest).write_str(ident)
             }
             MediaExpressionValue::Enumerated(value) => unsafe {
                 use std::{slice, str};
                 use std::os::raw::c_char;
--- a/servo/components/style/gecko/selector_parser.rs
+++ b/servo/components/style/gecko/selector_parser.rs
@@ -65,31 +65,33 @@ impl ToCss for NonTSPseudoClass {
         use fmt::Write;
         macro_rules! pseudo_class_serialize {
             (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
              string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*],
              keyword: [$(($k_css:expr, $k_name:ident, $k_gecko_type:tt, $k_state:tt, $k_flags:tt),)*]) => {
                 match *self {
                     $(NonTSPseudoClass::$name => concat!(":", $css),)*
                     $(NonTSPseudoClass::$s_name(ref s) => {
-                        write!(dest, ":{}(", $s_css)?;
+                        dest.write_str(concat!(":", $s_css, "("))?;
                         {
                             // FIXME(emilio): Avoid the extra allocation!
                             let mut css = CssStringWriter::new(dest);
 
                             // Discount the null char in the end from the
                             // string.
                             css.write_str(&String::from_utf16(&s[..s.len() - 1]).unwrap())?;
                         }
                         return dest.write_str(")")
                     }, )*
                     $(NonTSPseudoClass::$k_name(ref s) => {
                         // Don't include the terminating nul.
                         let value = String::from_utf16(&s[..s.len() - 1]).unwrap();
-                        return write!(dest, ":{}({})", $k_css, value)
+                        dest.write_str(concat!(":", $k_css, "("))?;
+                        dest.write_str(&value)?;
+                        return dest.write_char(')')
                     }, )*
                     NonTSPseudoClass::MozAny(ref selectors) => {
                         dest.write_str(":-moz-any(")?;
                         let mut iter = selectors.iter();
                         let first = iter.next().expect(":-moz-any must have at least 1 selector");
                         first.to_css(dest)?;
                         for selector in iter {
                             dest.write_str(", ")?;
--- a/servo/components/style/macros.rs
+++ b/servo/components/style/macros.rs
@@ -83,17 +83,17 @@ macro_rules! define_keyword_type {
         #[allow(missing_docs)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         #[derive(Animate, Clone, ComputeSquaredDistance, Copy, PartialEq)]
         #[derive(ToAnimatedZero, ToCss)]
         pub struct $name;
 
         impl fmt::Debug for $name {
             fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-                write!(f, $css)
+                f.write_str($css)
             }
         }
 
         impl $crate::parser::Parse for $name {
             fn parse<'i, 't>(_context: &$crate::parser::ParserContext,
                              input: &mut ::cssparser::Parser<'i, 't>)
                              -> Result<$name, ::style_traits::ParseError<'i>> {
                 input.expect_ident_matching($css).map(|_| $name).map_err(|e| e.into())
--- a/servo/components/style/media_queries.rs
+++ b/servo/components/style/media_queries.rs
@@ -91,45 +91,45 @@ impl MediaQuery {
 }
 
 impl ToCss for MediaQuery {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result
         where W: fmt::Write,
     {
         if let Some(qual) = self.qualifier {
             qual.to_css(dest)?;
-            write!(dest, " ")?;
+            dest.write_char(' ')?;
         }
 
         match self.media_type {
             MediaQueryType::All => {
                 // We need to print "all" if there's a qualifier, or there's
                 // just an empty list of expressions.
                 //
                 // Otherwise, we'd serialize media queries like "(min-width:
                 // 40px)" in "all (min-width: 40px)", which is unexpected.
                 if self.qualifier.is_some() || self.expressions.is_empty() {
-                    write!(dest, "all")?;
+                    dest.write_str("all")?;
                 }
             },
             MediaQueryType::Concrete(MediaType(ref desc)) => desc.to_css(dest)?,
         }
 
         if self.expressions.is_empty() {
             return Ok(());
         }
 
         if self.media_type != MediaQueryType::All || self.qualifier.is_some() {
-            write!(dest, " and ")?;
+            dest.write_str(" and ")?;
         }
 
         self.expressions[0].to_css(dest)?;
 
         for expr in self.expressions.iter().skip(1) {
-            write!(dest, " and ")?;
+            dest.write_str(" and ")?;
             expr.to_css(dest)?;
         }
         Ok(())
     }
 }
 
 /// http://dev.w3.org/csswg/mediaqueries-3/#media0
 #[derive(Clone, Debug, Eq, PartialEq)]
--- a/servo/components/style/properties/longhand/box.mako.rs
+++ b/servo/components/style/properties/longhand/box.mako.rs
@@ -626,17 +626,16 @@
                    spec="https://drafts.csswg.org/css-transforms/#propdef-transform">
     use app_units::Au;
     use values::computed::{LengthOrPercentageOrNumber as ComputedLoPoNumber, LengthOrNumber as ComputedLoN};
     use values::computed::{LengthOrPercentage as ComputedLoP, Length as ComputedLength};
     use values::generics::transform::Matrix;
     use values::specified::{Angle, Integer, Length, LengthOrPercentage};
     use values::specified::{LengthOrNumber, LengthOrPercentageOrNumber as LoPoNumber, Number};
     use style_traits::ToCss;
-    use style_traits::values::Css;
 
     use std::fmt;
 
     pub mod computed_value {
         use app_units::Au;
         use values::CSSFloat;
         use values::computed;
         use values::computed::{Length, LengthOrPercentage};
@@ -814,68 +813,109 @@
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             match *self {
                 SpecifiedOperation::Matrix(ref m) => m.to_css(dest),
                 SpecifiedOperation::PrefixedMatrix(ref m) => m.to_css(dest),
                 SpecifiedOperation::Matrix3D {
                     m11, m12, m13, m14,
                     m21, m22, m23, m24,
                     m31, m32, m33, m34,
-                    m41, m42, m43, m44 } => write!(
-                        dest, "matrix3d({}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {})",
-                        Css(m11), Css(m12), Css(m13), Css(m14),
-                        Css(m21), Css(m22), Css(m23), Css(m24),
-                        Css(m31), Css(m32), Css(m33), Css(m34),
-                        Css(m41), Css(m42), Css(m43), Css(m44)),
+                    m41, m42, m43, m44,
+                } => {
+                    serialize_function!(dest, matrix3d(
+                        m11, m12, m13, m14,
+                        m21, m22, m23, m24,
+                        m31, m32, m33, m34,
+                        m41, m42, m43, m44,
+                    ))
+                }
                 SpecifiedOperation::PrefixedMatrix3D {
                     m11, m12, m13, m14,
                     m21, m22, m23, m24,
                     m31, m32, m33, m34,
-                    ref m41, ref m42, ref m43, m44 } => write!(
-                        dest, "matrix3d({}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {})",
-                        Css(m11), Css(m12), Css(m13), Css(m14),
-                        Css(m21), Css(m22), Css(m23), Css(m24),
-                        Css(m31), Css(m32), Css(m33), Css(m34),
-                        Css(m41), Css(m42), Css(m43), Css(m44)),
-                SpecifiedOperation::Skew(ax, None) => write!(dest, "skew({})", Css(ax)),
-                SpecifiedOperation::Skew(ax, Some(ay)) => write!(dest, "skew({}, {})", Css(ax), Css(ay)),
-                SpecifiedOperation::SkewX(angle) => write!(dest, "skewX({})", Css(angle)),
-                SpecifiedOperation::SkewY(angle) => write!(dest, "skewY({})", Css(angle)),
-                SpecifiedOperation::Translate(ref tx, None) => write!(dest, "translate({})", Css(tx)),
+                    ref m41, ref m42, ref m43, m44,
+                } => {
+                    serialize_function!(dest, matrix3d(
+                        m11, m12, m13, m14,
+                        m21, m22, m23, m24,
+                        m31, m32, m33, m34,
+                        m41, m42, m43, m44,
+                    ))
+                }
+                SpecifiedOperation::Skew(ax, None) => {
+                    serialize_function!(dest, skew(ax))
+                }
+                SpecifiedOperation::Skew(ax, Some(ay)) => {
+                    serialize_function!(dest, skew(ax, ay))
+                }
+                SpecifiedOperation::SkewX(angle) => {
+                    serialize_function!(dest, skewX(angle))
+                }
+                SpecifiedOperation::SkewY(angle) => {
+                    serialize_function!(dest, skewY(angle))
+                }
+                SpecifiedOperation::Translate(ref tx, None) => {
+                    serialize_function!(dest, translate(tx))
+                }
                 SpecifiedOperation::Translate(ref tx, Some(ref ty)) => {
-                    write!(dest, "translate({}, {})", Css(tx), Css(ty))
-                },
-                SpecifiedOperation::TranslateX(ref tx) => write!(dest, "translateX({})", Css(tx)),
-                SpecifiedOperation::TranslateY(ref ty) => write!(dest, "translateY({})", Css(ty)),
-                SpecifiedOperation::TranslateZ(ref tz) => write!(dest, "translateZ({})", Css(tz)),
-                SpecifiedOperation::Translate3D(ref tx, ref ty, ref tz) => write!(
-                    dest, "translate3d({}, {}, {})", Css(tx), Css(ty), Css(tz)),
-                SpecifiedOperation::Scale(factor, None) => write!(dest, "scale({})", Css(factor)),
-                SpecifiedOperation::Scale(sx, Some(sy)) => write!(dest, "scale({}, {})", Css(sx), Css(sy)),
-                SpecifiedOperation::ScaleX(sx) => write!(dest, "scaleX({})", Css(sx)),
-                SpecifiedOperation::ScaleY(sy) => write!(dest, "scaleY({})", Css(sy)),
-                SpecifiedOperation::ScaleZ(sz) => write!(dest, "scaleZ({})", Css(sz)),
+                    serialize_function!(dest, translate(tx, ty))
+                }
+                SpecifiedOperation::TranslateX(ref tx) => {
+                    serialize_function!(dest, translateX(tx))
+                }
+                SpecifiedOperation::TranslateY(ref ty) => {
+                    serialize_function!(dest, translateY(ty))
+                }
+                SpecifiedOperation::TranslateZ(ref tz) => {
+                    serialize_function!(dest, translateZ(tz))
+                }
+                SpecifiedOperation::Translate3D(ref tx, ref ty, ref tz) => {
+                    serialize_function!(dest, translate3d(tx, ty, tz))
+                }
+                SpecifiedOperation::Scale(factor, None) => {
+                    serialize_function!(dest, scale(factor))
+                }
+                SpecifiedOperation::Scale(sx, Some(sy)) => {
+                    serialize_function!(dest, scale(sx, sy))
+                }
+                SpecifiedOperation::ScaleX(sx) => {
+                    serialize_function!(dest, scaleX(sx))
+                }
+                SpecifiedOperation::ScaleY(sy) => {
+                    serialize_function!(dest, scaleY(sy))
+                }
+                SpecifiedOperation::ScaleZ(sz) => {
+                    serialize_function!(dest, scaleZ(sz))
+                }
                 SpecifiedOperation::Scale3D(sx, sy, sz) => {
-                    write!(dest, "scale3d({}, {}, {})", Css(sx), Css(sy), Css(sz))
-                },
-                SpecifiedOperation::Rotate(theta) => write!(dest, "rotate({})", Css(theta)),
-                SpecifiedOperation::RotateX(theta) => write!(dest, "rotateX({})", Css(theta)),
-                SpecifiedOperation::RotateY(theta) => write!(dest, "rotateY({})", Css(theta)),
-                SpecifiedOperation::RotateZ(theta) => write!(dest, "rotateZ({})", Css(theta)),
-                SpecifiedOperation::Rotate3D(x, y, z, theta) => write!(
-                    dest, "rotate3d({}, {}, {}, {})",
-                    Css(x), Css(y), Css(z), Css(theta)),
-                SpecifiedOperation::Perspective(ref length) => write!(dest, "perspective({})", Css(length)),
+                    serialize_function!(dest, scale3d(sx, sy, sz))
+                }
+                SpecifiedOperation::Rotate(theta) => {
+                    serialize_function!(dest, rotate(theta))
+                }
+                SpecifiedOperation::RotateX(theta) => {
+                    serialize_function!(dest, rotateX(theta))
+                }
+                SpecifiedOperation::RotateY(theta) => {
+                    serialize_function!(dest, rotateY(theta))
+                }
+                SpecifiedOperation::RotateZ(theta) => {
+                    serialize_function!(dest, rotateZ(theta))
+                }
+                SpecifiedOperation::Rotate3D(x, y, z, theta) => {
+                    serialize_function!(dest, rotate3d(x, y, z, theta))
+                }
+                SpecifiedOperation::Perspective(ref length) => {
+                    serialize_function!(dest, perspective(length))
+                }
                 SpecifiedOperation::InterpolateMatrix { ref from_list, ref to_list, progress } => {
-                    write!(dest, "interpolatematrix({}, {}, {})",
-                           Css(from_list), Css(to_list), Css(progress))
-                },
+                    serialize_function!(dest, interpolatematrix(from_list, to_list, progress))
+                }
                 SpecifiedOperation::AccumulateMatrix { ref from_list, ref to_list, count } => {
-                    write!(dest, "accumulatematrix({}, {}, {})",
-                           Css(from_list), Css(to_list), Css(count))
+                    serialize_function!(dest, accumulatematrix(from_list, to_list, count))
                 }
             }
         }
     }
 
     #[derive(Clone, Debug, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub struct SpecifiedValue(Vec<SpecifiedOperation>);
--- a/servo/components/style/properties/longhand/font.mako.rs
+++ b/servo/components/style/properties/longhand/font.mako.rs
@@ -268,17 +268,17 @@ macro_rules! impl_gecko_keyword_conversi
                 match *self {
                     FontFamily::FamilyName(ref name) => name.to_css(dest),
 
                     // All generic values accepted by the parser are known to not require escaping.
                     FontFamily::Generic(ref name) => {
                         % if product == "gecko":
                             // We should treat -moz-fixed as monospace
                             if name == &atom!("-moz-fixed") {
-                                return write!(dest, "monospace");
+                                return dest.write_str("monospace");
                             }
                         % endif
 
                         write!(dest, "{}", name)
                     },
                 }
             }
         }
--- a/servo/components/style/properties/shorthand/box.mako.rs
+++ b/servo/components/style/properties/shorthand/box.mako.rs
@@ -171,17 +171,17 @@ macro_rules! try_parse_one {
             % for name in "property duration delay timing_function".split():
                 if len != self.transition_${name}.0.len() {
                     return Ok(());
                 }
             % endfor
 
             for i in 0..len {
                 if i != 0 {
-                    write!(dest, ", ")?;
+                    dest.write_str(", ")?;
                 }
                 self.transition_property.0[i].to_css(dest)?;
                 % for name in "duration timing_function delay".split():
                     dest.write_str(" ")?;
                     self.transition_${name}.0[i].to_css(dest)?;
                 % endfor
             }
             Ok(())
@@ -284,17 +284,17 @@ macro_rules! try_parse_one {
             % for name in props[1:]:
                 if len != self.animation_${name}.0.len() {
                     return Ok(())
                 }
             % endfor
 
             for i in 0..len {
                 if i != 0 {
-                    write!(dest, ", ")?;
+                    dest.write_str(", ")?;
                 }
 
                 % for name in props[1:]:
                     self.animation_${name}.0[i].to_css(dest)?;
                     dest.write_str(" ")?;
                 % endfor
                 self.animation_name.0[i].to_css(dest)?;
             }
--- a/servo/components/style/servo/media_queries.rs
+++ b/servo/components/style/servo/media_queries.rs
@@ -210,25 +210,24 @@ impl Expression {
         }
     }
 }
 
 impl ToCss for Expression {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result
         where W: fmt::Write,
     {
-        write!(dest, "(")?;
-        let (mm, l) = match self.0 {
-            ExpressionKind::Width(Range::Min(ref l)) => ("min-", l),
-            ExpressionKind::Width(Range::Max(ref l)) => ("max-", l),
-            ExpressionKind::Width(Range::Eq(ref l)) => ("", l),
+        let (s, l) = match self.0 {
+            ExpressionKind::Width(Range::Min(ref l)) => ("(min-width: ", l),
+            ExpressionKind::Width(Range::Max(ref l)) => ("(max-width: ", l),
+            ExpressionKind::Width(Range::Eq(ref l)) => ("(width: ", l),
         };
-        write!(dest, "{}width: ", mm)?;
+        dest.write_str(s)?;
         l.to_css(dest)?;
-        write!(dest, ")")
+        dest.write_char(')')
     }
 }
 
 /// An enumeration that represents a ranged value.
 ///
 /// Only public for testing, implementation details of `Expression` may change
 /// for Stylo.
 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
--- a/servo/components/style/stylesheets/font_feature_values_rule.rs
+++ b/servo/components/style/stylesheets/font_feature_values_rule.rs
@@ -63,17 +63,17 @@ impl Parse for SingleValue {
             Token::Number { int_value: Some(v), .. } if v >= 0 => Ok(SingleValue(v as u32)),
             ref t => Err(BasicParseError::UnexpectedToken(t.clone()).into()),
         }
     }
 }
 
 impl ToCss for SingleValue {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        write!(dest, "{}", self.0)
+        self.0.to_css(dest)
     }
 }
 
 #[cfg(feature = "gecko")]
 impl ToGeckoFontFeatureValues for SingleValue {
     fn to_gecko_font_feature_values(&self, array: &mut nsTArray<u32>) {
         unsafe { array.set_len_pod(1); }
         array[0] = self.0 as u32;
@@ -100,19 +100,20 @@ impl Parse for PairValues {
             // It can be just one value.
             Err(_) => Ok(PairValues(first, None))
         }
     }
 }
 
 impl ToCss for PairValues {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
-        write!(dest, "{}", self.0)?;
+        self.0.to_css(dest)?;
         if let Some(second) = self.1 {
-            write!(dest, " {}", second)?;
+            dest.write_char(' ')?;
+            second.to_css(dest)?;
         }
         Ok(())
     }
 }
 
 #[cfg(feature = "gecko")]
 impl ToGeckoFontFeatureValues for PairValues {
     fn to_gecko_font_feature_values(&self, array: &mut nsTArray<u32>) {
@@ -153,20 +154,20 @@ impl Parse for VectorValues {
     }
 }
 
 impl ToCss for VectorValues {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         let mut iter = self.0.iter();
         let first = iter.next();
         if let Some(first) = first {
-            write!(dest, "{}", first)?;
+            first.to_css(dest)?;
             for value in iter {
-                dest.write_str(" ")?;
-                write!(dest, "{}", value)?;
+                dest.write_char(' ')?;
+                value.to_css(dest)?;
             }
         }
         Ok(())
     }
 }
 
 #[cfg(feature = "gecko")]
 impl ToGeckoFontFeatureValues for VectorValues {
--- a/servo/components/style/stylesheets/keyframes_rule.rs
+++ b/servo/components/style/stylesheets/keyframes_rule.rs
@@ -150,17 +150,17 @@ impl KeyframePercentage {
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub struct KeyframeSelector(Vec<KeyframePercentage>);
 
 impl ToCss for KeyframeSelector {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         let mut iter = self.0.iter();
         iter.next().unwrap().to_css(dest)?;
         for percentage in iter {
-            write!(dest, ", ")?;
+            dest.write_str(", ")?;
             percentage.to_css(dest)?;
         }
         Ok(())
     }
 }
 
 impl KeyframeSelector {
     /// Return the list of percentages this selector contains.
--- a/servo/components/style/stylesheets/viewport_rule.rs
+++ b/servo/components/style/stylesheets/viewport_rule.rs
@@ -146,17 +146,17 @@ pub enum ViewportLength {
 }
 
 impl ToCss for ViewportLength {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result
         where W: fmt::Write,
     {
         match *self {
             ViewportLength::Specified(ref length) => length.to_css(dest),
-            ViewportLength::ExtendToZoom => write!(dest, "extend-to-zoom"),
+            ViewportLength::ExtendToZoom => dest.write_str("extend-to-zoom"),
         }
     }
 }
 
 impl FromMeta for ViewportLength {
     fn from_meta(value: &str) -> Option<ViewportLength> {
         macro_rules! specified {
             ($value:expr) => {
--- a/servo/components/style/values/generics/mod.rs
+++ b/servo/components/style/values/generics/mod.rs
@@ -221,17 +221,20 @@ pub struct FontSettingTagInt(pub u32);
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub struct FontSettingTagFloat(pub f32);
 
 impl ToCss for FontSettingTagInt {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         match self.0 {
             1 => Ok(()),
             0 => dest.write_str(" off"),
-            x => write!(dest, " {}", x)
+            x => {
+                dest.write_char(' ')?;
+                x.to_css(dest)
+            }
         }
     }
 }
 
 impl Parse for FontSettingTagInt {
     fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
         if let Ok(value) = input.try(|input| input.expect_integer()) {
             // handle integer, throw if it is negative
--- a/servo/components/style/values/specified/length.rs
+++ b/servo/components/style/values/specified/length.rs
@@ -419,17 +419,21 @@ pub enum NoCalcLength {
 
 impl ToCss for NoCalcLength {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         match *self {
             NoCalcLength::Absolute(length) => length.to_css(dest),
             NoCalcLength::FontRelative(length) => length.to_css(dest),
             NoCalcLength::ViewportPercentage(length) => length.to_css(dest),
             /* This should only be reached from style dumping code */
-            NoCalcLength::ServoCharacterWidth(CharacterWidth(i)) => write!(dest, "CharWidth({})", i),
+            NoCalcLength::ServoCharacterWidth(CharacterWidth(i)) => {
+                dest.write_str("CharWidth(")?;
+                i.to_css(dest)?;
+                dest.write_char(')')
+            }
             #[cfg(feature = "gecko")]
             NoCalcLength::Physical(length) => length.to_css(dest),
         }
     }
 }
 
 impl Mul<CSSFloat> for NoCalcLength {
     type Output = NoCalcLength;
--- a/servo/components/style_traits/values.rs
+++ b/servo/components/style_traits/values.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/. */
 
 //! Helper types and traits for the handling of CSS values.
 
 use app_units::Au;
 use cssparser::{BasicParseError, ParseError, Parser, Token, UnicodeRange, serialize_string};
+use cssparser::ToCss as CssparserToCss;
 use std::fmt::{self, Write};
 
 /// Serialises a value according to its CSS representation.
 ///
 /// This trait is implemented for `str` and its friends, serialising the string
 /// contents as a CSS quoted string.
 ///
 /// This trait is derivable with `#[derive(ToCss)]`, with the following behaviour:
@@ -64,16 +65,34 @@ where
     T: ToCss,
 {
     #[inline]
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: Write {
         self.as_ref().map_or(Ok(()), |value| value.to_css(dest))
     }
 }
 
+#[macro_export]
+macro_rules! serialize_function {
+    ($dest: expr, $name: ident($( $arg: expr, )+)) => {
+        serialize_function!($dest, $name($($arg),+))
+    };
+    ($dest: expr, $name: ident($first_arg: expr $( , $arg: expr )*)) => {
+        {
+            $dest.write_str(concat!(stringify!($name), "("))?;
+            $first_arg.to_css($dest)?;
+            $(
+                $dest.write_str(", ")?;
+                $arg.to_css($dest)?;
+            )*
+            $dest.write_char(')')
+        }
+    }
+}
+
 /// Convenience wrapper to serialise CSS values separated by a given string.
 pub struct SequenceWriter<'a, W> {
     writer: TrackedWriter<W>,
     separator: &'a str,
 }
 
 impl<'a, W> SequenceWriter<'a, W>
 where
@@ -321,17 +340,18 @@ impl<T> ToCss for Box<T> where T: ?Sized
         where W: Write,
     {
         (**self).to_css(dest)
     }
 }
 
 impl ToCss for Au {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: Write {
-        write!(dest, "{}px", self.to_f64_px())
+        self.to_f64_px().to_css(dest)?;
+        dest.write_str("px")
     }
 }
 
 macro_rules! impl_to_css_for_predefined_type {
     ($name: ty) => {
         impl<'a> ToCss for $name {
             fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: Write {
                 ::cssparser::ToCss::to_css(self, dest)
@@ -526,19 +546,8 @@ pub mod specified {
             match *self {
                 AllowedNumericType::NonNegative if val < 0. => 0.,
                 AllowedNumericType::AtLeastOne if val < 1. => 1.,
                 _ => val,
             }
         }
     }
 }
-
-
-/// Wrap CSS types for serialization with `write!` or `format!` macros.
-/// Used by ToCss of SpecifiedOperation.
-pub struct Css<T>(pub T);
-
-impl<T: ToCss> fmt::Display for Css<T> {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        self.0.to_css(f)
-    }
-}
--- a/servo/components/style_traits/viewport.rs
+++ b/servo/components/style_traits/viewport.rs
@@ -40,29 +40,41 @@ pub struct ViewportConstraints {
     /// https://drafts.csswg.org/css-device-adapt/#orientation-desc
     pub orientation: Orientation
 }
 
 impl ToCss for ViewportConstraints {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result
         where W: fmt::Write
     {
-        write!(dest, "@viewport {{")?;
-        write!(dest, " width: {}px;", self.size.width)?;
-        write!(dest, " height: {}px;", self.size.height)?;
-        write!(dest, " zoom: {};", self.initial_zoom.get())?;
+        dest.write_str("@viewport { width: ")?;
+        self.size.width.to_css(dest)?;
+
+        dest.write_str("px; height: ")?;
+        self.size.height.to_css(dest)?;
+
+        dest.write_str("px; zoom: ")?;
+        self.initial_zoom.get().to_css(dest)?;
+
         if let Some(min_zoom) = self.min_zoom {
-            write!(dest, " min-zoom: {};", min_zoom.get())?;
+            dest.write_str("; min-zoom: ")?;
+            min_zoom.get().to_css(dest)?;
         }
+
         if let Some(max_zoom) = self.max_zoom {
-            write!(dest, " max-zoom: {};", max_zoom.get())?;
+            dest.write_str("; max-zoom: ")?;
+            max_zoom.get().to_css(dest)?;
         }
-        write!(dest, " user-zoom: ")?; self.user_zoom.to_css(dest)?;
-        write!(dest, "; orientation: ")?; self.orientation.to_css(dest)?;
-        write!(dest, "; }}")
+
+        dest.write_str("; user-zoom: ")?;
+        self.user_zoom.to_css(dest)?;
+
+        dest.write_str("; orientation: ")?;
+        self.orientation.to_css(dest)?;
+        dest.write_str("; }")
     }
 }
 
 /// https://drafts.csswg.org/css-device-adapt/#descdef-viewport-zoom
 #[derive(Clone, Copy, Debug, PartialEq)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub enum Zoom {
     /// A number value.
@@ -73,19 +85,22 @@ pub enum Zoom {
     Auto,
 }
 
 impl ToCss for Zoom {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result
         where W: fmt::Write,
     {
         match *self {
-            Zoom::Number(number) => write!(dest, "{}", number),
-            Zoom::Percentage(percentage) => write!(dest, "{}%", percentage * 100.),
-            Zoom::Auto => write!(dest, "auto")
+            Zoom::Number(number) => number.to_css(dest),
+            Zoom::Auto => dest.write_str("auto"),
+            Zoom::Percentage(percentage) => {
+                (percentage * 100.).to_css(dest)?;
+                dest.write_char('%')
+            }
         }
     }
 }
 
 impl Zoom {
     /// Parse a zoom value per:
     ///
     /// https://drafts.csswg.org/css-device-adapt/#descdef-viewport-zoom
--- a/servo/ports/geckolib/error_reporter.rs
+++ b/servo/ports/geckolib/error_reporter.rs
@@ -1,18 +1,18 @@
 /* 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/. */
 
 //! Wrapper around Gecko's CSS error reporting mechanism.
 
 #![allow(unsafe_code)]
 
+use cssparser::{CowRcStr, serialize_identifier, ToCss};
 use cssparser::{SourceLocation, ParseError as CssParseError, Token, BasicParseError};
-use cssparser::CowRcStr;
 use selectors::parser::SelectorParseError;
 use std::ptr;
 use style::error_reporting::{ParseErrorReporter, ContextualParseError};
 use style::gecko_bindings::bindings::{Gecko_CreateCSSErrorReporter, Gecko_DestroyCSSErrorReporter};
 use style::gecko_bindings::bindings::Gecko_ReportUnexpectedCSSError;
 use style::gecko_bindings::structs::{Loader, ServoStyleSheet, nsIURI};
 use style::gecko_bindings::structs::ErrorReporter as GeckoErrorReporter;
 use style::gecko_bindings::structs::URLExtraData as RawUrlExtraData;
@@ -48,187 +48,78 @@ enum ErrorString<'a> {
     Ident(CowRcStr<'a>),
     UnexpectedToken(Token<'a>),
 }
 
 impl<'a> ErrorString<'a> {
     fn into_str(self) -> CowRcStr<'a> {
         match self {
             ErrorString::Snippet(s) => s,
-            ErrorString::Ident(i) => escape_css_ident(&i).into(),
-            ErrorString::UnexpectedToken(t) => token_to_str(t).into(),
-        }
-    }
-}
-
-// This is identical to the behaviour of cssparser::serialize_identifier, except that
-// it uses numerical escapes for a larger set of characters.
-fn escape_css_ident(ident: &str) -> String {
-    // The relevant parts of the CSS grammar are:
-    //   ident    ([-]?{nmstart}|[-][-]){nmchar}*
-    //   nmstart  [_a-z]|{nonascii}|{escape}
-    //   nmchar   [_a-z0-9-]|{nonascii}|{escape}
-    //   nonascii [^\0-\177]
-    //   escape   {unicode}|\\[^\n\r\f0-9a-f]
-    //   unicode  \\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?
-    // from http://www.w3.org/TR/CSS21/syndata.html#tokenization but
-    // modified for idents by
-    // http://dev.w3.org/csswg/cssom/#serialize-an-identifier and
-    // http://dev.w3.org/csswg/css-syntax/#would-start-an-identifier
-    if ident.is_empty() {
-        return ident.into()
-    }
-
-    let mut escaped = String::with_capacity(ident.len());
-
-    // A leading dash does not need to be escaped as long as it is not the
-    // *only* character in the identifier.
-    let mut iter = ident.chars().peekable();
-    if iter.peek() == Some(&'-') {
-        if ident.len() == 1 {
-            return "\\-".into();
-        }
-
-        escaped.push('-');
-        // Skip the first character.
-        let _ = iter.next();
-    }
-
-    // Escape a digit at the start (including after a dash),
-    // numerically.  If we didn't escape it numerically, it would get
-    // interpreted as a numeric escape for the wrong character.
-    if iter.peek().map_or(false, |&c| '0' <= c && c <= '9') {
-        let ch = iter.next().unwrap();
-        escaped.push_str(&format!("\\{:x} ", ch as u32));
-    }
-
-    while let Some(ch) = iter.next() {
-        if ch == '\0' {
-            escaped.push_str("\u{FFFD}");
-        } else if ch < (0x20 as char) || (0x7f as char <= ch && ch < (0xA0 as char)) {
-            // Escape U+0000 through U+001F and U+007F through U+009F numerically.
-            escaped.push_str(&format!("\\{:x} ", ch as u32));
-        } else {
-            // Escape ASCII non-identifier printables as a backslash plus
-            // the character.
-            if (ch < (0x7F as char)) &&
-                ch != '_' && ch != '-' &&
-                (ch < '0' || '9' < ch) &&
-                (ch < 'A' || 'Z' < ch) &&
-                (ch < 'a' || 'z' < ch)
-            {
-                escaped.push('\\');
+            ErrorString::UnexpectedToken(t) => t.to_css_string().into(),
+            ErrorString::Ident(i) => {
+                let mut s = String::new();
+                serialize_identifier(&i, &mut s).unwrap();
+                s.into()
             }
-            escaped.push(ch);
         }
     }
-
-    escaped
-}
-
-// This is identical to the behaviour of cssparser::CssStringWriter, except that
-// the characters between 0x7F and 0xA0 as numerically escaped as well.
-fn escape_css_string(s: &str) -> String {
-    let mut escaped = String::new();
-    for ch in s.chars() {
-        if ch < ' ' || (ch >= (0x7F as char) && ch < (0xA0 as char)) {
-            escaped.push_str(&format!("\\{:x} ", ch as u32));
-        } else {
-            if ch == '"' || ch == '\'' || ch == '\\' {
-                // Escape backslash and quote characters symbolically.
-                // It's not technically necessary to escape the quote
-                // character that isn't being used to delimit the string,
-                // but we do it anyway because that makes testing simpler.
-                escaped.push('\\');
-            }
-            escaped.push(ch);
-        }
-    }
-    escaped
-}
-
-fn token_to_str<'a>(t: Token<'a>) -> String {
-    match t {
-        Token::Ident(i) => escape_css_ident(&i),
-        Token::AtKeyword(kw) => format!("@{}", escape_css_ident(&kw)),
-        Token::Hash(h) | Token::IDHash(h) => format!("#{}", escape_css_ident(&h)),
-        Token::QuotedString(s) => format!("'{}'", escape_css_string(&s)),
-        Token::UnquotedUrl(u) => format!("'{}'", escape_css_string(&u)),
-        Token::Delim(d) => d.to_string(),
-        Token::Number { int_value: Some(i), .. } => i.to_string(),
-        Token::Number { value, .. } => value.to_string(),
-        Token::Percentage { int_value: Some(i), .. } => i.to_string(),
-        Token::Percentage { unit_value, .. } => unit_value.to_string(),
-        Token::Dimension { int_value: Some(i), ref unit, .. } =>
-            format!("{}{}", i, escape_css_ident(&*unit)),
-        Token::Dimension { value, ref unit, .. } =>
-            format!("{}{}", value, escape_css_ident(&*unit)),
-        Token::WhiteSpace(s) => s.into(),
-        Token::Comment(_) => "comment".into(),
-        Token::Colon => ":".into(),
-        Token::Semicolon => ";".into(),
-        Token::Comma => ",".into(),
-        Token::IncludeMatch => "~=".into(),
-        Token::DashMatch => "|=".into(),
-        Token::PrefixMatch => "^=".into(),
-        Token::SuffixMatch => "$=".into(),
-        Token::SubstringMatch => "*=".into(),
-        Token::Column => "||".into(),
-        Token::CDO => "<!--".into(),
-        Token::CDC => "-->".into(),
-        Token::Function(f) => format!("{}(", escape_css_ident(&f)),
-        Token::ParenthesisBlock => "(".into(),
-        Token::SquareBracketBlock => "[".into(),
-        Token::CurlyBracketBlock => "{".into(),
-        Token::BadUrl(url) => format!("url('{}", escape_css_string(&url)).into(),
-        Token::BadString(s) => format!("'{}", escape_css_string(&s)).into(),
-        Token::CloseParenthesis => "unmatched close parenthesis".into(),
-        Token::CloseSquareBracket => "unmatched close square bracket".into(),
-        Token::CloseCurlyBracket => "unmatched close curly bracket".into(),
-    }
 }
 
 enum Action {
     Nothing,
     Skip,
     Drop,
 }
 
 trait ErrorHelpers<'a> {
     fn error_data(self) -> (CowRcStr<'a>, ParseError<'a>);
     fn error_params(self) -> ErrorParams<'a>;
     fn to_gecko_message(&self) -> (Option<&'static [u8]>, &'static [u8], Action);
 }
 
 fn extract_error_param<'a>(err: ParseError<'a>) -> Option<ErrorString<'a>> {
     Some(match err {
-        CssParseError::Basic(BasicParseError::UnexpectedToken(t)) =>
-            ErrorString::UnexpectedToken(t),
+        CssParseError::Basic(BasicParseError::UnexpectedToken(t)) => {
+            ErrorString::UnexpectedToken(t)
+        }
 
         CssParseError::Basic(BasicParseError::AtRuleInvalid(i)) |
         CssParseError::Custom(SelectorParseError::Custom(
-            StyleParseError::UnsupportedAtRule(i))) =>
-            ErrorString::Snippet(format!("@{}", escape_css_ident(&i)).into()),
+            StyleParseError::UnsupportedAtRule(i)
+        )) => {
+            let mut s = String::from("@");
+            serialize_identifier(&i, &mut s).unwrap();
+            ErrorString::Snippet(s.into())
+        }
 
         CssParseError::Custom(SelectorParseError::Custom(
             StyleParseError::PropertyDeclaration(
-                PropertyDeclarationParseError::InvalidValue(property, None)))) =>
-            ErrorString::Snippet(property),
+                PropertyDeclarationParseError::InvalidValue(property, None)
+            )
+        )) => {
+            ErrorString::Snippet(property)
+        }
 
-        CssParseError::Custom(SelectorParseError::UnexpectedIdent(ident)) =>
-            ErrorString::Ident(ident),
+        CssParseError::Custom(SelectorParseError::UnexpectedIdent(ident)) => {
+            ErrorString::Ident(ident)
+        }
 
         CssParseError::Custom(SelectorParseError::Custom(
             StyleParseError::PropertyDeclaration(
-                PropertyDeclarationParseError::UnknownProperty(property)))) =>
-            ErrorString::Ident(property),
+                PropertyDeclarationParseError::UnknownProperty(property)
+            )
+        )) => {
+            ErrorString::Ident(property)
+        }
 
         CssParseError::Custom(SelectorParseError::Custom(
-            StyleParseError::UnexpectedTokenWithinNamespace(token))) =>
-            ErrorString::UnexpectedToken(token),
+            StyleParseError::UnexpectedTokenWithinNamespace(token)
+        )) => {
+            ErrorString::UnexpectedToken(token)
+        }
 
         _ => return None,
     })
 }
 
 fn extract_value_error_param<'a>(err: ValueParseError<'a>) -> ErrorString<'a> {
     match err {
         ValueParseError::InvalidColor(t) => ErrorString::UnexpectedToken(t),
--- a/servo/tests/unit/style/rule_tree/bench.rs
+++ b/servo/tests/unit/style/rule_tree/bench.rs
@@ -17,17 +17,17 @@ use style::thread_state;
 use test::{self, Bencher};
 
 struct ErrorringErrorReporter;
 impl ParseErrorReporter for ErrorringErrorReporter {
     fn report_error(&self,
                     url: &ServoUrl,
                     location: SourceLocation,
                     error: ContextualParseError) {
-        panic!("CSS error: {}\t\n{}:{} {}", url.as_str(), location.line, location.column, error.to_string());
+        panic!("CSS error: {}\t\n{}:{} {}", url.as_str(), location.line, location.column, error);
     }
 }
 
 struct AutoGCRuleTree<'a>(&'a RuleTree);
 
 impl<'a> AutoGCRuleTree<'a> {
     fn new(r: &'a RuleTree) -> Self {
         AutoGCRuleTree(r)
--- a/servo/tests/unit/style/stylesheets.rs
+++ b/servo/tests/unit/style/stylesheets.rs
@@ -275,17 +275,17 @@ impl ParseErrorReporter for CSSInvalidEr
                     location: SourceLocation,
                     error: ContextualParseError) {
         let mut errors = self.errors.lock().unwrap();
         errors.push(
             CSSError{
                 url: url.clone(),
                 line: location.line,
                 column: location.column,
-                message: error.to_string()
+                message: error.to_string(),
             }
         );
     }
 }
 
 
 #[test]
 fn test_report_error_stylesheet() {
--- a/servo/tests/unit/style/viewport.rs
+++ b/servo/tests/unit/style/viewport.rs
@@ -6,17 +6,17 @@ use cssparser::{Parser, ParserInput};
 use euclid::ScaleFactor;
 use euclid::TypedSize2D;
 use media_queries::CSSErrorReporterTest;
 use servo_arc::Arc;
 use servo_config::prefs::{PREFS, PrefValue};
 use servo_url::ServoUrl;
 use style::context::QuirksMode;
 use style::media_queries::{Device, MediaList, MediaType};
-use style::parser::{Parse, ParserContext, ParserErrorContext};
+use style::parser::{ParserContext, ParserErrorContext};
 use style::shared_lock::SharedRwLock;
 use style::stylesheets::{CssRuleType, Stylesheet, StylesheetInDocument, Origin};
 use style::stylesheets::viewport_rule::*;
 use style::values::specified::LengthOrPercentageOrAuto::{self, Auto};
 use style::values::specified::NoCalcLength::{self, ViewportPercentage};
 use style::values::specified::ViewportPercentageLength::Vw;
 use style_traits::{PARSING_MODE_DEFAULT, PinchZoomFactor};
 use style_traits::viewport::*;
--- a/servo/tests/unit/stylo/lib.rs
+++ b/servo/tests/unit/stylo/lib.rs
@@ -7,17 +7,16 @@ extern crate cssparser;
 extern crate env_logger;
 extern crate geckoservo;
 #[macro_use] extern crate log;
 extern crate selectors;
 #[macro_use] extern crate size_of_test;
 #[macro_use] extern crate style;
 extern crate style_traits;
 
-mod sanity_checks;
 #[cfg(target_pointer_width = "64")]
 mod size_of;
 mod specified_values;
 
 mod servo_function_signatures;
 
 use style::*;
 
deleted file mode 100644
--- a/servo/tests/unit/stylo/sanity_checks.rs
+++ /dev/null
@@ -1,28 +0,0 @@
-/* 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/. */
-
-//! Different static asserts that ensure the build does what it's expected to.
-//!
-//! TODO: maybe cfg(test) this?
-
-#![allow(unused_imports)]
-
-use std::mem;
-
-macro_rules! check_enum_value {
-    ($a:expr, $b:expr) => {
-        unsafe {
-            mem::transmute::<[u32; $a as usize],
-                             [u32; $b as usize]>([0; $a as usize]);
-        }
-    }
-}
-
-// NB: It's a shame we can't do this statically with bitflags, but no
-// const-fn and no other way to access the numerical value :-(
-macro_rules! check_enum_value_non_static {
-    ($a:expr, $b:expr) => {
-        assert_eq!($a.0 as usize, $b as usize);
-    }
-}
--- a/servo/tests/unit/stylo/specified_values.rs
+++ b/servo/tests/unit/stylo/specified_values.rs
@@ -1,15 +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/. */
 
 use style;
 
 #[cfg(all(test, target_pointer_width = "64"))]
+#[test]
 fn size_of_specified_values() {
     use std::mem::size_of;
     let threshold = 24;
 
     let mut bad_properties = vec![];
 
     macro_rules! check_property {
         ( $( { $name: ident, $boxed: expr } )+ ) => {