No bug - Revendor rust dependencies
authorServo VCS Sync <servo-vcs-sync@mozilla.com>
Tue, 10 Oct 2017 19:14:26 +0000
changeset 428056 eb7f98e69e26b838334556015835471aeaafa757
parent 428055 c0005a5aa643e2894a639dda34bf9e6250f55c13
child 428057 7af19bab721c6081171964adc48bbc201e78f4c8
push id97
push userfmarier@mozilla.com
push dateSat, 14 Oct 2017 01:12:59 +0000
milestone58.0a1
No bug - Revendor rust dependencies
third_party/rust/cssparser/.cargo-checksum.json
third_party/rust/cssparser/Cargo.toml
third_party/rust/cssparser/src/color.rs
third_party/rust/cssparser/src/lib.rs
third_party/rust/cssparser/src/nth.rs
third_party/rust/cssparser/src/parser.rs
third_party/rust/cssparser/src/rules_and_declarations.rs
third_party/rust/cssparser/src/size_of_tests.rs
third_party/rust/cssparser/src/tests.rs
third_party/rust/cssparser/src/unicode_range.rs
toolkit/library/gtest/rust/Cargo.lock
toolkit/library/rust/Cargo.lock
--- a/third_party/rust/cssparser/.cargo-checksum.json
+++ b/third_party/rust/cssparser/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",".travis.yml":"f1fb4b65964c81bc1240544267ea334f554ca38ae7a74d57066f4d47d2b5d568","Cargo.toml":"7d35a8de9f311d00bd9dfa96e533e3db0584f914b1798d4937ddf1ff6baff74e","LICENSE":"fab3dd6bdab226f1c08630b1dd917e11fcb4ec5e1e020e2c16f83a0a13863e85","README.md":"c5781e673335f37ed3d7acb119f8ed33efdf6eb75a7094b7da2abe0c3230adb8","build.rs":"950bcc47a196f07f99f59637c28cc65e02a885130011f90a2b2608248b4724a2","build/match_byte.rs":"2c84b8ca5884347d2007f49aecbd85b4c7582085526e2704399817249996e19b","docs/.nojekyll":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","docs/404.html":"025861f76f8d1f6d67c20ab624c6e418f4f824385e2dd8ad8732c4ea563c6a2e","docs/index.html":"025861f76f8d1f6d67c20ab624c6e418f4f824385e2dd8ad8732c4ea563c6a2e","src/color.rs":"422a2e934b06a2cca7beef7afeab42bdca81a73eb27afcbdb3d2a98db892590b","src/cow_rc_str.rs":"541216f8ef74ee3cc5cbbc1347e5f32ed66588c401851c9a7d68b867aede1de0","src/from_bytes.rs":"331fe63af2123ae3675b61928a69461b5ac77799fff3ce9978c55cf2c558f4ff","src/lib.rs":"37aec41c81021cd4cc8f34491ee75de2e8340feada2d0096b107597fc4ac485d","src/macros.rs":"adb9773c157890381556ea83d7942dcc676f99eea71abbb6afeffee1e3f28960","src/nth.rs":"17f014269795809cbbee0462eb1a0ecfb73a6e9f3e60d19971430cfc9662911a","src/parser.rs":"69ef59a1c28e49b89cfc67effc7408e59af63c416128889b5e8d48022e17c06d","src/rules_and_declarations.rs":"962f59aab8030b0d1202859ff841ed6254ce4bd4159eee5e915ccdf4b802f4d5","src/serializer.rs":"3dc1d9441c9602f26d34c0b209f6d3954814815b780c068f9dc3514d808de3d9","src/size_of_tests.rs":"4b5e4f6fa209bfe4ff8e44fdf10535a5eba5b7b644afac7ee32b60923c23460c","src/tests.rs":"3572f340d9e38b3482f5f8e6bdbabc99c8ff9caeb8f92cdb7d266a1e5110abb1","src/tokenizer.rs":"c46d540cf589de8a2a2a0d918575a257de4d6a55af78e87b0d9c1345032b80ef","src/unicode_range.rs":"fbbd0f4b393944699730a6b0f945b2b2376fcea61fce2ea37190fb287793021a"},"package":"89af8ca7a36c8c591a9ff56ffb88ea2597095d8c1b834af9ca5be5032dcfe06e"}
\ No newline at end of file
+{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",".travis.yml":"f1fb4b65964c81bc1240544267ea334f554ca38ae7a74d57066f4d47d2b5d568","Cargo.toml":"fac23b751b4a88d7102e488a4417d695099287c6c88541e64f11dd8bb52d710f","LICENSE":"fab3dd6bdab226f1c08630b1dd917e11fcb4ec5e1e020e2c16f83a0a13863e85","README.md":"c5781e673335f37ed3d7acb119f8ed33efdf6eb75a7094b7da2abe0c3230adb8","build.rs":"950bcc47a196f07f99f59637c28cc65e02a885130011f90a2b2608248b4724a2","build/match_byte.rs":"2c84b8ca5884347d2007f49aecbd85b4c7582085526e2704399817249996e19b","docs/.nojekyll":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","docs/404.html":"025861f76f8d1f6d67c20ab624c6e418f4f824385e2dd8ad8732c4ea563c6a2e","docs/index.html":"025861f76f8d1f6d67c20ab624c6e418f4f824385e2dd8ad8732c4ea563c6a2e","src/color.rs":"c92cdb9dcabcc63de005478ca770858b330f02ee712f9fd21b31eeb7472df72d","src/cow_rc_str.rs":"541216f8ef74ee3cc5cbbc1347e5f32ed66588c401851c9a7d68b867aede1de0","src/from_bytes.rs":"331fe63af2123ae3675b61928a69461b5ac77799fff3ce9978c55cf2c558f4ff","src/lib.rs":"a0433a3cf2147e09a8b2471f50ca136d3494d7fc764417b43dc146e2dd5a09ff","src/macros.rs":"adb9773c157890381556ea83d7942dcc676f99eea71abbb6afeffee1e3f28960","src/nth.rs":"0450aa21ecc4639cbe350a143598d570ed9d2b03ac21b9cb795577e0f92f3be6","src/parser.rs":"3533ece7309e694eccf4858435ad090fc6bf98123d443691572931c4323f44d4","src/rules_and_declarations.rs":"eda11baa27c80df932e43460ec6864ff8c3367aaf7673c15410443097efe2506","src/serializer.rs":"3dc1d9441c9602f26d34c0b209f6d3954814815b780c068f9dc3514d808de3d9","src/size_of_tests.rs":"ccbbd8601646798f87b57877135346bc270736560d2c85b5e5ae8cd6492efd65","src/tests.rs":"e8c2593ee6e93e10fd2bb6a9b8b872d280577ba132c04a533f76c58c7233bf70","src/tokenizer.rs":"c46d540cf589de8a2a2a0d918575a257de4d6a55af78e87b0d9c1345032b80ef","src/unicode_range.rs":"c1c4ed2493e09d248c526ce1ef8575a5f8258da3962b64ffc814ef3bdf9780d0"},"package":"44313341610282488e1156ad1fedebca51c54766c87a041d0287b10499c04ba1"}
\ No newline at end of file
--- a/third_party/rust/cssparser/Cargo.toml
+++ b/third_party/rust/cssparser/Cargo.toml
@@ -7,62 +7,62 @@
 #
 # If you believe there's an error in this file please file an
 # issue against the rust-lang/cargo repository. If you're
 # editing this file be aware that the upstream Cargo.toml
 # will likely look very different (and much more reasonable)
 
 [package]
 name = "cssparser"
-version = "0.21.3"
+version = "0.22.0"
 authors = ["Simon Sapin <simon.sapin@exyr.org>"]
 build = "build.rs"
 exclude = ["src/css-parsing-tests/**", "src/big-data-url.css"]
 description = "Rust implementation of CSS Syntax Level 3"
 documentation = "https://docs.rs/cssparser/"
 readme = "README.md"
 keywords = ["css", "syntax", "parser"]
 license = "MPL-2.0"
 repository = "https://github.com/servo/rust-cssparser"
-[dependencies.matches]
-version = "0.1"
-
-[dependencies.phf]
-version = "0.7"
-
-[dependencies.cssparser-macros]
+[dependencies.itoa]
 version = "0.3"
 
 [dependencies.serde]
 version = "1.0"
 optional = true
 
-[dependencies.dtoa-short]
-version = "0.3"
+[dependencies.procedural-masquerade]
+version = "0.1"
+
+[dependencies.matches]
+version = "0.1"
 
 [dependencies.heapsize]
 version = ">= 0.3, < 0.5"
 optional = true
 
 [dependencies.smallvec]
 version = "0.4.3"
 
-[dependencies.procedural-masquerade]
-version = "0.1"
+[dependencies.phf]
+version = "0.7"
 
-[dependencies.itoa]
+[dependencies.cssparser-macros]
+version = "0.3"
+
+[dependencies.dtoa-short]
 version = "0.3"
 [dev-dependencies.rustc-serialize]
 version = "0.3"
 
+[dev-dependencies.difference]
+version = "1.0"
+
 [dev-dependencies.encoding_rs]
 version = "0.7"
-
-[dev-dependencies.difference]
-version = "1.0"
 [build-dependencies.syn]
 version = "0.11"
 
 [build-dependencies.quote]
 version = "0.3"
 
 [features]
 dummy_match_byte = []
--- a/third_party/rust/cssparser/src/color.rs
+++ b/third_party/rust/cssparser/src/color.rs
@@ -145,30 +145,30 @@ impl ToCss for Color {
 }
 
 impl Color {
     /// Parse a <color> value, per CSS Color Module Level 3.
     ///
     /// FIXME(#2) Deprecated CSS2 System Colors are not supported yet.
     pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Color, BasicParseError<'i>> {
         // FIXME: remove clone() when lifetimes are non-lexical
+        let location = input.current_source_location();
         let token = input.next()?.clone();
         match token {
             Token::Hash(ref value) | Token::IDHash(ref value) => {
                 Color::parse_hash(value.as_bytes())
             },
             Token::Ident(ref value) => parse_color_keyword(&*value),
             Token::Function(ref name) => {
                 return input.parse_nested_block(|arguments| {
-                    parse_color_function(&*name, arguments)
-                        .map_err(|e| ParseError::Basic(e))
+                    parse_color_function(&*name, arguments).map_err(|e| e.into())
                 }).map_err(ParseError::<()>::basic);
             }
             _ => Err(())
-        }.map_err(|()| BasicParseError::UnexpectedToken(token))
+        }.map_err(|()| location.new_basic_unexpected_token_error(token))
     }
 
     /// Parse a color hash, without the leading '#' character.
     #[inline]
     pub fn parse_hash(value: &[u8]) -> Result<Self, ()> {
         match value.len() {
             8 => Ok(rgba(
                 from_hex(value[0])? * 16 + from_hex(value[1])?,
@@ -419,34 +419,35 @@ fn clamp_floor_256_f32(val: f32) -> u8 {
     val.round().max(0.).min(255.) as u8
 }
 
 #[inline]
 fn parse_color_function<'i, 't>(name: &str, arguments: &mut Parser<'i, 't>) -> Result<Color, BasicParseError<'i>> {
     let (red, green, blue, uses_commas) = match_ignore_ascii_case! { name,
         "rgb" | "rgba" => parse_rgb_components_rgb(arguments)?,
         "hsl" | "hsla" => parse_rgb_components_hsl(arguments)?,
-        _ => return Err(BasicParseError::UnexpectedToken(Token::Ident(name.to_owned().into()))),
+        _ => return Err(arguments.new_basic_unexpected_token_error(Token::Ident(name.to_owned().into()))),
     };
 
     let alpha = if !arguments.is_exhausted() {
         if uses_commas {
             arguments.expect_comma()?;
         } else {
             arguments.expect_delim('/')?;
         };
+        let location = arguments.current_source_location();
         match *arguments.next()? {
             Token::Number { value: v, .. } => {
                 clamp_unit_f32(v)
             }
             Token::Percentage { unit_value: v, .. } => {
                 clamp_unit_f32(v)
             }
             ref t => {
-                return Err(BasicParseError::UnexpectedToken(t.clone()))
+                return Err(location.new_basic_unexpected_token_error(t.clone()))
             }
         }
     } else {
         255
     };
 
     arguments.expect_exhausted()?;
     Ok(rgba(red, green, blue, alpha))
@@ -457,83 +458,87 @@ fn parse_color_function<'i, 't>(name: &s
 fn parse_rgb_components_rgb<'i, 't>(arguments: &mut Parser<'i, 't>) -> Result<(u8, u8, u8, bool), BasicParseError<'i>> {
     let red: u8;
     let green: u8;
     let blue: u8;
     let mut uses_commas = false;
 
     // Either integers or percentages, but all the same type.
     // https://drafts.csswg.org/css-color/#rgb-functions
+    // FIXME: remove .clone() when lifetimes are non-lexical.
+    let location = arguments.current_source_location();
     match arguments.next()?.clone() {
         Token::Number { value: v, .. } => {
             red = clamp_floor_256_f32(v);
             green = clamp_floor_256_f32(match arguments.next()?.clone() {
                 Token::Number { value: v, .. } => v,
                 Token::Comma => {
                     uses_commas = true;
                     arguments.expect_number()?
                 }
-                t => return Err(BasicParseError::UnexpectedToken(t))
+                t => return Err(location.new_basic_unexpected_token_error(t))
             });
             if uses_commas {
                 arguments.expect_comma()?;
             }
             blue = clamp_floor_256_f32(arguments.expect_number()?);
         }
         Token::Percentage { unit_value, .. } => {
             red = clamp_unit_f32(unit_value);
             green = clamp_unit_f32(match arguments.next()?.clone() {
                 Token::Percentage { unit_value, .. } => unit_value,
                 Token::Comma => {
                     uses_commas = true;
                     arguments.expect_percentage()?
                 }
-                t => return Err(BasicParseError::UnexpectedToken(t))
+                t => return Err(location.new_basic_unexpected_token_error(t))
             });
             if uses_commas {
                 arguments.expect_comma()?;
             }
             blue = clamp_unit_f32(arguments.expect_percentage()?);
         }
-        t => return Err(BasicParseError::UnexpectedToken(t))
+        t => return Err(location.new_basic_unexpected_token_error(t))
     };
     return Ok((red, green, blue, uses_commas));
 }
 
 #[inline]
 fn parse_rgb_components_hsl<'i, 't>(arguments: &mut Parser<'i, 't>) -> Result<(u8, u8, u8, bool), BasicParseError<'i>> {
     let mut uses_commas = false;
     // Hue given as an angle
     // https://drafts.csswg.org/css-values/#angles
+    let location = arguments.current_source_location();
     let hue_degrees = match *arguments.next()? {
         Token::Number { value: v, .. } => v,
         Token::Dimension { value: v, ref unit, .. } => {
             match_ignore_ascii_case! { &*unit,
                 "deg" => v,
                 "grad" => v * 360. / 400.,
                 "rad" => v * 360. / (2. * PI),
                 "turn" => v * 360.,
-                _ => return Err(BasicParseError::UnexpectedToken(Token::Ident(unit.clone()))),
+                _ => return Err(location.new_basic_unexpected_token_error(Token::Ident(unit.clone()))),
             }
         }
-        ref t => return Err(BasicParseError::UnexpectedToken(t.clone()))
+        ref t => return Err(location.new_basic_unexpected_token_error(t.clone()))
     };
     // Subtract an integer before rounding, to avoid some rounding errors:
     let hue_normalized_degrees = hue_degrees - 360. * (hue_degrees / 360.).floor();
     let hue = hue_normalized_degrees / 360.;
 
     // Saturation and lightness are clamped to 0% ... 100%
     // https://drafts.csswg.org/css-color/#the-hsl-notation
+    let location = arguments.current_source_location();
     let saturation = match arguments.next()?.clone() {
         Token::Percentage { unit_value, .. } => unit_value,
         Token::Comma => {
             uses_commas = true;
             arguments.expect_percentage()?
         }
-        t => return Err(BasicParseError::UnexpectedToken(t))
+        t => return Err(location.new_basic_unexpected_token_error(t))
     };
     let saturation = saturation.max(0.).min(1.);
 
     if uses_commas {
         arguments.expect_comma()?;
     }
 
     let lightness = arguments.expect_percentage()?;
--- a/third_party/rust/cssparser/src/lib.rs
+++ b/third_party/rust/cssparser/src/lib.rs
@@ -81,23 +81,24 @@ extern crate itoa;
 #[cfg(feature = "heapsize")] #[macro_use] extern crate heapsize;
 extern crate smallvec;
 
 pub use cssparser_macros::*;
 
 pub use tokenizer::{Token, SourcePosition, SourceLocation};
 pub use rules_and_declarations::{parse_important};
 pub use rules_and_declarations::{DeclarationParser, DeclarationListParser, parse_one_declaration};
-pub use rules_and_declarations::{RuleListParser, parse_one_rule, PreciseParseError};
+pub use rules_and_declarations::{RuleListParser, parse_one_rule};
 pub use rules_and_declarations::{AtRuleType, QualifiedRuleParser, AtRuleParser};
 pub use from_bytes::{stylesheet_encoding, EncodingSupport};
 pub use color::{RGBA, Color, parse_color_keyword};
 pub use nth::parse_nth;
 pub use serializer::{ToCss, CssStringWriter, serialize_identifier, serialize_string, TokenSerializationType};
-pub use parser::{Parser, Delimiter, Delimiters, ParserState, ParseError, BasicParseError, ParserInput};
+pub use parser::{Parser, Delimiter, Delimiters, ParserState, ParserInput};
+pub use parser::{ParseError, ParseErrorKind, BasicParseError, BasicParseErrorKind};
 pub use unicode_range::UnicodeRange;
 pub use cow_rc_str::CowRcStr;
 
 // For macros
 #[doc(hidden)] pub use macros::_internal__to_lowercase;
 
 // For macros when used in this crate. Unsure how $crate works with procedural-masquerade.
 mod cssparser { pub use _internal__phf; }
--- a/third_party/rust/cssparser/src/nth.rs
+++ b/third_party/rust/cssparser/src/nth.rs
@@ -19,17 +19,17 @@ pub fn parse_nth<'i, 't>(input: &mut Par
         }
         Token::Dimension { int_value: Some(a), unit, .. } => {
             match_ignore_ascii_case! {
                 &unit,
                 "n" => Ok(try!(parse_b(input, a))),
                 "n-" => Ok(try!(parse_signless_b(input, a, -1))),
                 _ => match parse_n_dash_digits(&*unit) {
                     Ok(b) => Ok((a, b)),
-                    Err(()) => Err(BasicParseError::UnexpectedToken(Token::Ident(unit.clone())))
+                    Err(()) => Err(input.new_basic_unexpected_token_error(Token::Ident(unit.clone())))
                 }
             }
         }
         Token::Ident(value) => {
             match_ignore_ascii_case! { &value,
                 "even" => Ok((2, 0)),
                 "odd" => Ok((2, 1)),
                 "n" => Ok(try!(parse_b(input, 1))),
@@ -39,36 +39,36 @@ pub fn parse_nth<'i, 't>(input: &mut Par
                 _ => {
                     let (slice, a) = if value.starts_with("-") {
                         (&value[1..], -1)
                     } else {
                         (&*value, 1)
                     };
                     match parse_n_dash_digits(slice) {
                         Ok(b) => Ok((a, b)),
-                        Err(()) => Err(BasicParseError::UnexpectedToken(Token::Ident(value.clone())))
+                        Err(()) => Err(input.new_basic_unexpected_token_error(Token::Ident(value.clone())))
                     }
                 }
             }
         }
         // FIXME: remove .clone() when lifetimes are non-lexical.
         Token::Delim('+') => match input.next_including_whitespace()?.clone() {
             Token::Ident(value) => {
                 match_ignore_ascii_case! { &value,
                     "n" => parse_b(input, 1),
                     "n-" => parse_signless_b(input, 1, -1),
                     _ => match parse_n_dash_digits(&*value) {
                         Ok(b) => Ok((1, b)),
-                        Err(()) => Err(BasicParseError::UnexpectedToken(Token::Ident(value.clone())))
+                        Err(()) => Err(input.new_basic_unexpected_token_error(Token::Ident(value.clone())))
                     }
                 }
             }
-            token => Err(BasicParseError::UnexpectedToken(token)),
+            token => Err(input.new_basic_unexpected_token_error(token)),
         },
-        token => Err(BasicParseError::UnexpectedToken(token)),
+        token => Err(input.new_basic_unexpected_token_error(token)),
     }
 }
 
 
 fn parse_b<'i, 't>(input: &mut Parser<'i, 't>, a: i32) -> Result<(i32, i32), BasicParseError<'i>> {
     let start = input.state();
     match input.next() {
         Ok(&Token::Delim('+')) => parse_signless_b(input, a, 1),
@@ -77,19 +77,20 @@ fn parse_b<'i, 't>(input: &mut Parser<'i
         _ => {
             input.reset(&start);
             Ok((a, 0))
         }
     }
 }
 
 fn parse_signless_b<'i, 't>(input: &mut Parser<'i, 't>, a: i32, b_sign: i32) -> Result<(i32, i32), BasicParseError<'i>> {
-    match *input.next()? {
+    // FIXME: remove .clone() when lifetimes are non-lexical.
+    match input.next()?.clone() {
         Token::Number { has_sign: false, int_value: Some(b), .. } => Ok((a, b_sign * b)),
-        ref token => Err(BasicParseError::UnexpectedToken(token.clone()))
+        token => Err(input.new_basic_unexpected_token_error(token))
     }
 }
 
 fn parse_n_dash_digits(string: &str) -> Result<i32, ()> {
     let bytes = string.as_bytes();
     if bytes.len() >= 3
     && bytes[..2].eq_ignore_ascii_case(b"n-")
     && bytes[2..].iter().all(|&c| matches!(c, b'0'...b'9'))
--- a/third_party/rust/cssparser/src/parser.rs
+++ b/third_party/rust/cssparser/src/parser.rs
@@ -35,54 +35,124 @@ impl ParserState {
     pub fn source_location(&self) -> SourceLocation {
         SourceLocation {
             line: self.current_line_number,
             column: (self.position - self.current_line_start_position + 1) as u32,
         }
     }
 }
 
-
-/// The funamental parsing errors that can be triggered by built-in parsing routines.
+/// Details about a `BasicParseError`
 #[derive(Clone, Debug, PartialEq)]
-pub enum BasicParseError<'a> {
+pub enum BasicParseErrorKind<'i> {
     /// An unexpected token was encountered.
-    UnexpectedToken(Token<'a>),
+    UnexpectedToken(Token<'i>),
     /// The end of the input was encountered unexpectedly.
     EndOfInput,
     /// An `@` rule was encountered that was invalid.
-    AtRuleInvalid(CowRcStr<'a>),
+    AtRuleInvalid(CowRcStr<'i>),
     /// The body of an '@' rule was invalid.
     AtRuleBodyInvalid,
     /// A qualified rule was encountered that was invalid.
     QualifiedRuleInvalid,
 }
 
-impl<'a, T> From<BasicParseError<'a>> for ParseError<'a, T> {
+/// The funamental parsing errors that can be triggered by built-in parsing routines.
+#[derive(Clone, Debug, PartialEq)]
+pub struct BasicParseError<'i> {
+    /// Details of this error
+    pub kind: BasicParseErrorKind<'i>,
+    /// Location where this error occurred
+    pub location: SourceLocation,
+}
+
+impl<'i, T> From<BasicParseError<'i>> for ParseError<'i, T> {
+    #[inline]
+    fn from(this: BasicParseError<'i>) -> ParseError<'i, T> {
+        ParseError {
+            kind: ParseErrorKind::Basic(this.kind),
+            location: this.location,
+        }
+    }
+}
+
+impl SourceLocation {
+    /// Create a new BasicParseError at this location for an unexpected token
+    #[inline]
+    pub fn new_basic_unexpected_token_error<'i>(self, token: Token<'i>) -> BasicParseError<'i> {
+        BasicParseError {
+            kind: BasicParseErrorKind::UnexpectedToken(token),
+            location: self,
+        }
+    }
+
+    /// Create a new ParseError at this location for an unexpected token
     #[inline]
-    fn from(this: BasicParseError<'a>) -> ParseError<'a, T> {
-        ParseError::Basic(this)
+    pub fn new_unexpected_token_error<'i, E>(self, token: Token<'i>) -> ParseError<'i, E> {
+        ParseError {
+            kind: ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(token)),
+            location: self,
+        }
+    }
+
+    /// Create a new custom ParseError at this location
+    #[inline]
+    pub fn new_custom_error<'i, E1: Into<E2>, E2>(self, error: E1) -> ParseError<'i, E2> {
+        ParseError {
+            kind: ParseErrorKind::Custom(error.into()),
+            location: self,
+        }
+    }
+}
+
+/// Details of a `ParseError`
+#[derive(Clone, Debug, PartialEq)]
+pub enum ParseErrorKind<'i, T: 'i> {
+    /// A fundamental parse error from a built-in parsing routine.
+    Basic(BasicParseErrorKind<'i>),
+    /// A parse error reported by downstream consumer code.
+    Custom(T),
+}
+
+impl<'i, T> ParseErrorKind<'i, T> {
+    /// Like `std::convert::Into::into`
+    pub fn into<U>(self) -> ParseErrorKind<'i, U> where T: Into<U> {
+        match self {
+            ParseErrorKind::Basic(basic) => ParseErrorKind::Basic(basic),
+            ParseErrorKind::Custom(custom) => ParseErrorKind::Custom(custom.into()),
+        }
     }
 }
 
 /// Extensible parse errors that can be encountered by client parsing implementations.
 #[derive(Clone, Debug, PartialEq)]
-pub enum ParseError<'a, T: 'a> {
-    /// A fundamental parse error from a built-in parsing routine.
-    Basic(BasicParseError<'a>),
-    /// A parse error reported by downstream consumer code.
-    Custom(T),
+pub struct ParseError<'i, E: 'i> {
+    /// Details of this error
+    pub kind: ParseErrorKind<'i, E>,
+    /// Location where this error occurred
+    pub location: SourceLocation,
 }
 
-impl<'a, T> ParseError<'a, T> {
+impl<'i, T> ParseError<'i, T> {
     /// Extract the fundamental parse error from an extensible error.
-    pub fn basic(self) -> BasicParseError<'a> {
-        match self {
-            ParseError::Basic(e) => e,
-            ParseError::Custom(_) => panic!("Not a basic parse error"),
+    pub fn basic(self) -> BasicParseError<'i> {
+        match self.kind {
+            ParseErrorKind::Basic(kind) => BasicParseError {
+                kind: kind,
+                location: self.location,
+            },
+            ParseErrorKind::Custom(_) => panic!("Not a basic parse error"),
+        }
+    }
+
+    /// Like `std::convert::Into::into`
+    pub fn into<U>(self) -> ParseError<'i, U> where T: Into<U> {
+        ParseError {
+            kind: self.kind.into(),
+            location: self.location,
         }
     }
 }
 
 /// The owned input for a parser.
 pub struct ParserInput<'i> {
     tokenizer: Tokenizer<'i>,
     cached_token: Option<CachedToken<'i>>,
@@ -223,16 +293,31 @@ impl Delimiters {
             Some(b'}') => ClosingDelimiter::CloseCurlyBracket,
             Some(b']') => ClosingDelimiter::CloseSquareBracket,
             Some(b')') => ClosingDelimiter::CloseParenthesis,
             _ => Delimiter::None,
         }
     }
 }
 
+/// Used in some `fn expect_*` methods
+macro_rules! expect {
+    ($parser: ident, $($branches: tt)+) => {
+        {
+            let start_location = $parser.current_source_location();
+            match *$parser.next()? {
+                $($branches)+
+                ref token => {
+                    return Err(start_location.new_basic_unexpected_token_error(token.clone()))
+                }
+            }
+        }
+    }
+}
+
 impl<'i: 't, 't> Parser<'i, 't> {
     /// Create a new parser
     #[inline]
     pub fn new(input: &'t mut ParserInput<'i>) -> Parser<'i, 't> {
         Parser {
             input: input,
             at_start_of: None,
             stop_before: Delimiter::None,
@@ -255,19 +340,19 @@ impl<'i: 't, 't> Parser<'i, 't> {
     /// Check whether the input is exhausted. That is, if `.next()` would return a token.
     /// Return a `Result` so that the `try!` macro can be used: `try!(input.expect_exhausted())`
     ///
     /// This ignores whitespace and comments.
     #[inline]
     pub fn expect_exhausted(&mut self) -> Result<(), BasicParseError<'i>> {
         let start = self.state();
         let result = match self.next() {
-            Err(BasicParseError::EndOfInput) => Ok(()),
+            Err(BasicParseError { kind: BasicParseErrorKind::EndOfInput, .. }) => Ok(()),
             Err(e) => unreachable!("Unexpected error encountered: {:?}", e),
-            Ok(t) => Err(BasicParseError::UnexpectedToken(t.clone())),
+            Ok(t) => Err(start.source_location().new_basic_unexpected_token_error(t.clone())),
         };
         self.reset(&start);
         result
     }
 
     /// Return the current position within the input.
     ///
     /// This can be used with the `Parser::slice` and `slice_from` methods.
@@ -295,16 +380,52 @@ impl<'i: 't, 't> Parser<'i, 't> {
     ///
     /// The source URL is extracted from a specially formatted
     /// comment.  The last such comment is used, so this value may
     /// change as parsing proceeds.
     pub fn current_source_url(&self) -> Option<&str> {
         self.input.tokenizer.current_source_url()
     }
 
+    /// Create a new BasicParseError at the current location
+    #[inline]
+    pub fn new_basic_error(&self, kind: BasicParseErrorKind<'i>) -> BasicParseError<'i> {
+        BasicParseError {
+            kind: kind,
+            location: self.current_source_location(),
+        }
+    }
+
+    /// Create a new basic ParseError at the current location
+    #[inline]
+    pub fn new_error<E>(&self, kind: BasicParseErrorKind<'i>) -> ParseError<'i, E> {
+        ParseError {
+            kind: ParseErrorKind::Basic(kind),
+            location: self.current_source_location(),
+        }
+    }
+
+    /// Create a new custom BasicParseError at the current location
+    #[inline]
+    pub fn new_custom_error<E1: Into<E2>, E2>(&self, error: E1) -> ParseError<'i, E2> {
+        self.current_source_location().new_custom_error(error)
+    }
+
+    /// Create a new unexpected token BasicParseError at the current location
+    #[inline]
+    pub fn new_basic_unexpected_token_error(&self, token: Token<'i>) -> BasicParseError<'i> {
+        self.new_basic_error(BasicParseErrorKind::UnexpectedToken(token))
+    }
+
+    /// Create a new unexpected token ParseError at the current location
+    #[inline]
+    pub fn new_unexpected_token_error<E>(&self, token: Token<'i>) -> ParseError<'i, E> {
+        self.new_error(BasicParseErrorKind::UnexpectedToken(token))
+    }
+
     /// Return the current internal state of the parser (including position within the input).
     ///
     /// This state can later be restored with the `Parser::reset` method.
     #[inline]
     pub fn state(&self) -> ParserState {
         ParserState {
             at_start_of: self.at_start_of,
             .. self.input.tokenizer.state()
@@ -425,33 +546,34 @@ impl<'i: 't, 't> Parser<'i, 't> {
     /// comments should always be ignored between tokens.
     pub fn next_including_whitespace_and_comments(&mut self) -> Result<&Token<'i>, BasicParseError<'i>> {
         if let Some(block_type) = self.at_start_of.take() {
             consume_until_end_of_block(block_type, &mut self.input.tokenizer);
         }
 
         let byte = self.input.tokenizer.next_byte();
         if self.stop_before.contains(Delimiters::from_byte(byte)) {
-            return Err(BasicParseError::EndOfInput)
+            return Err(self.new_basic_error(BasicParseErrorKind::EndOfInput))
         }
 
         let token_start_position = self.input.tokenizer.position();
         let token;
         match self.input.cached_token {
             Some(ref cached_token)
             if cached_token.start_position == token_start_position => {
                 self.input.tokenizer.reset(&cached_token.end_state);
                 match cached_token.token {
                     Token::Function(ref name) => self.input.tokenizer.see_function(name),
                     _ => {}
                 }
                 token = &cached_token.token
             }
             _ => {
-                let new_token = self.input.tokenizer.next().map_err(|()| BasicParseError::EndOfInput)?;
+                let new_token = self.input.tokenizer.next()
+                    .map_err(|()| self.new_basic_error(BasicParseErrorKind::EndOfInput))?;
                 self.input.cached_token = Some(CachedToken {
                     token: new_token,
                     start_position: token_start_position,
                     end_state: self.input.tokenizer.state(),
                 });
                 token = self.input.cached_token_ref()
             }
         }
@@ -545,243 +667,228 @@ impl<'i: 't, 't> Parser<'i, 't> {
                                    -> Result <T, ParseError<'i, E>>
     where F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>> {
         parse_until_after(self, delimiters, parse)
     }
 
     /// Parse a <whitespace-token> and return its value.
     #[inline]
     pub fn expect_whitespace(&mut self) -> Result<&'i str, BasicParseError<'i>> {
+        let start_location = self.current_source_location();
         match *self.next_including_whitespace()? {
             Token::WhiteSpace(value) => Ok(value),
-            ref t => Err(BasicParseError::UnexpectedToken(t.clone()))
+            ref t => Err(start_location.new_basic_unexpected_token_error(t.clone()))
         }
     }
 
     /// Parse a <ident-token> and return the unescaped value.
     #[inline]
     pub fn expect_ident(&mut self) -> Result<&CowRcStr<'i>, BasicParseError<'i>> {
-        match *self.next()? {
+        expect! {self,
             Token::Ident(ref value) => Ok(value),
-            ref t => Err(BasicParseError::UnexpectedToken(t.clone()))
         }
     }
 
     /// expect_ident, but clone the CowRcStr
     #[inline]
     pub fn expect_ident_cloned(&mut self) -> Result<CowRcStr<'i>, BasicParseError<'i>> {
         self.expect_ident().map(|s| s.clone())
     }
 
     /// Parse a <ident-token> whose unescaped value is an ASCII-insensitive match for the given value.
     #[inline]
     pub fn expect_ident_matching(&mut self, expected_value: &str) -> Result<(), BasicParseError<'i>> {
-        match *self.next()? {
+        expect! {self,
             Token::Ident(ref value) if value.eq_ignore_ascii_case(expected_value) => Ok(()),
-            ref t => Err(BasicParseError::UnexpectedToken(t.clone()))
         }
     }
 
     /// Parse a <string-token> and return the unescaped value.
     #[inline]
     pub fn expect_string(&mut self) -> Result<&CowRcStr<'i>, BasicParseError<'i>> {
-        match *self.next()? {
+        expect! {self,
             Token::QuotedString(ref value) => Ok(value),
-            ref t => Err(BasicParseError::UnexpectedToken(t.clone()))
         }
     }
 
     /// expect_string, but clone the CowRcStr
     #[inline]
     pub fn expect_string_cloned(&mut self) -> Result<CowRcStr<'i>, BasicParseError<'i>> {
         self.expect_string().map(|s| s.clone())
     }
 
     /// Parse either a <ident-token> or a <string-token>, and return the unescaped value.
     #[inline]
     pub fn expect_ident_or_string(&mut self) -> Result<&CowRcStr<'i>, BasicParseError<'i>> {
-        match *self.next()? {
+        expect! {self,
             Token::Ident(ref value) => Ok(value),
             Token::QuotedString(ref value) => Ok(value),
-            ref t => Err(BasicParseError::UnexpectedToken(t.clone()))
         }
     }
 
     /// Parse a <url-token> and return the unescaped value.
     #[inline]
     pub fn expect_url(&mut self) -> Result<CowRcStr<'i>, BasicParseError<'i>> {
         // FIXME: revert early returns when lifetimes are non-lexical
-        match *self.next()? {
+        expect! {self,
             Token::UnquotedUrl(ref value) => return Ok(value.clone()),
             Token::Function(ref name) if name.eq_ignore_ascii_case("url") => {}
-            ref t => return Err(BasicParseError::UnexpectedToken(t.clone()))
         }
-        self.parse_nested_block(|input| input.expect_string().map_err(ParseError::Basic).map(|s| s.clone()))
+        self.parse_nested_block(|input| input.expect_string().map_err(Into::into).map(|s| s.clone()))
             .map_err(ParseError::<()>::basic)
     }
 
     /// Parse either a <url-token> or a <string-token>, and return the unescaped value.
     #[inline]
     pub fn expect_url_or_string(&mut self) -> Result<CowRcStr<'i>, BasicParseError<'i>> {
         // FIXME: revert early returns when lifetimes are non-lexical
-        match *self.next()? {
+        expect! {self,
             Token::UnquotedUrl(ref value) => return Ok(value.clone()),
             Token::QuotedString(ref value) => return Ok(value.clone()),
             Token::Function(ref name) if name.eq_ignore_ascii_case("url") => {}
-            ref t => return Err(BasicParseError::UnexpectedToken(t.clone()))
         }
-        self.parse_nested_block(|input| input.expect_string().map_err(ParseError::Basic).map(|s| s.clone()))
+        self.parse_nested_block(|input| input.expect_string().map_err(Into::into).map(|s| s.clone()))
             .map_err(ParseError::<()>::basic)
     }
 
     /// Parse a <number-token> and return the integer value.
     #[inline]
     pub fn expect_number(&mut self) -> Result<f32, BasicParseError<'i>> {
-        match *self.next()? {
+        expect! {self,
             Token::Number { value, .. } => Ok(value),
-            ref t => Err(BasicParseError::UnexpectedToken(t.clone()))
         }
     }
 
     /// Parse a <number-token> that does not have a fractional part, and return the integer value.
     #[inline]
     pub fn expect_integer(&mut self) -> Result<i32, BasicParseError<'i>> {
-        match *self.next()? {
-            Token::Number { int_value: Some(int_value), .. } => {
-                Ok(int_value)
-            }
-            ref t => Err(BasicParseError::UnexpectedToken(t.clone()))
+        expect! {self,
+            Token::Number { int_value: Some(int_value), .. } => Ok(int_value),
         }
     }
 
     /// Parse a <percentage-token> and return the value.
     /// `0%` and `100%` map to `0.0` and `1.0` (not `100.0`), respectively.
     #[inline]
     pub fn expect_percentage(&mut self) -> Result<f32, BasicParseError<'i>> {
-        match *self.next()? {
+        expect! {self,
             Token::Percentage { unit_value, .. } => Ok(unit_value),
-            ref t => Err(BasicParseError::UnexpectedToken(t.clone()))
         }
     }
 
     /// Parse a `:` <colon-token>.
     #[inline]
     pub fn expect_colon(&mut self) -> Result<(), BasicParseError<'i>> {
-        match *self.next()? {
+        expect! {self,
             Token::Colon => Ok(()),
-            ref t => Err(BasicParseError::UnexpectedToken(t.clone()))
         }
     }
 
     /// Parse a `;` <semicolon-token>.
     #[inline]
     pub fn expect_semicolon(&mut self) -> Result<(), BasicParseError<'i>> {
-        match *self.next()? {
+        expect! {self,
             Token::Semicolon => Ok(()),
-            ref t => Err(BasicParseError::UnexpectedToken(t.clone()))
         }
     }
 
     /// Parse a `,` <comma-token>.
     #[inline]
     pub fn expect_comma(&mut self) -> Result<(), BasicParseError<'i>> {
-        match *self.next()? {
+        expect! {self,
             Token::Comma => Ok(()),
-            ref t => Err(BasicParseError::UnexpectedToken(t.clone()))
         }
     }
 
     /// Parse a <delim-token> with the given value.
     #[inline]
     pub fn expect_delim(&mut self, expected_value: char) -> Result<(), BasicParseError<'i>> {
-        match *self.next()? {
+        expect! {self,
             Token::Delim(value) if value == expected_value => Ok(()),
-            ref t => Err(BasicParseError::UnexpectedToken(t.clone()))
         }
     }
 
     /// Parse a `{ /* ... */ }` curly brackets block.
     ///
     /// If the result is `Ok`, you can then call the `Parser::parse_nested_block` method.
     #[inline]
     pub fn expect_curly_bracket_block(&mut self) -> Result<(), BasicParseError<'i>> {
-        match *self.next()? {
+        expect! {self,
             Token::CurlyBracketBlock => Ok(()),
-            ref t => Err(BasicParseError::UnexpectedToken(t.clone()))
         }
     }
 
     /// Parse a `[ /* ... */ ]` square brackets block.
     ///
     /// If the result is `Ok`, you can then call the `Parser::parse_nested_block` method.
     #[inline]
     pub fn expect_square_bracket_block(&mut self) -> Result<(), BasicParseError<'i>> {
-        match *self.next()? {
+        expect! {self,
             Token::SquareBracketBlock => Ok(()),
-            ref t => Err(BasicParseError::UnexpectedToken(t.clone()))
         }
     }
 
     /// Parse a `( /* ... */ )` parenthesis block.
     ///
     /// If the result is `Ok`, you can then call the `Parser::parse_nested_block` method.
     #[inline]
     pub fn expect_parenthesis_block(&mut self) -> Result<(), BasicParseError<'i>> {
-        match *self.next()? {
+        expect! {self,
             Token::ParenthesisBlock => Ok(()),
-            ref t => Err(BasicParseError::UnexpectedToken(t.clone()))
         }
     }
 
     /// Parse a <function> token and return its name.
     ///
     /// If the result is `Ok`, you can then call the `Parser::parse_nested_block` method.
     #[inline]
     pub fn expect_function(&mut self) -> Result<&CowRcStr<'i>, BasicParseError<'i>> {
-        match *self.next()? {
+        expect! {self,
             Token::Function(ref name) => Ok(name),
-            ref t => Err(BasicParseError::UnexpectedToken(t.clone()))
         }
     }
 
     /// Parse a <function> token whose name is an ASCII-insensitive match for the given value.
     ///
     /// If the result is `Ok`, you can then call the `Parser::parse_nested_block` method.
     #[inline]
     pub fn expect_function_matching(&mut self, expected_name: &str) -> Result<(), BasicParseError<'i>> {
-        match *self.next()? {
+        expect! {self,
             Token::Function(ref name) if name.eq_ignore_ascii_case(expected_name) => Ok(()),
-            ref t => Err(BasicParseError::UnexpectedToken(t.clone()))
         }
     }
 
     /// Parse the input until exhaustion and check that it contains no “error” token.
     ///
     /// See `Token::is_parse_error`. This also checks nested blocks and functions recursively.
     #[inline]
     pub fn expect_no_error_token(&mut self) -> Result<(), BasicParseError<'i>> {
+        // FIXME: remove break and intermediate variable when lifetimes are non-lexical
+        let token;
         loop {
             match self.next_including_whitespace_and_comments() {
                 Ok(&Token::Function(_)) |
                 Ok(&Token::ParenthesisBlock) |
                 Ok(&Token::SquareBracketBlock) |
                 Ok(&Token::CurlyBracketBlock) => {}
-                Ok(token) => {
-                    if token.is_parse_error() {
-                        //FIXME: maybe these should be separate variants of BasicParseError instead?
-                        return Err(BasicParseError::UnexpectedToken(token.clone()))
+                Ok(t) => {
+                    if t.is_parse_error() {
+                        token = t.clone();
+                        break
                     }
                     continue
                 }
                 Err(_) => return Ok(())
             }
             let result = self.parse_nested_block(|input| input.expect_no_error_token()
-                                                 .map_err(|e| ParseError::Basic(e)));
+                                                 .map_err(|e| Into::into(e)));
             result.map_err(ParseError::<()>::basic)?
         }
+        // FIXME: maybe these should be separate variants of BasicParseError instead?
+        Err(self.new_basic_unexpected_token_error(token))
     }
 }
 
 pub fn parse_until_before<'i: 't, 't, F, T, E>(parser: &mut Parser<'i, 't>,
                                                delimiters: Delimiters,
                                                parse: F)
                                                -> Result <T, ParseError<'i, E>>
     where F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>> {
--- a/third_party/rust/cssparser/src/rules_and_declarations.rs
+++ b/third_party/rust/cssparser/src/rules_and_declarations.rs
@@ -2,29 +2,27 @@
  * 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/. */
 
 // https://drafts.csswg.org/css-syntax/#parsing
 
 use cow_rc_str::CowRcStr;
 use parser::{parse_until_before, parse_until_after, parse_nested_block, ParserState};
 use std::ascii::AsciiExt;
-use super::{Token, Parser, Delimiter, ParseError, BasicParseError, SourceLocation};
-
+use super::{Token, Parser, Delimiter, ParseError, BasicParseError, BasicParseErrorKind};
 
 /// Parse `!important`.
 ///
 /// Typical usage is `input.try(parse_important).is_ok()`
 /// at the end of a `DeclarationParser::parse_value` implementation.
 pub fn parse_important<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(), BasicParseError<'i>> {
     input.expect_delim('!')?;
     input.expect_ident_matching("important")
 }
 
-
 /// The return value for `AtRuleParser::parse_prelude`.
 /// Indicates whether the at-rule is expected to have a `{ /* ... */ }` block
 /// or end with a `;` semicolon.
 pub enum AtRuleType<P, PB> {
     /// The at-rule is expected to end with a `;` semicolon. Example: `@import`.
     ///
     /// The value is the representation of all data of the rule which would be
     /// handled in rule_without_block.
@@ -107,17 +105,17 @@ pub trait AtRuleParser<'i> {
     /// The given `input` is a "delimited" parser
     /// that ends wherever the prelude should end.
     /// (Before the next semicolon, the next `{`, or the end of the current block.)
     fn parse_prelude<'t>(&mut self, name: CowRcStr<'i>, input: &mut Parser<'i, 't>)
                      -> Result<AtRuleType<Self::PreludeNoBlock, Self::PreludeBlock>,
                                ParseError<'i, Self::Error>> {
         let _ = name;
         let _ = input;
-        Err(ParseError::Basic(BasicParseError::AtRuleInvalid(name)))
+        Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name)))
     }
 
     /// End an at-rule which doesn't have block. Return the finished
     /// representation of the at-rule.
     ///
     /// This is only called when `parse_prelude` returned `WithoutBlock`, and
     /// either the `;` semicolon indeed follows the prelude, or parser is at
     /// the end of the input.
@@ -134,17 +132,17 @@ pub trait AtRuleParser<'i> {
     /// or `Err(())` to ignore the entire at-rule as invalid.
     ///
     /// This is only called when `parse_prelude` returned `WithBlock`, and a block
     /// was indeed found following the prelude.
     fn parse_block<'t>(&mut self, prelude: Self::PreludeBlock, input: &mut Parser<'i, 't>)
                        -> Result<Self::AtRule, ParseError<'i, Self::Error>> {
         let _ = prelude;
         let _ = input;
-        Err(ParseError::Basic(BasicParseError::AtRuleBodyInvalid))
+        Err(input.new_error(BasicParseErrorKind::AtRuleBodyInvalid))
     }
 }
 
 /// A trait to provide various parsing of qualified rules.
 ///
 /// For example, there could be different implementations
 /// for top-level qualified rules (i.e. style rules with Selectors as prelude)
 /// and for qualified rules inside `@keyframes` (keyframe rules with keyframe selectors as prelude).
@@ -170,29 +168,29 @@ pub trait QualifiedRuleParser<'i> {
     ///
     /// The prelude is the part before the `{ /* ... */ }` block.
     ///
     /// The given `input` is a "delimited" parser
     /// that ends where the prelude should end (before the next `{`).
     fn parse_prelude<'t>(&mut self, input: &mut Parser<'i, 't>)
                          -> Result<Self::Prelude, ParseError<'i, Self::Error>> {
         let _ = input;
-        Err(ParseError::Basic(BasicParseError::QualifiedRuleInvalid))
+        Err(input.new_error(BasicParseErrorKind::QualifiedRuleInvalid))
     }
 
     /// Parse the content of a `{ /* ... */ }` block for the body of the qualified rule.
     ///
     /// Return the finished representation of the qualified rule
     /// as returned by `RuleListParser::next`,
     /// or `Err(())` to ignore the entire at-rule as invalid.
     fn parse_block<'t>(&mut self, prelude: Self::Prelude, input: &mut Parser<'i, 't>)
                        -> Result<Self::QualifiedRule, ParseError<'i, Self::Error>> {
         let _ = prelude;
         let _ = input;
-        Err(ParseError::Basic(BasicParseError::QualifiedRuleInvalid))
+        Err(input.new_error(BasicParseErrorKind::QualifiedRuleInvalid))
     }
 }
 
 
 /// Provides an iterator for declaration list parsing.
 pub struct DeclarationListParser<'i: 't, 't: 'a, 'a, P> {
     /// The input given to `DeclarationListParser::new`
     pub input: &'a mut Parser<'i, 't>,
@@ -227,57 +225,51 @@ where P: DeclarationParser<'i, Declarati
     }
 }
 
 /// `DeclarationListParser` is an iterator that yields `Ok(_)` for a valid declaration or at-rule
 /// or `Err(())` for an invalid one.
 impl<'i: 't, 't: 'a, 'a, I, P, E: 'i> Iterator for DeclarationListParser<'i, 't, 'a, P>
 where P: DeclarationParser<'i, Declaration = I, Error = E> +
          AtRuleParser<'i, AtRule = I, Error = E> {
-    type Item = Result<I, PreciseParseError<'i, E>>;
+    type Item = Result<I, (ParseError<'i, E>, &'i str)>;
 
-    fn next(&mut self) -> Option<Result<I, PreciseParseError<'i, E>>> {
+    fn next(&mut self) -> Option<Self::Item> {
         loop {
             let start = self.input.state();
             // FIXME: remove intermediate variable when lifetimes are non-lexical
             let ident = match self.input.next_including_whitespace_and_comments() {
                 Ok(&Token::WhiteSpace(_)) | Ok(&Token::Comment(_)) | Ok(&Token::Semicolon) => continue,
                 Ok(&Token::Ident(ref name)) => Ok(Ok(name.clone())),
                 Ok(&Token::AtKeyword(ref name)) => Ok(Err(name.clone())),
                 Ok(token) => Err(token.clone()),
                 Err(_) => return None,
             };
             match ident {
                 Ok(Ok(name)) => {
                     // Ident
-                    return Some({
+                    let result = {
                         let parser = &mut self.parser;
                         // FIXME: https://github.com/rust-lang/rust/issues/42508
                         parse_until_after::<'i, 't, _, _, _>(self.input, Delimiter::Semicolon, |input| {
                             input.expect_colon()?;
                             parser.parse_value(name, input)
                         })
-                    }.map_err(|e| PreciseParseError {
-                        error: e,
-                        slice: self.input.slice_from(start.position()),
-                        location: start.source_location(),
-                    }))
+                    };
+                    return Some(result.map_err(|e| (e, self.input.slice_from(start.position()))))
                 }
                 Ok(Err(name)) => {
                     // At-keyword
                     return Some(parse_at_rule(&start, name, self.input, &mut self.parser))
                 }
                 Err(token) => {
-                    return Some(self.input.parse_until_after(Delimiter::Semicolon,
-                                                             |_| Err(ParseError::Basic(BasicParseError::UnexpectedToken(token.clone()))))
-                                .map_err(|e| PreciseParseError {
-                                    error: e,
-                                    slice: self.input.slice_from(start.position()),
-                                    location: start.source_location(),
-                                }))
+                    let result = self.input.parse_until_after(Delimiter::Semicolon, |_| {
+                        Err(start.source_location().new_unexpected_token_error(token.clone()))
+                    });
+                    return Some(result.map_err(|e| (e, self.input.slice_from(start.position()))))
                 }
             }
         }
     }
 }
 
 
 /// Provides an iterator for rule list parsing.
@@ -332,19 +324,19 @@ where P: QualifiedRuleParser<'i, Qualifi
 }
 
 
 
 /// `RuleListParser` is an iterator that yields `Ok(_)` for a rule or `Err(())` for an invalid one.
 impl<'i: 't, 't: 'a, 'a, R, P, E: 'i> Iterator for RuleListParser<'i, 't, 'a, P>
 where P: QualifiedRuleParser<'i, QualifiedRule = R, Error = E> +
          AtRuleParser<'i, AtRule = R, Error = E> {
-    type Item = Result<R, PreciseParseError<'i, E>>;
+    type Item = Result<R, (ParseError<'i, E>, &'i str)>;
 
-    fn next(&mut self) -> Option<Result<R, PreciseParseError<'i, E>>> {
+    fn next(&mut self) -> Option<Self::Item> {
         loop {
             if self.is_stylesheet {
                 self.input.skip_cdc_and_cdo()
             } else {
                 self.input.skip_whitespace()
             }
             let start = self.input.state();
 
@@ -370,44 +362,36 @@ where P: QualifiedRuleParser<'i, Qualifi
                 if first_stylesheet_rule && name.eq_ignore_ascii_case("charset") {
                     let delimiters = Delimiter::Semicolon | Delimiter::CurlyBracketBlock;
                     let _: Result<(), ParseError<()>> = self.input.parse_until_after(delimiters, |_| Ok(()));
                 } else {
                     return Some(parse_at_rule(&start, name.clone(), self.input, &mut self.parser))
                 }
             } else {
                 self.any_rule_so_far = true;
-                return Some(parse_qualified_rule(self.input, &mut self.parser)
-                            .map_err(|e| PreciseParseError {
-                                error: e,
-                                slice: self.input.slice_from(start.position()),
-                                location: start.source_location(),
-                            }))
+                let result = parse_qualified_rule(self.input, &mut self.parser);
+                return Some(result.map_err(|e| (e, self.input.slice_from(start.position()))))
             }
         }
     }
 }
 
 
 /// Parse a single declaration, such as an `( /* ... */ )` parenthesis in an `@supports` prelude.
 pub fn parse_one_declaration<'i, 't, P, E>(input: &mut Parser<'i, 't>, parser: &mut P)
                                            -> Result<<P as DeclarationParser<'i>>::Declaration,
-                                                     PreciseParseError<'i, E>>
+                                                     (ParseError<'i, E>, &'i str)>
                                            where P: DeclarationParser<'i, Error = E> {
     let start_position = input.position();
-    let start_location = input.current_source_location();
     input.parse_entirely(|input| {
         let name = input.expect_ident()?.clone();
         input.expect_colon()?;
         parser.parse_value(name, input)
-    }).map_err(|e| PreciseParseError {
-        error: e,
-        slice: input.slice_from(start_position),
-        location: start_location,
     })
+    .map_err(|e| (e, input.slice_from(start_position)))
 }
 
 
 /// Parse a single rule, such as for CSSOM’s `CSSStyleSheet.insertRule`.
 pub fn parse_one_rule<'i, 't, R, P, E>(input: &mut Parser<'i, 't>, parser: &mut P)
                                        -> Result<R, ParseError<'i, E>>
 where P: QualifiedRuleParser<'i, QualifiedRule = R, Error = E> +
          AtRuleParser<'i, AtRule = R, Error = E> {
@@ -425,91 +409,66 @@ where P: QualifiedRuleParser<'i, Qualifi
             if at_keyword.is_none() {
                 input.reset(&start)
             }
         } else {
             at_keyword = None
         }
 
         if let Some(name) = at_keyword {
-            parse_at_rule(&start, name, input, parser).map_err(|e| e.error)
+            parse_at_rule(&start, name, input, parser).map_err(|e| e.0)
         } else {
             parse_qualified_rule(input, parser)
         }
     })
 }
 
-/// A parse error with details of where it occured
-pub struct PreciseParseError<'i, E: 'i> {
-    /// Error details
-    pub error: ParseError<'i, E>,
-
-    /// The relevant slice of the input.
-    pub slice: &'i str,
-
-    /// The line number and column number of the start of the relevant input slice.
-    pub location: SourceLocation,
-}
-
 fn parse_at_rule<'i: 't, 't, P, E>(start: &ParserState, name: CowRcStr<'i>,
                                    input: &mut Parser<'i, 't>, parser: &mut P)
-                                   -> Result<<P as AtRuleParser<'i>>::AtRule, PreciseParseError<'i, E>>
+                                   -> Result<<P as AtRuleParser<'i>>::AtRule,
+                                             (ParseError<'i, E>, &'i str)>
                                    where P: AtRuleParser<'i, Error = E> {
     let delimiters = Delimiter::Semicolon | Delimiter::CurlyBracketBlock;
     // FIXME: https://github.com/rust-lang/rust/issues/42508
     let result = parse_until_before::<'i, 't, _, _, _>(input, delimiters, |input| {
         parser.parse_prelude(name, input)
     });
     match result {
         Ok(AtRuleType::WithoutBlock(prelude)) => {
             match input.next() {
                 Ok(&Token::Semicolon) | Err(_) => Ok(parser.rule_without_block(prelude)),
-                Ok(&Token::CurlyBracketBlock) => Err(PreciseParseError {
-                    error: ParseError::Basic(BasicParseError::UnexpectedToken(Token::CurlyBracketBlock)),
-                    slice: input.slice_from(start.position()),
-                    location: start.source_location(),
-                }),
+                Ok(&Token::CurlyBracketBlock) => Err((
+                    input.new_unexpected_token_error(Token::CurlyBracketBlock),
+                    input.slice_from(start.position()),
+                )),
                 Ok(_) => unreachable!()
             }
         }
         Ok(AtRuleType::WithBlock(prelude)) => {
             match input.next() {
                 Ok(&Token::CurlyBracketBlock) => {
                     // FIXME: https://github.com/rust-lang/rust/issues/42508
                     parse_nested_block::<'i, 't, _, _, _>(input, move |input| parser.parse_block(prelude, input))
-                        .map_err(|e| PreciseParseError {
-                            error: e,
-                            slice: input.slice_from(start.position()),
-                            location: start.source_location(),
-                        })
+                        .map_err(|e| (e, input.slice_from(start.position())))
                 }
-                Ok(&Token::Semicolon) => Err(PreciseParseError {
-                    error: ParseError::Basic(BasicParseError::UnexpectedToken(Token::Semicolon)),
-                    slice: input.slice_from(start.position()),
-                    location: start.source_location(),
-                }),
-                Err(e) => Err(PreciseParseError {
-                    error: ParseError::Basic(e),
-                    slice: input.slice_from(start.position()),
-                    location: start.source_location(),
-                }),
+                Ok(&Token::Semicolon) => Err((
+                    input.new_unexpected_token_error(Token::Semicolon),
+                    input.slice_from(start.position()),
+                )),
+                Err(e) => Err((e.into(), input.slice_from(start.position()))),
                 Ok(_) => unreachable!()
             }
         }
         Err(error) => {
             let end_position = input.position();
             match input.next() {
                 Ok(&Token::CurlyBracketBlock) | Ok(&Token::Semicolon) | Err(_) => {},
                 _ => unreachable!()
             };
-            Err(PreciseParseError {
-                error: error,
-                slice: input.slice(start.position()..end_position),
-                location: start.source_location(),
-            })
+            Err((error, input.slice(start.position()..end_position)))
         }
     }
 }
 
 
 fn parse_qualified_rule<'i, 't, P, E>(input: &mut Parser<'i, 't>, parser: &mut P)
                                       -> Result<<P as QualifiedRuleParser<'i>>::QualifiedRule, ParseError<'i, E>>
                                       where P: QualifiedRuleParser<'i, Error = E> {
--- a/third_party/rust/cssparser/src/size_of_tests.rs
+++ b/third_party/rust/cssparser/src/size_of_tests.rs
@@ -37,11 +37,10 @@ size_of_test!(std_cow_str, Cow<'static, 
 size_of_test!(cow_rc_str, CowRcStr, 16);
 
 size_of_test!(tokenizer, ::tokenizer::Tokenizer, 72);
 size_of_test!(parser_input, ::parser::ParserInput, 144);
 size_of_test!(parser, ::parser::Parser, 16);
 size_of_test!(source_position, ::SourcePosition, 8);
 size_of_test!(parser_state, ::ParserState, 24);
 
-size_of_test!(basic_parse_error, ::BasicParseError, 40);
-size_of_test!(parse_error_lower_bound, ::ParseError<()>, 48);
-size_of_test!(precise_parse_error_lower_bound, ::PreciseParseError<()>, 72);
+size_of_test!(basic_parse_error, ::BasicParseError, 48);
+size_of_test!(parse_error_lower_bound, ::ParseError<()>, 56);
--- a/third_party/rust/cssparser/src/tests.rs
+++ b/third_party/rust/cssparser/src/tests.rs
@@ -6,18 +6,19 @@
 extern crate test;
 
 use encoding_rs;
 use rustc_serialize::json::{self, Json, ToJson};
 
 #[cfg(feature = "bench")]
 use self::test::Bencher;
 
-use super::{Parser, Delimiter, Token, SourceLocation, ParseError,
-            DeclarationListParser, DeclarationParser, RuleListParser, BasicParseError,
+use super::{Parser, Delimiter, Token, SourceLocation,
+            ParseError, ParseErrorKind, BasicParseError, BasicParseErrorKind,
+            DeclarationListParser, DeclarationParser, RuleListParser,
             AtRuleType, AtRuleParser, QualifiedRuleParser, ParserInput,
             parse_one_declaration, parse_one_rule, parse_important,
             stylesheet_encoding, EncodingSupport,
             TokenSerializationType, CowRcStr,
             Color, RGBA, parse_nth, UnicodeRange, ToCss};
 
 macro_rules! JArray {
     ($($e: expr,)*) => { JArray![ $( $e ),* ] };
@@ -258,19 +259,17 @@ fn expect_no_error_token() {
 
 /// https://github.com/servo/rust-cssparser/issues/71
 #[test]
 fn outer_block_end_consumed() {
     let mut input = ParserInput::new("(calc(true))");
     let mut input = Parser::new(&mut input);
     assert!(input.expect_parenthesis_block().is_ok());
     assert!(input.parse_nested_block(|input| {
-        let result: Result<_, ParseError<()>> = input.expect_function_matching("calc")
-            .map_err(|e| ParseError::Basic(e));
-        result
+        input.expect_function_matching("calc").map_err(Into::<ParseError<()>>::into)
     }).is_ok());
     println!("{:?}", input.position());
     assert!(input.next().is_err());
 }
 
 /// https://github.com/servo/rust-cssparser/issues/174
 #[test]
 fn bad_url_slice_out_of_bounds() {
@@ -332,17 +331,17 @@ fn test_expect_url() {
     let mut input = ParserInput::new("url('abc' more stuff)");
     assert!(parse(&mut input).is_err());
 }
 
 
 fn run_color_tests<F: Fn(Result<Color, ()>) -> Json>(json_data: &str, to_json: F) {
     run_json_tests(json_data, |input| {
         let result: Result<_, ParseError<()>> = input.parse_entirely(|i| {
-            Color::parse(i).map_err(|e| ParseError::Basic(e))
+            Color::parse(i).map_err(Into::into)
         });
         to_json(result.map_err(|_| ()))
     });
 }
 
 
 #[test]
 fn color3() {
@@ -362,17 +361,17 @@ fn color3_keywords() {
     run_color_tests(include_str!("css-parsing-tests/color3_keywords.json"), |c| c.ok().to_json())
 }
 
 
 #[test]
 fn nth() {
     run_json_tests(include_str!("css-parsing-tests/An+B.json"), |input| {
         input.parse_entirely(|i| {
-            let result: Result<_, ParseError<()>> = parse_nth(i).map_err(|e| ParseError::Basic(e));
+            let result: Result<_, ParseError<()>> = parse_nth(i).map_err(Into::into);
             result
         }).ok().to_json()
     });
 }
 
 #[test]
 fn unicode_range() {
     run_json_tests(include_str!("css-parsing-tests/urange.json"), |input| {
@@ -774,17 +773,19 @@ impl<'i> AtRuleParser<'i> for JsonParser
                          -> Result<AtRuleType<Vec<Json>, Vec<Json>>, ParseError<'i, ()>> {
         let prelude = vec![
             "at-rule".to_json(),
             name.to_json(),
             Json::Array(component_values_to_json(input)),
         ];
         match_ignore_ascii_case! { &*name,
             "media" | "foo-with-block" => Ok(AtRuleType::WithBlock(prelude)),
-            "charset" => Err(BasicParseError::AtRuleInvalid(name.clone()).into()),
+            "charset" => {
+                Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name.clone()).into()))
+            }
             _ => Ok(AtRuleType::WithoutBlock(prelude)),
         }
     }
 
     fn rule_without_block(&mut self, mut prelude: Vec<Json>) -> Json {
         prelude.push(Json::Null);
         Json::Array(prelude)
     }
@@ -1011,27 +1012,33 @@ fn parser_with_line_number_offset() {
 }
 
 #[test]
 fn cdc_regression_test() {
     let mut input = ParserInput::new("-->x");
     let mut parser = Parser::new(&mut input);
     parser.skip_cdc_and_cdo();
     assert_eq!(parser.next(), Ok(&Token::Ident("x".into())));
-    assert_eq!(parser.next(), Err(BasicParseError::EndOfInput));
+    assert_eq!(parser.next(), Err(BasicParseError {
+        kind: BasicParseErrorKind::EndOfInput,
+        location: SourceLocation { line: 0, column: 5 }
+    }));
 }
 
 #[test]
 fn parse_entirely_reports_first_error() {
     #[derive(PartialEq, Debug)]
     enum E { Foo }
     let mut input = ParserInput::new("ident");
     let mut parser = Parser::new(&mut input);
-    let result: Result<(), _> = parser.parse_entirely(|_| Err(ParseError::Custom(E::Foo)));
-    assert_eq!(result, Err(ParseError::Custom(E::Foo)));
+    let result: Result<(), _> = parser.parse_entirely(|p| Err(p.new_custom_error(E::Foo)));
+    assert_eq!(result, Err(ParseError {
+        kind: ParseErrorKind::Custom(E::Foo),
+        location: SourceLocation { line: 0, column: 1 },
+    }));
 }
 
 #[test]
 fn parse_sourcemapping_comments() {
     let tests = vec![
         ("/*# sourceMappingURL=here*/", Some("here")),
         ("/*# sourceMappingURL=here  */", Some("here")),
         ("/*@ sourceMappingURL=here*/", Some("here")),
@@ -1135,17 +1142,17 @@ fn utf16_columns() {
 
     for test in tests {
         let mut input = ParserInput::new(test.0);
         let mut parser = Parser::new(&mut input);
 
         // Read all tokens.
         loop {
             match parser.next() {
-                Err(BasicParseError::EndOfInput) => { break; }
+                Err(BasicParseError { kind: BasicParseErrorKind::EndOfInput, .. }) => { break; }
                 Err(_) => { assert!(false); }
                 Ok(_) => {}
             };
         }
 
         // Check the resulting column.
         assert_eq!(parser.current_source_location().column, test.1);
     }
--- a/third_party/rust/cssparser/src/unicode_range.rs
+++ b/third_party/rust/cssparser/src/unicode_range.rs
@@ -39,49 +39,50 @@ impl UnicodeRange {
 
         // This deviates from the spec in case there are CSS comments
         // between tokens in the middle of one <unicode-range>,
         // but oh well…
         let concatenated_tokens = input.slice_from(after_u);
 
         let range = match parse_concatenated(concatenated_tokens.as_bytes()) {
             Ok(range) => range,
-            Err(()) => return Err(BasicParseError::UnexpectedToken(Token::Ident(concatenated_tokens.into()))),
+            Err(()) => return Err(input.new_basic_unexpected_token_error(Token::Ident(concatenated_tokens.into()))),
         };
         if range.end > char::MAX as u32 || range.start > range.end {
-            Err(BasicParseError::UnexpectedToken(Token::Ident(concatenated_tokens.into())))
+            Err(input.new_basic_unexpected_token_error(Token::Ident(concatenated_tokens.into())))
         } else {
             Ok(range)
         }
     }
 }
 
 fn parse_tokens<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(), BasicParseError<'i>> {
     match input.next_including_whitespace()?.clone() {
         Token::Delim('+') => {
-            match *input.next_including_whitespace()? {
+            // FIXME: remove .clone() when lifetimes are non-lexical.
+            match input.next_including_whitespace()?.clone() {
                 Token::Ident(_) => {}
                 Token::Delim('?') => {}
-                ref t => return Err(BasicParseError::UnexpectedToken(t.clone()))
+                t => return Err(input.new_basic_unexpected_token_error(t))
             }
             parse_question_marks(input)
         }
         Token::Dimension { .. } => {
             parse_question_marks(input)
         }
         Token::Number { .. } => {
             let after_number = input.state();
             match input.next_including_whitespace() {
                 Ok(&Token::Delim('?')) => parse_question_marks(input),
                 Ok(&Token::Dimension { .. }) => {}
                 Ok(&Token::Number { .. }) => {}
                 _ => input.reset(&after_number)
             }
         }
-        t => return Err(BasicParseError::UnexpectedToken(t))
+        t => return Err(input.new_basic_unexpected_token_error(t))
     }
     Ok(())
 }
 
 /// Consume as many '?' as possible
 fn parse_question_marks(input: &mut Parser) {
     loop {
         let start = input.state();
--- a/toolkit/library/gtest/rust/Cargo.lock
+++ b/toolkit/library/gtest/rust/Cargo.lock
@@ -294,17 +294,17 @@ source = "registry+https://github.com/ru
 dependencies = [
  "core-foundation 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "cssparser"
-version = "0.21.3"
+version = "0.22.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "cssparser-macros 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "dtoa-short 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
  "procedural-masquerade 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -554,17 +554,17 @@ dependencies = [
  "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "geckoservo"
 version = "0.0.1"
 dependencies = [
  "atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "cssparser 0.21.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "malloc_size_of 0.0.1",
  "nsstring_vendor 0.1.0",
  "parking_lot 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "selectors 0.19.0",
  "servo_arc 0.0.1",
@@ -754,17 +754,17 @@ dependencies = [
  "arrayvec 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "malloc_size_of"
 version = "0.0.1"
 dependencies = [
  "app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "cssparser 0.21.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "hashglobe 0.1.0",
  "servo_arc 0.0.1",
  "smallbitvec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -1186,17 +1186,17 @@ name = "scopeguard"
 version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "selectors"
 version = "0.19.0"
 dependencies = [
  "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "cssparser 0.21.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "malloc_size_of 0.0.1",
  "malloc_size_of_derive 0.0.1",
  "matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
  "precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1293,17 +1293,17 @@ version = "0.0.1"
 dependencies = [
  "app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "arrayvec 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)",
  "atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bindgen 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "cfg-if 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "cssparser 0.21.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "fallible 0.0.1",
  "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "hashglobe 0.1.0",
  "itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1347,17 +1347,17 @@ dependencies = [
 ]
 
 [[package]]
 name = "style_traits"
 version = "0.0.1"
 dependencies = [
  "app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "cssparser 0.21.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "malloc_size_of 0.0.1",
  "malloc_size_of_derive 0.0.1",
  "selectors 0.19.0",
  "servo_arc 0.0.1",
 ]
 
 [[package]]
@@ -1692,17 +1692,17 @@ dependencies = [
 "checksum clang-sys 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "611ec2e3a7623afd8a8c0d027887b6b55759d894abbf5fe11b9dc11b50d5b49a"
 "checksum clap 2.25.0 (registry+https://github.com/rust-lang/crates.io-index)" = "867a885995b4184be051b70a592d4d70e32d7a188db6e8dff626af286a962771"
 "checksum coco 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c06169f5beb7e31c7c67ebf5540b8b472d23e3eade3b2ec7d1f5b504a85f91bd"
 "checksum core-foundation 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5909502e547762013619f4c4e01cc7393c20fe2d52d7fa471c1210adb2320dc7"
 "checksum core-foundation-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "41115a6aa5d3e1e5ef98148373f25971d1fad53818553f216495f9e67e90a624"
 "checksum core-foundation-sys 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bc9fb3d6cb663e6fd7cf1c63f9b144ee2b1e4a78595a0451dd34bff85b9a3387"
 "checksum core-graphics 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2fd47addfc77b7e574d24e5434f95bb64a863769dfd4f1d451ca4ff5530ba01a"
 "checksum core-text 7.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2a23bef779fab70e5e6af23e36eed03a48e1c1687dea8929505d405ea48d1f5e"
-"checksum cssparser 0.21.3 (registry+https://github.com/rust-lang/crates.io-index)" = "89af8ca7a36c8c591a9ff56ffb88ea2597095d8c1b834af9ca5be5032dcfe06e"
+"checksum cssparser 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44313341610282488e1156ad1fedebca51c54766c87a041d0287b10499c04ba1"
 "checksum cssparser-macros 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "079adec4af52bb5275eadd004292028c79eb3c5f5b4ee8086a36d4197032f6df"
 "checksum darling 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9861a8495606435477df581bc858ccf15a3469747edf175b94a4704fd9aaedac"
 "checksum darling_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1486a8b00b45062c997f767738178b43219133dd0c8c826cb811e60563810821"
 "checksum darling_macro 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a86ec160aa0c3dd492dd4a14ec8104ad8f1a9400a820624db857998cc1f80f9"
 "checksum dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97590ba53bcb8ac28279161ca943a924d1fd4a8fb3fa63302591647c4fc5b850"
 "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab"
 "checksum dtoa-short 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "068d4026697c1a18f0b0bb8cfcad1b0c151b90d8edb9bf4c235ad68128920d1d"
 "checksum dwrote 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "36e3b27cd0b8a68e00f07e8d8e1e4f4d8a6b8b873290a734f63bd56d792d23e1"
--- a/toolkit/library/rust/Cargo.lock
+++ b/toolkit/library/rust/Cargo.lock
@@ -293,17 +293,17 @@ source = "registry+https://github.com/ru
 dependencies = [
  "core-foundation 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "cssparser"
-version = "0.21.3"
+version = "0.22.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "cssparser-macros 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "dtoa-short 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
  "procedural-masquerade 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -553,17 +553,17 @@ dependencies = [
  "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "geckoservo"
 version = "0.0.1"
 dependencies = [
  "atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "cssparser 0.21.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "malloc_size_of 0.0.1",
  "nsstring_vendor 0.1.0",
  "parking_lot 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "selectors 0.19.0",
  "servo_arc 0.0.1",
@@ -753,17 +753,17 @@ dependencies = [
  "arrayvec 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "malloc_size_of"
 version = "0.0.1"
 dependencies = [
  "app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "cssparser 0.21.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "hashglobe 0.1.0",
  "servo_arc 0.0.1",
  "smallbitvec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -1174,17 +1174,17 @@ name = "scopeguard"
 version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "selectors"
 version = "0.19.0"
 dependencies = [
  "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "cssparser 0.21.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "malloc_size_of 0.0.1",
  "malloc_size_of_derive 0.0.1",
  "matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
  "precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1285,17 +1285,17 @@ version = "0.0.1"
 dependencies = [
  "app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "arrayvec 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)",
  "atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bindgen 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "cfg-if 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "cssparser 0.21.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "fallible 0.0.1",
  "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "hashglobe 0.1.0",
  "itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1339,30 +1339,30 @@ dependencies = [
 ]
 
 [[package]]
 name = "style_traits"
 version = "0.0.1"
 dependencies = [
  "app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "cssparser 0.21.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "malloc_size_of 0.0.1",
  "malloc_size_of_derive 0.0.1",
  "selectors 0.19.0",
  "servo_arc 0.0.1",
 ]
 
 [[package]]
 name = "stylo_tests"
 version = "0.0.1"
 dependencies = [
  "atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "cssparser 0.21.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "geckoservo 0.0.1",
  "libc 0.2.24 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "malloc_size_of 0.0.1",
  "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "selectors 0.19.0",
@@ -1704,17 +1704,17 @@ dependencies = [
 "checksum clang-sys 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "611ec2e3a7623afd8a8c0d027887b6b55759d894abbf5fe11b9dc11b50d5b49a"
 "checksum clap 2.25.0 (registry+https://github.com/rust-lang/crates.io-index)" = "867a885995b4184be051b70a592d4d70e32d7a188db6e8dff626af286a962771"
 "checksum coco 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c06169f5beb7e31c7c67ebf5540b8b472d23e3eade3b2ec7d1f5b504a85f91bd"
 "checksum core-foundation 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5909502e547762013619f4c4e01cc7393c20fe2d52d7fa471c1210adb2320dc7"
 "checksum core-foundation-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "41115a6aa5d3e1e5ef98148373f25971d1fad53818553f216495f9e67e90a624"
 "checksum core-foundation-sys 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bc9fb3d6cb663e6fd7cf1c63f9b144ee2b1e4a78595a0451dd34bff85b9a3387"
 "checksum core-graphics 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2fd47addfc77b7e574d24e5434f95bb64a863769dfd4f1d451ca4ff5530ba01a"
 "checksum core-text 7.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2a23bef779fab70e5e6af23e36eed03a48e1c1687dea8929505d405ea48d1f5e"
-"checksum cssparser 0.21.3 (registry+https://github.com/rust-lang/crates.io-index)" = "89af8ca7a36c8c591a9ff56ffb88ea2597095d8c1b834af9ca5be5032dcfe06e"
+"checksum cssparser 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44313341610282488e1156ad1fedebca51c54766c87a041d0287b10499c04ba1"
 "checksum cssparser-macros 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "079adec4af52bb5275eadd004292028c79eb3c5f5b4ee8086a36d4197032f6df"
 "checksum darling 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9861a8495606435477df581bc858ccf15a3469747edf175b94a4704fd9aaedac"
 "checksum darling_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1486a8b00b45062c997f767738178b43219133dd0c8c826cb811e60563810821"
 "checksum darling_macro 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a86ec160aa0c3dd492dd4a14ec8104ad8f1a9400a820624db857998cc1f80f9"
 "checksum dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97590ba53bcb8ac28279161ca943a924d1fd4a8fb3fa63302591647c4fc5b850"
 "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab"
 "checksum dtoa-short 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "068d4026697c1a18f0b0bb8cfcad1b0c151b90d8edb9bf4c235ad68128920d1d"
 "checksum dwrote 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "36e3b27cd0b8a68e00f07e8d8e1e4f4d8a6b8b873290a734f63bd56d792d23e1"