No bug - Revendor rust dependencies
authorServo VCS Sync <servo-vcs-sync@mozilla.com>
Fri, 09 Jun 2017 22:57:05 +0000
changeset 413781 b6f4ba89792e51ed67a7407113f9953de3eb4f72
parent 413780 79d8f06313932e8f116100390dd153a1c8f9734b
child 413782 3f12cc772ae84e7d8ab6ca959b6b2f86809c0785
push id1490
push usermtabara@mozilla.com
push dateMon, 31 Jul 2017 14:08:16 +0000
treeherdermozilla-release@70e32e6bf15e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone55.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
No bug - Revendor rust dependencies
third_party/rust/cssparser/.cargo-checksum.json
third_party/rust/cssparser/Cargo.toml
third_party/rust/cssparser/Cargo.toml.orig
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/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",".gitignore":"0b944a5f5edfe3dca4796907e63f537a0e919630df66dca7945d51ba4e3c8051",".travis.yml":"f1fb4b65964c81bc1240544267ea334f554ca38ae7a74d57066f4d47d2b5d568","Cargo.toml":"c6aa6d96263fb5247a4b3d3bf5be16644373aa4b956e166647e23f4527aac7b8","Cargo.toml.orig":"bc6ad4db67d62cf6101527073857fa4abd86cb76f4cbe7e91727aff917955246","LICENSE":"fab3dd6bdab226f1c08630b1dd917e11fcb4ec5e1e020e2c16f83a0a13863e85","README.md":"9afe084d70a5d9396674a2624012d6ac749df35f81e322d2d75b042bf208f523","build.rs":"950bcc47a196f07f99f59637c28cc65e02a885130011f90a2b2608248b4724a2","build/match_byte.rs":"89e8b941af74df2c204abf808672d3ff278bdec75abc918c41a843260b924677","docs/.nojekyll":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","docs/404.html":"025861f76f8d1f6d67c20ab624c6e418f4f824385e2dd8ad8732c4ea563c6a2e","docs/index.html":"025861f76f8d1f6d67c20ab624c6e418f4f824385e2dd8ad8732c4ea563c6a2e","src/big-data-url.css":"04a8f6197ea1181123bca48bd1ebd016268e1da40f01b8f21055814e44bf62b8","src/color.rs":"33927641dc64d3bafba20f65d17d63d39c565013a07393c86cdf29f93dfc536b","src/css-parsing-tests/An+B.json":"d24559c1dad55d3da9d1fca29383edefdfc6046988435d6388a9bc0f28850257","src/css-parsing-tests/LICENSE":"5f9019a92f4aa8917aadc8e035aa673c2c1bf08d5ca2e535a0564106599f44eb","src/css-parsing-tests/README.rst":"775c5f957dd1d46d3ce954aaad219c821d2b64b4a9fb93c42e9737a11131ca44","src/css-parsing-tests/color3.json":"814179b0a6a9d3a935e635f485f1eaec9880129c5a0d09c0cda4efcea69649fb","src/css-parsing-tests/color3_hsl.json":"61c3b7d8d5ae02f94769d64458e0dd29e4c22c5068e5ea44040d14b88f9616b0","src/css-parsing-tests/color3_keywords.json":"95609bf9fe762c316878a30f371fa375a2e51c21a6fda24fa188a95cd9118f5c","src/css-parsing-tests/component_value_list.json":"516f9495fe089fa669321660bc431d7884839da8fb73e45edcbcd98625cb09dc","src/css-parsing-tests/declaration_list.json":"0b85cc3f19e945f838432acbfb9edb003abea13debc4ea27bcdcef25d117eac5","src/css-parsing-tests/make_color3_hsl.py":"3df7dd908ee719f78fd097ab24622f75edd10fcb67514230c172cbfc842c8ab7","src/css-parsing-tests/make_color3_keywords.py":"66bccab3f1dea18698fcfd854be79b1fd1cd724dd487e25b1f057b522163aad2","src/css-parsing-tests/one_component_value.json":"8798017709002e14cf11e203c9d716f82d308ce6ba0f6e64ee4eea331b8485c6","src/css-parsing-tests/one_declaration.json":"a34c9da56edfff9e2e21615f059e141b0e878e90f794dc8fa58d65b47cd193ed","src/css-parsing-tests/one_rule.json":"88f7b1b6049be88e1e2827673b75fc9261986b216e8ee6bf09621fecbe274e3c","src/css-parsing-tests/rule_list.json":"97c45e80fb83abef149a4016c5625a74f053e7ad70a2ce5a95c02fce1c195686","src/css-parsing-tests/stylesheet.json":"05f1e10fc486bfbda2c059c313a74ff78c0063c0768b99737cab41969c0c87ce","src/css-parsing-tests/stylesheet_bytes.json":"890fd856a596e61f82cf7ed77920ffe95df89209fdb5ee0afe0b26bdfdb80a42","src/css-parsing-tests/urange.json":"62720b143ddf52508baad42921473dd69519aad6c1cd49f37f3f264dc29e1c13","src/from_bytes.rs":"331fe63af2123ae3675b61928a69461b5ac77799fff3ce9978c55cf2c558f4ff","src/lib.rs":"ccc0f04541147d4fb90d3fe70591bacfb0c7030706c9be8fa60b80533e522bbc","src/macros.rs":"adb9773c157890381556ea83d7942dcc676f99eea71abbb6afeffee1e3f28960","src/nth.rs":"0a5e68bd8a597403e184ebf34e69230ae1e955f92b16b99b3f67cf8730a180a9","src/parser.rs":"a41b1d885389d34b4d81176f844ae3c4100e621628dd50d7348c42b08cdd13ae","src/rules_and_declarations.rs":"6b66a986e411a56998546ab0e64de5285df3368d7c4018c7230a1b6cf6bcc532","src/serializer.rs":"13bf417e747b742576402543e25d7bdf65aa5dd6971455eb9d355169d2182f89","src/tests.rs":"80e4fec507258fe4e63a590f842f3213b44418cd69d755f78f938894966037db","src/tokenizer.rs":"4ad65f6f96fe7162c10fc6a1451c2ce8670b47b6a520e51382a952246515a217","src/unicode_range.rs":"f1f1dee3cc4efb6647557c6166aa5a93354fc0ee9beba53c1d63427beffe5a8d"},"package":"ef6124306e5ebc5ab11891d063aeafdd0cdc308079b708c8b566125f3680292b"}
\ No newline at end of file
+{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",".gitignore":"0b944a5f5edfe3dca4796907e63f537a0e919630df66dca7945d51ba4e3c8051",".travis.yml":"f1fb4b65964c81bc1240544267ea334f554ca38ae7a74d57066f4d47d2b5d568","Cargo.toml":"053b1cec1e53bb2ace4d689f28def451e2349aa1bb2d4c04b72daa3d9beb9240","LICENSE":"fab3dd6bdab226f1c08630b1dd917e11fcb4ec5e1e020e2c16f83a0a13863e85","README.md":"9afe084d70a5d9396674a2624012d6ac749df35f81e322d2d75b042bf208f523","build.rs":"950bcc47a196f07f99f59637c28cc65e02a885130011f90a2b2608248b4724a2","build/match_byte.rs":"89e8b941af74df2c204abf808672d3ff278bdec75abc918c41a843260b924677","docs/.nojekyll":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","docs/404.html":"025861f76f8d1f6d67c20ab624c6e418f4f824385e2dd8ad8732c4ea563c6a2e","docs/index.html":"025861f76f8d1f6d67c20ab624c6e418f4f824385e2dd8ad8732c4ea563c6a2e","src/big-data-url.css":"04a8f6197ea1181123bca48bd1ebd016268e1da40f01b8f21055814e44bf62b8","src/color.rs":"99e737145aa01f4c721c5da939632c39fb085abdff5548a32c130456907269f8","src/css-parsing-tests/An+B.json":"d24559c1dad55d3da9d1fca29383edefdfc6046988435d6388a9bc0f28850257","src/css-parsing-tests/LICENSE":"5f9019a92f4aa8917aadc8e035aa673c2c1bf08d5ca2e535a0564106599f44eb","src/css-parsing-tests/README.rst":"775c5f957dd1d46d3ce954aaad219c821d2b64b4a9fb93c42e9737a11131ca44","src/css-parsing-tests/color3.json":"814179b0a6a9d3a935e635f485f1eaec9880129c5a0d09c0cda4efcea69649fb","src/css-parsing-tests/color3_hsl.json":"61c3b7d8d5ae02f94769d64458e0dd29e4c22c5068e5ea44040d14b88f9616b0","src/css-parsing-tests/color3_keywords.json":"95609bf9fe762c316878a30f371fa375a2e51c21a6fda24fa188a95cd9118f5c","src/css-parsing-tests/component_value_list.json":"516f9495fe089fa669321660bc431d7884839da8fb73e45edcbcd98625cb09dc","src/css-parsing-tests/declaration_list.json":"0b85cc3f19e945f838432acbfb9edb003abea13debc4ea27bcdcef25d117eac5","src/css-parsing-tests/make_color3_hsl.py":"3df7dd908ee719f78fd097ab24622f75edd10fcb67514230c172cbfc842c8ab7","src/css-parsing-tests/make_color3_keywords.py":"66bccab3f1dea18698fcfd854be79b1fd1cd724dd487e25b1f057b522163aad2","src/css-parsing-tests/one_component_value.json":"8798017709002e14cf11e203c9d716f82d308ce6ba0f6e64ee4eea331b8485c6","src/css-parsing-tests/one_declaration.json":"a34c9da56edfff9e2e21615f059e141b0e878e90f794dc8fa58d65b47cd193ed","src/css-parsing-tests/one_rule.json":"88f7b1b6049be88e1e2827673b75fc9261986b216e8ee6bf09621fecbe274e3c","src/css-parsing-tests/rule_list.json":"97c45e80fb83abef149a4016c5625a74f053e7ad70a2ce5a95c02fce1c195686","src/css-parsing-tests/stylesheet.json":"05f1e10fc486bfbda2c059c313a74ff78c0063c0768b99737cab41969c0c87ce","src/css-parsing-tests/stylesheet_bytes.json":"890fd856a596e61f82cf7ed77920ffe95df89209fdb5ee0afe0b26bdfdb80a42","src/css-parsing-tests/urange.json":"62720b143ddf52508baad42921473dd69519aad6c1cd49f37f3f264dc29e1c13","src/from_bytes.rs":"331fe63af2123ae3675b61928a69461b5ac77799fff3ce9978c55cf2c558f4ff","src/lib.rs":"d619e424e665f0c73a6ff5e556f877fe937dc33b2c40ba37762da5f4741c755c","src/macros.rs":"adb9773c157890381556ea83d7942dcc676f99eea71abbb6afeffee1e3f28960","src/nth.rs":"105c7f4295ce8f86a41a3e28a51b98626fdbe34b5b412889796668dcdb02e1a2","src/parser.rs":"2a392ce783121f454c19f8d0c32994e2469db2486f72917310b9cd0e37d41f63","src/rules_and_declarations.rs":"6f270f19e1083a2d7c599b84ca8e9d352fc5ba2f94ac05b406090663ed4b6ea5","src/serializer.rs":"13bf417e747b742576402543e25d7bdf65aa5dd6971455eb9d355169d2182f89","src/tests.rs":"01695d16370ecf9c657f44c0dd87af7b0ec9c0ee6f4c6ab980fc7ac0b7f0f9b0","src/tokenizer.rs":"4ad65f6f96fe7162c10fc6a1451c2ce8670b47b6a520e51382a952246515a217","src/unicode_range.rs":"30c05ad535e3c35d70d698eb0e0f4a73b16dcdd058ac31546857098979cece06"},"package":"bb4a5ca71edbab09f8dc1e3d1c132717562c3b01c8598ab669183c5195bb1761"}
\ No newline at end of file
--- a/third_party/rust/cssparser/Cargo.toml
+++ b/third_party/rust/cssparser/Cargo.toml
@@ -1,60 +1,39 @@
-# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
-#
-# When uploading crates to the registry Cargo will automatically
-# "normalize" Cargo.toml files for maximal compatibility
-# with all versions of Cargo and also rewrite `path` dependencies
-# to registry (e.g. crates.io) dependencies
-#
-# 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]
 
-[package]
 name = "cssparser"
-version = "0.13.7"
-authors = ["Simon Sapin <simon.sapin@exyr.org>"]
-build = "build.rs"
-exclude = ["src/css-parsing-tests"]
+version = "0.14.0"
+authors = [ "Simon Sapin <simon.sapin@exyr.org>" ]
+
 description = "Rust implementation of CSS Syntax Level 3"
 documentation = "https://docs.rs/cssparser/"
+repository = "https://github.com/servo/rust-cssparser"
 readme = "README.md"
 keywords = ["css", "syntax", "parser"]
 license = "MPL-2.0"
-repository = "https://github.com/servo/rust-cssparser"
-[dependencies.serde]
-version = "0.9"
-optional = true
+build = "build.rs"
+
+exclude = ["src/css-parsing-tests"]
 
-[dependencies.cssparser-macros]
-version = "0.3"
-
-[dependencies.heapsize]
-version = ">= 0.3, < 0.5"
-optional = true
-
-[dependencies.procedural-masquerade]
-version = "0.1"
+[dev-dependencies]
+rustc-serialize = "0.3"
+difference = "1.0"
+encoding_rs = "0.5"
 
-[dependencies.matches]
-version = "0.1"
-
-[dependencies.phf]
-version = "0.7"
-[dev-dependencies.encoding_rs]
-version = "0.5"
+[dependencies]
+cssparser-macros = {path = "./macros", version = "0.3"}
+heapsize = {version = ">= 0.3, < 0.5", optional = true}
+matches = "0.1"
+phf = "0.7"
+procedural-masquerade = {path = "./procedural-masquerade", version = "0.1"}
+serde = {version = "0.9", optional = true}
 
-[dev-dependencies.difference]
-version = "1.0"
-
-[dev-dependencies.rustc-serialize]
-version = "0.3"
-[build-dependencies.quote]
-version = "0.3"
-
-[build-dependencies.syn]
-version = "0.11"
+[build-dependencies]
+syn = "0.11"
+quote = "0.3"
 
 [features]
+bench = []
 dummy_match_byte = []
-bench = []
+
+[workspace]
+members = [".", "./macros", "./procedural-masquerade"]
deleted file mode 100644
--- a/third_party/rust/cssparser/Cargo.toml.orig
+++ /dev/null
@@ -1,39 +0,0 @@
-[package]
-
-name = "cssparser"
-version = "0.13.7"
-authors = [ "Simon Sapin <simon.sapin@exyr.org>" ]
-
-description = "Rust implementation of CSS Syntax Level 3"
-documentation = "https://docs.rs/cssparser/"
-repository = "https://github.com/servo/rust-cssparser"
-readme = "README.md"
-keywords = ["css", "syntax", "parser"]
-license = "MPL-2.0"
-build = "build.rs"
-
-exclude = ["src/css-parsing-tests"]
-
-[dev-dependencies]
-rustc-serialize = "0.3"
-difference = "1.0"
-encoding_rs = "0.5"
-
-[dependencies]
-cssparser-macros = {path = "./macros", version = "0.3"}
-heapsize = {version = ">= 0.3, < 0.5", optional = true}
-matches = "0.1"
-phf = "0.7"
-procedural-masquerade = {path = "./procedural-masquerade", version = "0.1"}
-serde = {version = "0.9", optional = true}
-
-[build-dependencies]
-syn = "0.11"
-quote = "0.3"
-
-[features]
-bench = []
-dummy_match_byte = []
-
-[workspace]
-members = [".", "./macros", "./procedural-masquerade"]
--- a/third_party/rust/cssparser/src/color.rs
+++ b/third_party/rust/cssparser/src/color.rs
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use std::fmt;
 use std::f32::consts::PI;
 
-use super::{Token, Parser, ToCss};
+use super::{Token, Parser, ToCss, ParseError, BasicParseError};
 use tokenizer::NumericValue;
 
 #[cfg(feature = "serde")]
 use serde::{Deserialize, Deserializer, Serialize, Serializer};
 
 /// A color with red, green, blue, and alpha components, in a byte each.
 #[derive(Clone, Copy, PartialEq, Debug)]
 pub struct RGBA {
@@ -136,72 +136,74 @@ 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(input: &mut Parser) -> Result<Color, ()> {
-        match try!(input.next()) {
-            Token::Hash(value) | Token::IDHash(value) => {
+    pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Color, BasicParseError<'i>> {
+        let token = try!(input.next());
+        match token {
+            Token::Hash(ref value) | Token::IDHash(ref value) => {
                 Color::parse_hash(value.as_bytes())
             },
-            Token::Ident(value) => parse_color_keyword(&*value),
-            Token::Function(name) => {
-                input.parse_nested_block(|arguments| {
+            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))
+                }).map_err(ParseError::<()>::basic);
             }
             _ => Err(())
-        }
+        }.map_err(|()| BasicParseError::UnexpectedToken(token))
     }
 
     /// Parse a color hash, without the leading '#' character.
     #[inline]
     pub fn parse_hash(value: &[u8]) -> Result<Self, ()> {
         match value.len() {
-            8 => rgba(
-                try!(from_hex(value[0])) * 16 + try!(from_hex(value[1])),
-                try!(from_hex(value[2])) * 16 + try!(from_hex(value[3])),
-                try!(from_hex(value[4])) * 16 + try!(from_hex(value[5])),
-                try!(from_hex(value[6])) * 16 + try!(from_hex(value[7])),
-            ),
-            6 => rgb(
+            8 => Ok(rgba(
                 try!(from_hex(value[0])) * 16 + try!(from_hex(value[1])),
                 try!(from_hex(value[2])) * 16 + try!(from_hex(value[3])),
                 try!(from_hex(value[4])) * 16 + try!(from_hex(value[5])),
+                try!(from_hex(value[6])) * 16 + try!(from_hex(value[7]))),
             ),
-            4 => rgba(
+            6 => Ok(rgb(
+                try!(from_hex(value[0])) * 16 + try!(from_hex(value[1])),
+                try!(from_hex(value[2])) * 16 + try!(from_hex(value[3])),
+                try!(from_hex(value[4])) * 16 + try!(from_hex(value[5]))),
+            ),
+            4 => Ok(rgba(
                 try!(from_hex(value[0])) * 17,
                 try!(from_hex(value[1])) * 17,
                 try!(from_hex(value[2])) * 17,
-                try!(from_hex(value[3])) * 17,
+                try!(from_hex(value[3])) * 17),
             ),
-            3 => rgb(
+            3 => Ok(rgb(
                 try!(from_hex(value[0])) * 17,
                 try!(from_hex(value[1])) * 17,
-                try!(from_hex(value[2])) * 17,
+                try!(from_hex(value[2])) * 17),
             ),
             _ => Err(())
         }
     }
 
 }
 
 
 #[inline]
-fn rgb(red: u8, green: u8, blue: u8) -> Result<Color, ()> {
+fn rgb(red: u8, green: u8, blue: u8) -> Color {
     rgba(red, green, blue, 255)
 }
 
 #[inline]
-fn rgba(red: u8, green: u8, blue: u8, alpha: u8) -> Result<Color, ()> {
-    Ok(Color::RGBA(RGBA::new(red, green, blue, alpha)))
+fn rgba(red: u8, green: u8, blue: u8, alpha: u8) -> Color {
+    Color::RGBA(RGBA::new(red, green, blue, alpha))
 }
 
 
 /// Return the named color with the given name.
 ///
 /// Matching is case-insensitive in the ASCII range.
 /// CSS escaping (if relevant) should be resolved before calling this function.
 /// (For example, the value of an `Ident` token is fine.)
@@ -405,129 +407,131 @@ fn clamp_unit_f32(val: f32) -> u8 {
     clamp_floor_256_f32(val * 255.)
 }
 
 fn clamp_floor_256_f32(val: f32) -> u8 {
     val.round().max(0.).min(255.) as u8
 }
 
 #[inline]
-fn parse_color_function(name: &str, arguments: &mut Parser) -> Result<Color, ()> {
+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(())
+        _ => return Err(BasicParseError::UnexpectedToken(Token::Ident(name.to_owned().into()))),
     };
 
     let alpha = if !arguments.is_exhausted() {
         if uses_commas {
             try!(arguments.expect_comma());
         } else {
             match try!(arguments.next()) {
                 Token::Delim('/') => {},
-                _ => return Err(())
+                t => return Err(BasicParseError::UnexpectedToken(t)),
             };
         };
         let token = try!(arguments.next());
         match token {
             Token::Number(NumericValue { value: v, .. }) => {
                 clamp_unit_f32(v)
             }
             Token::Percentage(ref v) => {
                 clamp_unit_f32(v.unit_value)
             }
-            _ => {
-                return Err(())
+            t => {
+                return Err(BasicParseError::UnexpectedToken(t))
             }
         }
     } else {
         255
     };
 
     try!(arguments.expect_exhausted());
-    rgba(red, green, blue, alpha)
+    Ok(rgba(red, green, blue, alpha))
 }
 
 
 #[inline]
-fn parse_rgb_components_rgb(arguments: &mut Parser) -> Result<(u8, u8, u8, bool), ()> {
+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
     match try!(arguments.next()) {
         Token::Number(NumericValue { value: v, .. }) => {
             red = clamp_floor_256_f32(v);
             green = clamp_floor_256_f32(match try!(arguments.next()) {
                 Token::Number(NumericValue { value: v, .. }) => v,
                 Token::Comma => {
                     uses_commas = true;
                     try!(arguments.expect_number())
                 }
-                _ => return Err(())
+                t => return Err(BasicParseError::UnexpectedToken(t))
             });
             if uses_commas {
                 try!(arguments.expect_comma());
             }
             blue = clamp_floor_256_f32(try!(arguments.expect_number()));
         }
         Token::Percentage(ref v) => {
             red = clamp_unit_f32(v.unit_value);
             green = clamp_unit_f32(match try!(arguments.next()) {
                 Token::Percentage(ref v) => v.unit_value,
                 Token::Comma => {
                     uses_commas = true;
                     try!(arguments.expect_percentage())
                 }
-                _ => return Err(())
+                t => return Err(BasicParseError::UnexpectedToken(t))
             });
             if uses_commas {
                 try!(arguments.expect_comma());
             }
             blue = clamp_unit_f32(try!(arguments.expect_percentage()));
         }
-        _ => return Err(())
+        t => return Err(BasicParseError::UnexpectedToken(t))
     };
     return Ok((red, green, blue, uses_commas));
 }
 
 #[inline]
-fn parse_rgb_components_hsl(arguments: &mut Parser) -> Result<(u8, u8, u8, bool), ()> {
+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 hue_degrees = match try!(arguments.next()) {
-        Token::Number(NumericValue { value: v, .. }) => v,
-        Token::Dimension(NumericValue { value: v, .. }, unit) => {
+    let token = try!(arguments.next());
+    let hue_degrees = match token {
+        Token::Number(NumericValue { value: v, .. }) => Ok(v),
+        Token::Dimension(NumericValue { 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(())
+                "deg" => Ok(v),
+                "grad" => Ok(v * 360. / 400.),
+                "rad" => Ok(v * 360. / (2. * PI)),
+                "turn" => Ok(v * 360.),
+                _ => Err(()),
             }
         }
-        _ => return Err(())
+        t => return Err(BasicParseError::UnexpectedToken(t))
     };
+    let hue_degrees = try!(hue_degrees.map_err(|()| BasicParseError::UnexpectedToken(token)));
     // 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 saturation = match try!(arguments.next()) {
         Token::Percentage(ref v) => v.unit_value,
         Token::Comma => {
             uses_commas = true;
             try!(arguments.expect_percentage())
         }
-        _ => return Err(())
+        t => return Err(BasicParseError::UnexpectedToken(t))
     };
     let saturation = saturation.max(0.).min(1.);
 
     if uses_commas {
         try!(arguments.expect_comma());
     }
 
     let lightness = try!(arguments.expect_percentage());
--- a/third_party/rust/cssparser/src/lib.rs
+++ b/third_party/rust/cssparser/src/lib.rs
@@ -84,17 +84,17 @@ pub use tokenizer::{Token, NumericValue,
 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};
 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, SourcePosition};
+pub use parser::{Parser, Delimiter, Delimiters, SourcePosition, ParseError, BasicParseError, ParserInput};
 pub use unicode_range::UnicodeRange;
 
 // 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
@@ -1,84 +1,106 @@
 /* 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 std::ascii::AsciiExt;
 
-use super::{Token, Parser};
+use super::{Token, Parser, BasicParseError};
 
 
 /// Parse the *An+B* notation, as found in the `:nth-child()` selector.
 /// The input is typically the arguments of a function,
 /// in which case the caller needs to check if the arguments’ parser is exhausted.
 /// Return `Ok((A, B))`, or `Err(())` for a syntax error.
-pub fn parse_nth(input: &mut Parser) -> Result<(i32, i32), ()> {
-    match try!(input.next()) {
-        Token::Number(value) => Ok((0, try!(value.int_value.ok_or(())) as i32)),
-        Token::Dimension(value, unit) => {
-            let a = try!(value.int_value.ok_or(())) as i32;
-            match_ignore_ascii_case! { &unit,
-                "n" => parse_b(input, a),
-                "n-" => parse_signless_b(input, a, -1),
-                _ => Ok((a, try!(parse_n_dash_digits(&*unit))))
+pub fn parse_nth<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(i32, i32), BasicParseError<'i>> {
+    let token = try!(input.next());
+    match token {
+        Token::Number(ref value) => {
+            match value.int_value {
+                Some(v) => Ok((0, v as i32)),
+                None => Err(()),
             }
         }
-        Token::Ident(value) => {
+        Token::Dimension(value, ref unit) => {
+            match value.int_value {
+                Some(v) => {
+                    let a = v as i32;
+                    match_ignore_ascii_case! {
+                        &unit,
+                        "n" => Ok(try!(parse_b(input, a))),
+                        "n-" => Ok(try!(parse_signless_b(input, a, -1))),
+                        _ => {
+                            parse_n_dash_digits(&*unit).map(|val| (a, val))
+                        }
+                    }
+                }
+                None => Err(()),
+            }
+        }
+        Token::Ident(ref value) => {
             match_ignore_ascii_case! { &value,
                 "even" => Ok((2, 0)),
                 "odd" => Ok((2, 1)),
-                "n" => parse_b(input, 1),
-                "-n" => parse_b(input, -1),
-                "n-" => parse_signless_b(input, 1, -1),
-                "-n-" => parse_signless_b(input, -1, -1),
+                "n" => Ok(try!(parse_b(input, 1))),
+                "-n" => Ok(try!(parse_b(input, -1))),
+                "n-" => Ok(try!(parse_signless_b(input, 1, -1))),
+                "-n-" => Ok(try!(parse_signless_b(input, -1, -1))),
                 _ => if value.starts_with("-") {
-                    Ok((-1, try!(parse_n_dash_digits(&value[1..]))))
+                    parse_n_dash_digits(&value[1..]).map(|v| (-1, v))
                 } else {
-                    Ok((1, try!(parse_n_dash_digits(&*value))))
+                    parse_n_dash_digits(&*value).map(|v| (1, v))
                 }
             }
         }
         Token::Delim('+') => match try!(input.next_including_whitespace()) {
             Token::Ident(value) => {
                 match_ignore_ascii_case! { &value,
-                    "n" => parse_b(input, 1),
-                    "n-" => parse_signless_b(input, 1, -1),
-                    _ => Ok((1, try!(parse_n_dash_digits(&*value))))
+                    "n" => Ok(try!(parse_b(input, 1))),
+                    "n-" => Ok(try!(parse_signless_b(input, 1, -1))),
+                    _ => parse_n_dash_digits(&*value).map(|v| (1, v))
                 }
             }
-            _ => Err(())
+            t => return Err(BasicParseError::UnexpectedToken(t)),
         },
-        _ => Err(())
-    }
+        _ => Err(()),
+    }.map_err(|()| BasicParseError::UnexpectedToken(token))
 }
 
 
-fn parse_b(input: &mut Parser, a: i32) -> Result<(i32, i32), ()> {
+fn parse_b<'i, 't>(input: &mut Parser<'i, 't>, a: i32) -> Result<(i32, i32), BasicParseError<'i>> {
     let start_position = input.position();
-    match input.next() {
-        Ok(Token::Delim('+')) => parse_signless_b(input, a, 1),
-        Ok(Token::Delim('-')) => parse_signless_b(input, a, -1),
+    let token = input.next();
+    match token {
+        Ok(Token::Delim('+')) => Ok(try!(parse_signless_b(input, a, 1))),
+        Ok(Token::Delim('-')) => Ok(try!(parse_signless_b(input, a, -1))),
         Ok(Token::Number(ref value)) if value.has_sign => {
-            Ok((a, try!(value.int_value.ok_or(())) as i32))
+            match value.int_value {
+                Some(v) => Ok((a, v as i32)),
+                None => Err(()),
+            }
         }
         _ => {
             input.reset(start_position);
             Ok((a, 0))
         }
-    }
+    }.map_err(|()| BasicParseError::UnexpectedToken(token.unwrap()))
 }
 
-fn parse_signless_b(input: &mut Parser, a: i32, b_sign: i32) -> Result<(i32, i32), ()> {
-    match try!(input.next()) {
+fn parse_signless_b<'i, 't>(input: &mut Parser<'i, 't>, a: i32, b_sign: i32) -> Result<(i32, i32), BasicParseError<'i>> {
+    let token = try!(input.next());
+    match token {
         Token::Number(ref value) if !value.has_sign => {
-            Ok((a, b_sign * (try!(value.int_value.ok_or(())) as i32)))
+            match value.int_value {
+                Some(v) => Ok((a, b_sign * v as i32)),
+                None => Err(()),
+            }
         }
         _ => Err(())
-    }
+    }.map_err(|()| BasicParseError::UnexpectedToken(token))
 }
 
 fn parse_n_dash_digits(string: &str) -> Result<i32, ()> {
     if string.len() >= 3
     && string[..2].eq_ignore_ascii_case("n-")
     && string[2..].chars().all(|c| matches!(c, '0'...'9'))
     {
         Ok(string[1..].parse().unwrap())  // Include the minus sign
--- a/third_party/rust/cssparser/src/parser.rs
+++ b/third_party/rust/cssparser/src/parser.rs
@@ -1,72 +1,85 @@
 /* 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 std::ops::Range;
 use std::ascii::AsciiExt;
 use std::ops::BitOr;
 use std::borrow::Cow;
-use std::ops;
 use tokenizer::{self, Token, NumericValue, PercentageValue, Tokenizer, SourceLocation};
 
 
 /// A capture of the internal state of a `Parser` (including the position within the input),
 /// obtained from the `Parser::position` method.
 ///
 /// Can be used with the `Parser::reset` method to restore that state.
 /// Should only be used with the `Parser` instance it came from.
 #[derive(PartialEq, Eq, Debug, Clone, Copy)]
 pub struct SourcePosition {
     position: tokenizer::SourcePosition,
     at_start_of: Option<BlockType>,
 }
 
-
-/// Like std::borrow::Cow, except the borrowed variant contains a mutable
-/// reference.
-enum MaybeOwned<'a, T: 'a> {
-    Owned(T),
-    Borrowed(&'a mut T),
+/// The funamental parsing errors that can be triggered by built-in parsing routines.
+#[derive(Clone, Debug, PartialEq)]
+pub enum BasicParseError<'a> {
+    /// An unexpected token was encountered.
+    UnexpectedToken(Token<'a>),
+    /// A particular token was expected but not found.
+    ExpectedToken(Token<'a>),
+    /// The end of the input was encountered unexpectedly.
+    EndOfInput,
+    /// An `@` rule was encountered that was invalid.
+    AtRuleInvalid,
+    /// A qualified rule was encountered that was invalid.
+    QualifiedRuleInvalid,
 }
 
-impl<'a, T> ops::Deref for MaybeOwned<'a, T> {
-    type Target = T;
+impl<'a, T> From<BasicParseError<'a>> for ParseError<'a, T> {
+    fn from(this: BasicParseError<'a>) -> ParseError<'a, T> {
+        ParseError::Basic(this)
+    }
+}
 
-    fn deref<'b>(&'b self) -> &'b T {
-        match *self {
-            MaybeOwned::Owned(ref t) => t,
-            MaybeOwned::Borrowed(ref pointer) => &**pointer,
+/// 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),
+}
+
+impl<'a, T> ParseError<'a, 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"),
         }
     }
 }
 
-impl<'a, T> ops::DerefMut for MaybeOwned<'a, T> {
-    fn deref_mut<'b>(&'b mut self) -> &'b mut T {
-        match *self {
-            MaybeOwned::Owned(ref mut t) => t,
-            MaybeOwned::Borrowed(ref mut pointer) => &mut **pointer,
-        }
+/// The owned input for a parser.
+pub struct ParserInput<'t>(Tokenizer<'t>);
+
+impl<'t> ParserInput<'t> {
+    /// Create a new input for a parser.
+    pub fn new(input: &'t str) -> ParserInput<'t> {
+        ParserInput(Tokenizer::new(input))
     }
 }
 
-impl<'a, T> Clone for MaybeOwned<'a, T> where T: Clone {
-    fn clone(&self) -> MaybeOwned<'a, T> {
-        MaybeOwned::Owned((**self).clone())
-    }
-}
-
-
 /// A CSS parser that borrows its `&str` input,
 /// yields `Token`s,
 /// and keeps track of nested blocks and functions.
-#[derive(Clone)]
 pub struct Parser<'i: 't, 't> {
-    tokenizer: MaybeOwned<'t, Tokenizer<'i>>,
+    tokenizer: &'t mut ParserInput<'i>,
     /// If `Some(_)`, .parse_nested_block() can be called.
     at_start_of: Option<BlockType>,
     /// For parsers from `parse_until` or `parse_nested_block`
     stop_before: Delimiters,
 }
 
 
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
@@ -159,22 +172,22 @@ impl Delimiters {
             Some(b'}') => ClosingDelimiter::CloseCurlyBracket,
             Some(b']') => ClosingDelimiter::CloseSquareBracket,
             Some(b')') => ClosingDelimiter::CloseParenthesis,
             _ => Delimiter::None,
         }
     }
 }
 
-impl<'i, 't> Parser<'i, 't> {
+impl<'i: 't, 't> Parser<'i, 't> {
     /// Create a new parser
     #[inline]
-    pub fn new(input: &'i str) -> Parser<'i, 'i> {
+    pub fn new(input: &'t mut ParserInput<'i>) -> Parser<'i, 't> {
         Parser {
-            tokenizer: MaybeOwned::Owned(Tokenizer::new(input)),
+            tokenizer: input,
             at_start_of: None,
             stop_before: Delimiter::None,
         }
     }
 
     /// Check whether the input is exhausted. That is, if `.next()` would return a token.
     ///
     /// This ignores whitespace and comments.
@@ -183,74 +196,73 @@ impl<'i, 't> Parser<'i, 't> {
         self.expect_exhausted().is_ok()
     }
 
     /// 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<(), ()> {
+    pub fn expect_exhausted(&mut self) -> Result<(), BasicParseError<'i>> {
         let start_position = self.position();
         let result = match self.next() {
-            Err(()) => Ok(()),
-            Ok(_) => {
-                Err(())
-            }
+            Err(BasicParseError::EndOfInput) => Ok(()),
+            Err(e) => unreachable!("Unexpected error encountered: {:?}", e),
+            Ok(t) => Err(BasicParseError::UnexpectedToken(t)),
         };
         self.reset(start_position);
         result
     }
 
     /// 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 position(&self) -> SourcePosition {
         SourcePosition {
-            position: self.tokenizer.position(),
+            position: (self.tokenizer.0).position(),
             at_start_of: self.at_start_of,
         }
     }
 
     /// Restore the internal state of the parser (including position within the input)
     /// to what was previously saved by the `Parser::position` method.
     ///
     /// Should only be used with `SourcePosition` values from the same `Parser` instance.
     #[inline]
     pub fn reset(&mut self, new_position: SourcePosition) {
-        self.tokenizer.reset(new_position.position);
+        (self.tokenizer.0).reset(new_position.position);
         self.at_start_of = new_position.at_start_of;
     }
 
     /// Start looking for `var()` functions. (See the `.seen_var_functions()` method.)
     #[inline]
     pub fn look_for_var_functions(&mut self) {
-        self.tokenizer.look_for_var_functions()
+        (self.tokenizer.0).look_for_var_functions()
     }
 
     /// Return whether a `var()` function has been seen by the tokenizer since
     /// either `look_for_var_functions` was called, and stop looking.
     #[inline]
     pub fn seen_var_functions(&mut self) -> bool {
-        self.tokenizer.seen_var_functions()
+        (self.tokenizer.0).seen_var_functions()
     }
 
     /// Start looking for viewport percentage lengths. (See the `seen_viewport_percentages`
     /// method.)
     #[inline]
     pub fn look_for_viewport_percentages(&mut self) {
-        self.tokenizer.look_for_viewport_percentages()
+        (self.tokenizer.0).look_for_viewport_percentages()
     }
 
     /// Return whether a `vh`, `vw`, `vmin`, or `vmax` dimension has been seen by the tokenizer
     /// since `look_for_viewport_percentages` was called, and stop looking.
     #[inline]
     pub fn seen_viewport_percentages(&mut self) -> bool {
-        self.tokenizer.seen_viewport_percentages()
+        (self.tokenizer.0).seen_viewport_percentages()
     }
 
     /// Execute the given closure, passing it the parser.
     /// If the result (returned unchanged) is `Err`,
     /// the internal state of the parser  (including position within the input)
     /// is restored to what it was before the call.
     #[inline]
     pub fn try<F, T, E>(&mut self, thing: F) -> Result<T, E>
@@ -261,117 +273,118 @@ impl<'i, 't> Parser<'i, 't> {
             self.reset(start_position)
         }
         result
     }
 
     /// Return a slice of the CSS input
     #[inline]
     pub fn slice(&self, range: Range<SourcePosition>) -> &'i str {
-        self.tokenizer.slice(range.start.position..range.end.position)
+        (self.tokenizer.0).slice(range.start.position..range.end.position)
     }
 
     /// Return a slice of the CSS input, from the given position to the current one.
     #[inline]
     pub fn slice_from(&self, start_position: SourcePosition) -> &'i str {
-        self.tokenizer.slice_from(start_position.position)
+        (self.tokenizer.0).slice_from(start_position.position)
     }
 
     /// Return the line and column number within the input for the current position.
     #[inline]
     pub fn current_source_location(&self) -> SourceLocation {
-        self.tokenizer.current_source_location()
+        (self.tokenizer.0).current_source_location()
     }
 
     /// Return the line and column number within the input for the given position.
     #[inline]
     pub fn source_location(&self, target: SourcePosition) -> SourceLocation {
-        self.tokenizer.source_location(target.position)
+        (self.tokenizer.0).source_location(target.position)
     }
 
     /// Return the next token in the input that is neither whitespace or a comment,
     /// and advance the position accordingly.
     ///
     /// After returning a `Function`, `ParenthesisBlock`,
     /// `CurlyBracketBlock`, or `SquareBracketBlock` token,
     /// the next call will skip until after the matching `CloseParenthesis`,
     /// `CloseCurlyBracket`, or `CloseSquareBracket` token.
     ///
     /// See the `Parser::parse_nested_block` method to parse the content of functions or blocks.
     ///
     /// This only returns a closing token when it is unmatched (and therefore an error).
-    pub fn next(&mut self) -> Result<Token<'i>, ()> {
+    pub fn next(&mut self) -> Result<Token<'i>, BasicParseError<'i>> {
         loop {
             match self.next_including_whitespace_and_comments() {
                 Ok(Token::WhiteSpace(_)) | Ok(Token::Comment(_)) => {},
                 result => return result
             }
         }
     }
 
     /// Same as `Parser::next`, but does not skip whitespace tokens.
-    pub fn next_including_whitespace(&mut self) -> Result<Token<'i>, ()> {
+    pub fn next_including_whitespace(&mut self) -> Result<Token<'i>, BasicParseError<'i>> {
         loop {
             match self.next_including_whitespace_and_comments() {
                 Ok(Token::Comment(_)) => {},
                 result => return result
             }
         }
     }
 
     /// Same as `Parser::next`, but does not skip whitespace or comment tokens.
     ///
     /// **Note**: This should only be used in contexts like a CSS pre-processor
     /// where comments are preserved.
     /// When parsing higher-level values, per the CSS Syntax specification,
     /// comments should always be ignored between tokens.
-    pub fn next_including_whitespace_and_comments(&mut self) -> Result<Token<'i>, ()> {
+    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.tokenizer);
+            consume_until_end_of_block(block_type, &mut self.tokenizer.0);
         }
-        if self.stop_before.contains(Delimiters::from_byte(self.tokenizer.next_byte())) {
-            return Err(())
+        let byte = (self.tokenizer.0).next_byte();
+        if self.stop_before.contains(Delimiters::from_byte(byte)) {
+            return Err(BasicParseError::EndOfInput)
         }
-        let token = try!(self.tokenizer.next());
+        let token = try!((self.tokenizer.0).next().map_err(|()| BasicParseError::EndOfInput));
         if let Some(block_type) = BlockType::opening(&token) {
             self.at_start_of = Some(block_type);
         }
         Ok(token)
     }
 
     /// Have the given closure parse something, then check the the input is exhausted.
     /// The result is overridden to `Err(())` if some input remains.
     ///
     /// This can help tell e.g. `color: green;` from `color: green 4px;`
     #[inline]
-    pub fn parse_entirely<F, T>(&mut self, parse: F) -> Result<T, ()>
-    where F: FnOnce(&mut Parser<'i, 't>) -> Result<T, ()> {
+    pub fn parse_entirely<F, T, E>(&mut self, parse: F) -> Result<T, ParseError<'i, E>>
+    where F: FnOnce(&mut Parser<'i, 't>) -> Result<T, ParseError<'i, E>> {
         let result = parse(self);
         try!(self.expect_exhausted());
         result
     }
 
     /// Parse a list of comma-separated values, all with the same syntax.
     ///
     /// The given closure is called repeatedly with a "delimited" parser
     /// (see the `Parser::parse_until_before` method)
     /// so that it can over consume the input past a comma at this block/function nesting level.
     ///
     /// Successful results are accumulated in a vector.
     ///
     /// This method retuns `Err(())` the first time that a closure call does,
     /// or if a closure call leaves some input before the next comma or the end of the input.
     #[inline]
-    pub fn parse_comma_separated<F, T>(&mut self, mut parse_one: F) -> Result<Vec<T>, ()>
-    where F: FnMut(&mut Parser) -> Result<T, ()> {
+    pub fn parse_comma_separated<F, T, E>(&mut self, mut parse_one: F) -> Result<Vec<T>, ParseError<'i, E>>
+    where F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>> {
         let mut values = vec![];
         loop {
-            values.push(try!(self.parse_until_before(Delimiter::Comma, |parser| parse_one(parser))));
+            values.push(try!(self.parse_until_before(Delimiter::Comma, &mut parse_one)));
             match self.next() {
-                Err(()) => return Ok(values),
+                Err(_) => return Ok(values),
                 Ok(Token::Comma) => continue,
                 Ok(_) => unreachable!(),
             }
         }
     }
 
     /// Parse the content of a block or function.
     ///
@@ -380,319 +393,349 @@ impl<'i, 't> Parser<'i, 't> {
     /// is not a on that marks the start of a block or function:
     /// a `Function`, `ParenthesisBlock`, `CurlyBracketBlock`, or `SquareBracketBlock`.
     ///
     /// The given closure is called with a "delimited" parser
     /// that stops at the end of the block or function (at the matching closing token).
     ///
     /// The result is overridden to `Err(())` if the closure leaves some input before that point.
     #[inline]
-    pub fn parse_nested_block<F, T>(&mut self, parse: F) -> Result <T, ()>
-    where F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result<T, ()> {
-        let block_type = self.at_start_of.take().expect("\
-            A nested parser can only be created when a Function, \
-            ParenthesisBlock, SquareBracketBlock, or CurlyBracketBlock \
-            token was just consumed.\
-        ");
-        let closing_delimiter = match block_type {
-            BlockType::CurlyBracket => ClosingDelimiter::CloseCurlyBracket,
-            BlockType::SquareBracket => ClosingDelimiter::CloseSquareBracket,
-            BlockType::Parenthesis => ClosingDelimiter::CloseParenthesis,
-        };
-        let result;
-        // Introduce a new scope to limit duration of nested_parser’s borrow
-        {
-            let mut nested_parser = Parser {
-                tokenizer: MaybeOwned::Borrowed(&mut *self.tokenizer),
-                at_start_of: None,
-                stop_before: closing_delimiter,
-            };
-            result = nested_parser.parse_entirely(parse);
-            if let Some(block_type) = nested_parser.at_start_of {
-                consume_until_end_of_block(block_type, &mut *nested_parser.tokenizer);
-            }
-        }
-        consume_until_end_of_block(block_type, &mut *self.tokenizer);
-        result
+    pub fn parse_nested_block<F, T, E>(&mut self, parse: F) -> Result <T, ParseError<'i, E>>
+    where F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>> {
+        parse_nested_block(self, parse)
     }
 
     /// Limit parsing to until a given delimiter. (E.g. a semicolon for a property value.)
     ///
     /// The given closure is called with a "delimited" parser
     /// that stops before the first character at this block/function nesting level
     /// that matches the given set of delimiters.
     ///
     /// The result is overridden to `Err(())` if the closure leaves some input before that point.
     #[inline]
-    pub fn parse_until_before<F, T>(&mut self, delimiters: Delimiters, parse: F)
-                                    -> Result <T, ()>
-    where F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result<T, ()> {
-        let delimiters = self.stop_before | delimiters;
-        let result;
-        // Introduce a new scope to limit duration of nested_parser’s borrow
-        {
-            let mut delimited_parser = Parser {
-                tokenizer: MaybeOwned::Borrowed(&mut *self.tokenizer),
-                at_start_of: self.at_start_of.take(),
-                stop_before: delimiters,
-            };
-            result = delimited_parser.parse_entirely(parse);
-            if let Some(block_type) = delimited_parser.at_start_of {
-                consume_until_end_of_block(block_type, &mut *delimited_parser.tokenizer);
-            }
-        }
-        // FIXME: have a special-purpose tokenizer method for this that does less work.
-        loop {
-            if delimiters.contains(Delimiters::from_byte(self.tokenizer.next_byte())) {
-                break
-            }
-            if let Ok(token) = self.tokenizer.next() {
-                if let Some(block_type) = BlockType::opening(&token) {
-                    consume_until_end_of_block(block_type, &mut *self.tokenizer);
-                }
-            } else {
-                break
-            }
-        }
-        result
+    pub fn parse_until_before<F, T, E>(&mut self, delimiters: Delimiters, parse: F)
+                                    -> Result <T, ParseError<'i, E>>
+    where F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>> {
+        parse_until_before(self, delimiters, parse)
     }
 
     /// Like `parse_until_before`, but also consume the delimiter token.
     ///
     /// This can be useful when you don’t need to know which delimiter it was
     /// (e.g. if these is only one in the given set)
     /// or if it was there at all (as opposed to reaching the end of the input).
     #[inline]
-    pub fn parse_until_after<F, T>(&mut self, delimiters: Delimiters, parse: F)
-                                   -> Result <T, ()>
-    where F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result<T, ()> {
-        let result = self.parse_until_before(delimiters, parse);
-        let next_byte = self.tokenizer.next_byte();
-        if next_byte.is_some() && !self.stop_before.contains(Delimiters::from_byte(next_byte)) {
-            debug_assert!(delimiters.contains(Delimiters::from_byte(next_byte)));
-            self.tokenizer.advance(1);
-            if next_byte == Some(b'{') {
-                consume_until_end_of_block(BlockType::CurlyBracket, &mut *self.tokenizer);
-            }
-        }
-        result
+    pub fn parse_until_after<F, T, E>(&mut self, delimiters: Delimiters, parse: F)
+                                   -> 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, ()> {
+    pub fn expect_whitespace(&mut self) -> Result<&'i str, BasicParseError<'i>> {
         match try!(self.next_including_whitespace()) {
             Token::WhiteSpace(value) => Ok(value),
-            _ => Err(())
+            t => Err(BasicParseError::UnexpectedToken(t))
         }
     }
 
     /// Parse a <ident-token> and return the unescaped value.
     #[inline]
-    pub fn expect_ident(&mut self) -> Result<Cow<'i, str>, ()> {
+    pub fn expect_ident(&mut self) -> Result<Cow<'i, str>, BasicParseError<'i>> {
         match try!(self.next()) {
             Token::Ident(value) => Ok(value),
-            _ => Err(())
+            t => Err(BasicParseError::UnexpectedToken(t))
         }
     }
 
     /// 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<(), ()> {
+    pub fn expect_ident_matching(&mut self, expected_value: &str) -> Result<(), BasicParseError<'i>> {
         match try!(self.next()) {
             Token::Ident(ref value) if value.eq_ignore_ascii_case(expected_value) => Ok(()),
-            _ => Err(())
+            t => Err(BasicParseError::UnexpectedToken(t))
         }
     }
 
     /// Parse a <string-token> and return the unescaped value.
     #[inline]
-    pub fn expect_string(&mut self) -> Result<Cow<'i, str>, ()> {
+    pub fn expect_string(&mut self) -> Result<Cow<'i, str>, BasicParseError<'i>> {
         match try!(self.next()) {
             Token::QuotedString(value) => Ok(value),
-            _ => Err(())
+            t => Err(BasicParseError::UnexpectedToken(t))
         }
     }
 
     /// Parse either a <ident-token> or a <string-token>, and return the unescaped value.
     #[inline]
-    pub fn expect_ident_or_string(&mut self) -> Result<Cow<'i, str>, ()> {
+    pub fn expect_ident_or_string(&mut self) -> Result<Cow<'i, str>, BasicParseError<'i>> {
         match try!(self.next()) {
             Token::Ident(value) => Ok(value),
             Token::QuotedString(value) => Ok(value),
-            _ => Err(())
+            t => Err(BasicParseError::UnexpectedToken(t))
         }
     }
 
     /// Parse a <url-token> and return the unescaped value.
     #[inline]
-    pub fn expect_url(&mut self) -> Result<Cow<'i, str>, ()> {
+    pub fn expect_url(&mut self) -> Result<Cow<'i, str>, BasicParseError<'i>> {
         match try!(self.next()) {
             Token::UnquotedUrl(value) => Ok(value),
             Token::Function(ref name) if name.eq_ignore_ascii_case("url") => {
-                self.parse_nested_block(|input| input.expect_string())
+                self.parse_nested_block(|input| input.expect_string()
+                                        .map_err(|e| ParseError::Basic(e)))
+                    .map_err(ParseError::<()>::basic)
             },
-            _ => Err(())
+            t => Err(BasicParseError::UnexpectedToken(t))
         }
     }
 
     /// Parse either a <url-token> or a <string-token>, and return the unescaped value.
     #[inline]
-    pub fn expect_url_or_string(&mut self) -> Result<Cow<'i, str>, ()> {
+    pub fn expect_url_or_string(&mut self) -> Result<Cow<'i, str>, BasicParseError<'i>> {
         match try!(self.next()) {
             Token::UnquotedUrl(value) => Ok(value),
             Token::QuotedString(value) => Ok(value),
             Token::Function(ref name) if name.eq_ignore_ascii_case("url") => {
-                self.parse_nested_block(|input| input.expect_string())
+                self.parse_nested_block(|input| input.expect_string().map_err(|e| ParseError::Basic(e)))
+                    .map_err(ParseError::<()>::basic)
             },
-            _ => Err(())
+            t => Err(BasicParseError::UnexpectedToken(t))
         }
     }
 
     /// Parse a <number-token> and return the integer value.
     #[inline]
-    pub fn expect_number(&mut self) -> Result<f32, ()> {
+    pub fn expect_number(&mut self) -> Result<f32, BasicParseError<'i>> {
         match try!(self.next()) {
             Token::Number(NumericValue { value, .. }) => Ok(value),
-            _ => Err(())
+            t => Err(BasicParseError::UnexpectedToken(t))
         }
     }
 
     /// 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, ()> {
-        match try!(self.next()) {
-            Token::Number(NumericValue { int_value, .. }) => int_value.ok_or(()),
-            _ => Err(())
+    pub fn expect_integer(&mut self) -> Result<i32, BasicParseError<'i>> {
+        let token = try!(self.next());
+        match token {
+            Token::Number(NumericValue { int_value: Some(int_value), .. }) => {
+                Ok(int_value)
+            }
+            t => Err(BasicParseError::UnexpectedToken(t))
         }
     }
 
     /// 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, ()> {
+    pub fn expect_percentage(&mut self) -> Result<f32, BasicParseError<'i>> {
         match try!(self.next()) {
             Token::Percentage(PercentageValue { unit_value, .. }) => Ok(unit_value),
-            _ => Err(())
+            t => Err(BasicParseError::UnexpectedToken(t))
         }
     }
 
     /// Parse a `:` <colon-token>.
     #[inline]
-    pub fn expect_colon(&mut self) -> Result<(), ()> {
+    pub fn expect_colon(&mut self) -> Result<(), BasicParseError<'i>> {
         match try!(self.next()) {
             Token::Colon => Ok(()),
-            _ => Err(())
+            t => Err(BasicParseError::UnexpectedToken(t))
         }
     }
 
     /// Parse a `;` <semicolon-token>.
     #[inline]
-    pub fn expect_semicolon(&mut self) -> Result<(), ()> {
+    pub fn expect_semicolon(&mut self) -> Result<(), BasicParseError<'i>> {
         match try!(self.next()) {
             Token::Semicolon => Ok(()),
-            _ => Err(())
+            t => Err(BasicParseError::UnexpectedToken(t))
         }
     }
 
     /// Parse a `,` <comma-token>.
     #[inline]
-    pub fn expect_comma(&mut self) -> Result<(), ()> {
+    pub fn expect_comma(&mut self) -> Result<(), BasicParseError<'i>> {
         match try!(self.next()) {
             Token::Comma => Ok(()),
-            _ => Err(())
+            t => Err(BasicParseError::UnexpectedToken(t))
         }
     }
 
     /// Parse a <delim-token> with the given value.
     #[inline]
-    pub fn expect_delim(&mut self, expected_value: char) -> Result<(), ()> {
+    pub fn expect_delim(&mut self, expected_value: char) -> Result<(), BasicParseError<'i>> {
         match try!(self.next()) {
             Token::Delim(value) if value == expected_value => Ok(()),
-            _ => Err(())
+            t => Err(BasicParseError::UnexpectedToken(t))
         }
     }
 
     /// 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<(), ()> {
+    pub fn expect_curly_bracket_block(&mut self) -> Result<(), BasicParseError<'i>> {
         match try!(self.next()) {
             Token::CurlyBracketBlock => Ok(()),
-            _ => Err(())
+            t => Err(BasicParseError::UnexpectedToken(t))
         }
     }
 
     /// 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<(), ()> {
+    pub fn expect_square_bracket_block(&mut self) -> Result<(), BasicParseError<'i>> {
         match try!(self.next()) {
             Token::SquareBracketBlock => Ok(()),
-            _ => Err(())
+            t => Err(BasicParseError::UnexpectedToken(t))
         }
     }
 
     /// 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<(), ()> {
+    pub fn expect_parenthesis_block(&mut self) -> Result<(), BasicParseError<'i>> {
         match try!(self.next()) {
             Token::ParenthesisBlock => Ok(()),
-            _ => Err(())
+            t => Err(BasicParseError::UnexpectedToken(t))
         }
     }
 
     /// 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<Cow<'i, str>, ()> {
+    pub fn expect_function(&mut self) -> Result<Cow<'i, str>, BasicParseError<'i>> {
         match try!(self.next()) {
             Token::Function(name) => Ok(name),
-            _ => Err(())
+            t => Err(BasicParseError::UnexpectedToken(t))
         }
     }
 
     /// 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<(), ()> {
+    pub fn expect_function_matching(&mut self, expected_name: &str) -> Result<(), BasicParseError<'i>> {
         match try!(self.next()) {
             Token::Function(ref name) if name.eq_ignore_ascii_case(expected_name) => Ok(()),
-            _ => Err(())
+            t => Err(BasicParseError::UnexpectedToken(t))
         }
     }
 
     /// 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<(), ()> {
+    pub fn expect_no_error_token(&mut self) -> Result<(), BasicParseError<'i>> {
         loop {
             match self.next_including_whitespace_and_comments() {
                 Ok(Token::Function(_)) | Ok(Token::ParenthesisBlock) |
                 Ok(Token::SquareBracketBlock) | Ok(Token::CurlyBracketBlock) => {
-                    try!(self.parse_nested_block(|input| input.expect_no_error_token()))
+                    let result = self.parse_nested_block(|input| input.expect_no_error_token()
+                                                         .map_err(|e| ParseError::Basic(e)));
+                    try!(result.map_err(ParseError::<()>::basic))
                 }
                 Ok(token) => {
                     if token.is_parse_error() {
-                        return Err(())
+                        //FIXME: maybe these should be separate variants of BasicParseError instead?
+                        return Err(BasicParseError::UnexpectedToken(token))
                     }
                 }
-                Err(()) => return Ok(())
+                Err(_) => return Ok(())
             }
         }
     }
 }
 
+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>> {
+    let delimiters = parser.stop_before | delimiters;
+    let result;
+    // Introduce a new scope to limit duration of nested_parser’s borrow
+    {
+        let mut delimited_parser = Parser {
+            tokenizer: parser.tokenizer,
+            at_start_of: parser.at_start_of.take(),
+            stop_before: delimiters,
+        };
+        result = delimited_parser.parse_entirely(parse);
+        if let Some(block_type) = delimited_parser.at_start_of {
+            consume_until_end_of_block(block_type, &mut delimited_parser.tokenizer.0);
+        }
+    }
+    // FIXME: have a special-purpose tokenizer method for this that does less work.
+    loop {
+        if delimiters.contains(Delimiters::from_byte((parser.tokenizer.0).next_byte())) {
+            break
+        }
+        if let Ok(token) = (parser.tokenizer.0).next() {
+            if let Some(block_type) = BlockType::opening(&token) {
+                consume_until_end_of_block(block_type, &mut parser.tokenizer.0);
+            }
+        } else {
+            break
+        }
+    }
+    result
+}
+
+pub fn parse_until_after<'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>> {
+    let result = parser.parse_until_before(delimiters, parse);
+    let next_byte = (parser.tokenizer.0).next_byte();
+    if next_byte.is_some() && !parser.stop_before.contains(Delimiters::from_byte(next_byte)) {
+        debug_assert!(delimiters.contains(Delimiters::from_byte(next_byte)));
+        (parser.tokenizer.0).advance(1);
+        if next_byte == Some(b'{') {
+            consume_until_end_of_block(BlockType::CurlyBracket, &mut parser.tokenizer.0);
+        }
+    }
+    result
+}
+
+pub fn parse_nested_block<'i: 't, 't, F, T, E>(parser: &mut Parser<'i, 't>, parse: F)
+                                               -> Result <T, ParseError<'i, E>>
+    where F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>> {
+    let block_type = parser.at_start_of.take().expect("\
+        A nested parser can only be created when a Function, \
+        ParenthesisBlock, SquareBracketBlock, or CurlyBracketBlock \
+        token was just consumed.\
+        ");
+    let closing_delimiter = match block_type {
+        BlockType::CurlyBracket => ClosingDelimiter::CloseCurlyBracket,
+        BlockType::SquareBracket => ClosingDelimiter::CloseSquareBracket,
+        BlockType::Parenthesis => ClosingDelimiter::CloseParenthesis,
+    };
+    let result;
+    // Introduce a new scope to limit duration of nested_parser’s borrow
+    {
+        let mut nested_parser = Parser {
+            tokenizer: parser.tokenizer,
+            at_start_of: None,
+            stop_before: closing_delimiter,
+        };
+        result = nested_parser.parse_entirely(parse);
+        if let Some(block_type) = nested_parser.at_start_of {
+            consume_until_end_of_block(block_type, &mut nested_parser.tokenizer.0);
+        }
+    }
+    consume_until_end_of_block(block_type, &mut parser.tokenizer.0);
+    result
+}
 
 fn consume_until_end_of_block(block_type: BlockType, tokenizer: &mut Tokenizer) {
     let mut stack = vec![block_type];
 
     // FIXME: have a special-purpose tokenizer method for this that does less work.
     while let Ok(ref token) = tokenizer.next() {
         if let Some(b) = BlockType::closing(token) {
             if *stack.last().unwrap() == b {
--- a/third_party/rust/cssparser/src/rules_and_declarations.rs
+++ b/third_party/rust/cssparser/src/rules_and_declarations.rs
@@ -1,25 +1,26 @@
 /* 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/. */
 
 // https://drafts.csswg.org/css-syntax/#parsing
 
+use parser::{parse_until_before, parse_until_after, parse_nested_block};
 use std::ascii::AsciiExt;
 use std::ops::Range;
 use std::borrow::Cow;
-use super::{Token, Parser, Delimiter, SourcePosition};
+use super::{Token, Parser, Delimiter, SourcePosition, ParseError, BasicParseError};
 
 
 /// Parse `!important`.
 ///
 /// Typical usage is `input.try(parse_important).is_ok()`
 /// at the end of a `DeclarationParser::parse_value` implementation.
-pub fn parse_important(input: &mut Parser) -> Result<(), ()> {
+pub fn parse_important<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(), BasicParseError<'i>> {
     try!(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.
@@ -38,25 +39,27 @@ pub enum AtRuleType<P, R> {
     /// The at-rule may either have a block or end with a semicolon.
     ///
     /// This is mostly for testing. As of this writing no real CSS at-rule behaves like this.
     ///
     /// The value is the representation of the "prelude" part of the rule.
     OptionalBlock(P),
 }
 
-
 /// A trait to provide various parsing of declaration values.
 ///
 /// For example, there could be different implementations for property declarations in style rules
 /// and for descriptors in `@font-face` rules.
-pub trait DeclarationParser {
+pub trait DeclarationParser<'i> {
     /// The finished representation of a declaration.
     type Declaration;
 
+    /// The error type that is included in the ParseError value that can be returned.
+    type Error: 'i;
+
     /// Parse the value of a declaration with the given `name`.
     ///
     /// Return the finished representation for the declaration
     /// as returned by `DeclarationListParser::next`,
     /// or `Err(())` to ignore the entire declaration as invalid.
     ///
     /// Declaration name matching should be case-insensitive in the ASCII range.
     /// This can be done with `std::ascii::Ascii::eq_ignore_ascii_case`,
@@ -64,36 +67,39 @@ pub trait DeclarationParser {
     ///
     /// The given `input` is a "delimited" parser
     /// that ends wherever the declaration value should end.
     /// (In declaration lists, before the next semicolon or end of the current block.)
     ///
     /// If `!important` can be used in a given context,
     /// `input.try(parse_important).is_ok()` should be used at the end
     /// of the implementation of this method and the result should be part of the return value.
-    fn parse_value(&mut self, name: &str, input: &mut Parser) -> Result<Self::Declaration, ()>;
+    fn parse_value<'t>(&mut self, name: Cow<'i, str>, input: &mut Parser<'i, 't>)
+                       -> Result<Self::Declaration, ParseError<'i, Self::Error>>;
 }
 
-
 /// A trait to provide various parsing of at-rules.
 ///
 /// For example, there could be different implementations for top-level at-rules
 /// (`@media`, `@font-face`, …)
 /// and for page-margin rules inside `@page`.
 ///
 /// Default implementations that reject all at-rules are provided,
 /// so that `impl AtRuleParser<(), ()> for ... {}` can be used
 /// for using `DeclarationListParser` to parse a declartions list with only qualified rules.
-pub trait AtRuleParser {
+pub trait AtRuleParser<'i> {
     /// The intermediate representation of an at-rule prelude.
     type Prelude;
 
     /// The finished representation of an at-rule.
     type AtRule;
 
+    /// The error type that is included in the ParseError value that can be returned.
+    type Error: 'i;
+
     /// Parse the prelude of an at-rule with the given `name`.
     ///
     /// Return the representation of the prelude and the type of at-rule,
     /// or `Err(())` to ignore the entire at-rule as invalid.
     ///
     /// See `AtRuleType`’s documentation for the return value.
     ///
     /// The prelude is the part after the at-keyword
@@ -101,107 +107,111 @@ pub trait AtRuleParser {
     ///
     /// At-rule name matching should be case-insensitive in the ASCII range.
     /// This can be done with `std::ascii::Ascii::eq_ignore_ascii_case`,
     /// or with the `match_ignore_ascii_case!` macro.
     ///
     /// 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(&mut self, name: &str, input: &mut Parser)
-                     -> Result<AtRuleType<Self::Prelude, Self::AtRule>, ()> {
+    fn parse_prelude<'t>(&mut self, name: Cow<'i, str>, input: &mut Parser<'i, 't>)
+                     -> Result<AtRuleType<Self::Prelude, Self::AtRule>, ParseError<'i, Self::Error>> {
         let _ = name;
         let _ = input;
-        Err(())
+        Err(ParseError::Basic(BasicParseError::AtRuleInvalid))
     }
 
     /// Parse the content of a `{ /* ... */ }` block for the body of the at-rule.
     ///
     /// Return the finished representation of the at-rule
     /// as returned by `RuleListParser::next` or `DeclarationListParser::next`,
     /// or `Err(())` to ignore the entire at-rule as invalid.
     ///
     /// This is only called when `parse_prelude` returned `WithBlock` or `OptionalBlock`,
     /// and a block was indeed found following the prelude.
-    fn parse_block(&mut self, prelude: Self::Prelude, input: &mut Parser)
-                   -> Result<Self::AtRule, ()> {
+    fn parse_block<'t>(&mut self, prelude: Self::Prelude, input: &mut Parser<'i, 't>)
+                       -> Result<Self::AtRule, ParseError<'i, Self::Error>> {
         let _ = prelude;
         let _ = input;
-        Err(())
+        Err(ParseError::Basic(BasicParseError::AtRuleInvalid))
     }
 
     /// An `OptionalBlock` prelude was followed by `;`.
     ///
     /// Convert the prelude into the finished representation of the at-rule
     /// as returned by `RuleListParser::next` or `DeclarationListParser::next`.
     fn rule_without_block(&mut self, prelude: Self::Prelude) -> Self::AtRule {
         let _ = prelude;
         panic!("The `AtRuleParser::rule_without_block` method must be overriden \
                 if `AtRuleParser::parse_prelude` ever returns `AtRuleType::OptionalBlock`.")
     }
 }
 
-
 /// 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).
 ///
 /// Default implementations that reject all qualified rules are provided,
 /// so that `impl QualifiedRuleParser<(), ()> for ... {}` can be used
 /// for example for using `RuleListParser` to parse a rule list with only at-rules
 /// (such as inside `@font-feature-values`).
-pub trait QualifiedRuleParser {
+pub trait QualifiedRuleParser<'i> {
     /// The intermediate representation of a qualified rule prelude.
     type Prelude;
 
     /// The finished representation of a qualified rule.
     type QualifiedRule;
 
+    /// The error type that is included in the ParseError value that can be returned.
+    type Error: 'i;
+
     /// Parse the prelude of a qualified rule. For style rules, this is as Selector list.
     ///
     /// Return the representation of the prelude,
     /// or `Err(())` to ignore the entire at-rule as invalid.
     ///
     /// 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(&mut self, input: &mut Parser) -> Result<Self::Prelude, ()> {
+    fn parse_prelude<'t>(&mut self, input: &mut Parser<'i, 't>)
+                         -> Result<Self::Prelude, ParseError<'i, Self::Error>> {
         let _ = input;
-        Err(())
+        Err(ParseError::Basic(BasicParseError::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(&mut self, prelude: Self::Prelude, input: &mut Parser)
-                   -> Result<Self::QualifiedRule, ()> {
+    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(())
+        Err(ParseError::Basic(BasicParseError::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>,
 
     /// The parser given to `DeclarationListParser::new`
     pub parser: P,
 }
 
 
-impl<'i, 't, 'a, I, P> DeclarationListParser<'i, 't, 'a, P>
-where P: DeclarationParser<Declaration = I> + AtRuleParser<AtRule = I> {
+impl<'i: 't, 't: 'a, 'a, I, P, E: 'i> DeclarationListParser<'i, 't, 'a, P>
+where P: DeclarationParser<'i, Declaration = I, Error = E> +
+         AtRuleParser<'i, AtRule = I, Error = E> {
     /// Create a new `DeclarationListParser` for the given `input` and `parser`.
     ///
     /// Note that all CSS declaration lists can on principle contain at-rules.
     /// Even if no such valid at-rule exists (yet),
     /// this affects error handling: at-rules end at `{}` blocks, not just semicolons.
     ///
     /// The given `parser` therefore needs to implement
     /// both `DeclarationParser` and `AtRuleParser` traits.
@@ -216,42 +226,51 @@ where P: DeclarationParser<Declaration =
             input: input,
             parser: parser,
         }
     }
 }
 
 /// `DeclarationListParser` is an iterator that yields `Ok(_)` for a valid declaration or at-rule
 /// or `Err(())` for an invalid one.
-impl<'i, 't, 'a, I, P> Iterator for DeclarationListParser<'i, 't, 'a, P>
-where P: DeclarationParser<Declaration = I> + AtRuleParser<AtRule = I> {
-    type Item = Result<I, Range<SourcePosition>>;
+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>>;
 
-    fn next(&mut self) -> Option<Result<I, Range<SourcePosition>>> {
+    fn next(&mut self) -> Option<Result<I, PreciseParseError<'i, E>>> {
         loop {
             let start_position = self.input.position();
             match self.input.next_including_whitespace_and_comments() {
                 Ok(Token::WhiteSpace(_)) | Ok(Token::Comment(_)) | Ok(Token::Semicolon) => {}
                 Ok(Token::Ident(name)) => {
                     return Some({
                         let parser = &mut self.parser;
-                        self.input.parse_until_after(Delimiter::Semicolon, |input| {
+                        // FIXME: https://github.com/rust-lang/rust/issues/42508
+                        parse_until_after::<'i, 't, _, _, _>(self.input, Delimiter::Semicolon, |input| {
                             try!(input.expect_colon());
-                            parser.parse_value(&*name, input)
+                            parser.parse_value(name, input)
                         })
-                    }.map_err(|()| start_position..self.input.position()))
+                    }.map_err(|e| PreciseParseError {
+                        error: e,
+                        span: start_position..self.input.position()
+                    }))
                 }
                 Ok(Token::AtKeyword(name)) => {
                     return Some(parse_at_rule(start_position, name, self.input, &mut self.parser))
                 }
                 Ok(_) => {
-                    return Some(self.input.parse_until_after(Delimiter::Semicolon, |_| Err(()))
-                                .map_err(|()| start_position..self.input.position()))
+                    return Some(self.input.parse_until_after(Delimiter::Semicolon,
+                                                             |_| Err(ParseError::Basic(BasicParseError::ExpectedToken(Token::Semicolon))))
+                                .map_err(|e| PreciseParseError {
+                                    error: e,
+                                    span: start_position..self.input.position()
+                                }))
                 }
-                Err(()) => return None,
+                Err(_) => return None,
             }
         }
     }
 }
 
 
 /// Provides an iterator for rule list parsing.
 pub struct RuleListParser<'i: 't, 't: 'a, 'a, P> {
@@ -261,18 +280,19 @@ pub struct RuleListParser<'i: 't, 't: 'a
     /// The parser given to `RuleListParser::new`
     pub parser: P,
 
     is_stylesheet: bool,
     any_rule_so_far: bool,
 }
 
 
-impl<'i: 't, 't: 'a, 'a, R, P> RuleListParser<'i, 't, 'a, P>
-where P: QualifiedRuleParser<QualifiedRule = R> + AtRuleParser<AtRule = R> {
+impl<'i: 't, 't: 'a, 'a, R, P, E: 'i> RuleListParser<'i, 't, 'a, P>
+where P: QualifiedRuleParser<'i, QualifiedRule = R, Error = E> +
+         AtRuleParser<'i, AtRule = R, Error = E> {
     /// Create a new `RuleListParser` for the given `input` at the top-level of a stylesheet
     /// and the given `parser`.
     ///
     /// The given `parser` needs to implement both `QualifiedRuleParser` and `AtRuleParser` traits.
     /// However, either of them can be an empty `impl`
     /// since the traits provide default implementations of their methods.
     ///
     /// The return type for finished qualified rules and at-rules also needs to be the same,
@@ -301,139 +321,178 @@ where P: QualifiedRuleParser<QualifiedRu
             any_rule_so_far: false,
         }
     }
 }
 
 
 
 /// `RuleListParser` is an iterator that yields `Ok(_)` for a rule or `Err(())` for an invalid one.
-impl<'i, 't, 'a, R, P> Iterator for RuleListParser<'i, 't, 'a, P>
-where P: QualifiedRuleParser<QualifiedRule = R> + AtRuleParser<AtRule = R> {
-    type Item = Result<R, Range<SourcePosition>>;
+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>>;
 
-    fn next(&mut self) -> Option<Result<R, Range<SourcePosition>>> {
+    fn next(&mut self) -> Option<Result<R, PreciseParseError<'i, E>>> {
         loop {
             let start_position = self.input.position();
             match self.input.next_including_whitespace_and_comments() {
                 Ok(Token::WhiteSpace(_)) | Ok(Token::Comment(_)) => {}
                 Ok(Token::CDO) | Ok(Token::CDC) if self.is_stylesheet => {}
                 Ok(Token::AtKeyword(name)) => {
                     let first_stylesheet_rule = self.is_stylesheet && !self.any_rule_so_far;
                     self.any_rule_so_far = true;
                     if first_stylesheet_rule && name.eq_ignore_ascii_case("charset") {
                         let delimiters = Delimiter::Semicolon | Delimiter::CurlyBracketBlock;
-                        let _ = self.input.parse_until_after(delimiters, |_input| Ok(()));
+                        let _: Result<(), ParseError<()>> = self.input.parse_until_after(delimiters, |_| Ok(()));
                     } else {
                         return Some(parse_at_rule(start_position, name, self.input, &mut self.parser))
                     }
                 }
                 Ok(_) => {
                     self.any_rule_so_far = true;
                     self.input.reset(start_position);
                     return Some(parse_qualified_rule(self.input, &mut self.parser)
-                                .map_err(|()| start_position..self.input.position()))
+                                .map_err(|e| PreciseParseError {
+                                    error: e,
+                                    span: start_position..self.input.position()
+                                }))
                 }
-                Err(()) => return None,
+                Err(_) => return None,
             }
         }
     }
 }
 
 
 /// Parse a single declaration, such as an `( /* ... */ )` parenthesis in an `@supports` prelude.
-pub fn parse_one_declaration<P>(input: &mut Parser, parser: &mut P)
-                                -> Result<<P as DeclarationParser>::Declaration,
-                                          Range<SourcePosition>>
-                                where P: DeclarationParser {
+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>>
+                                           where P: DeclarationParser<'i, Error = E> {
     let start_position = input.position();
     input.parse_entirely(|input| {
         let name = try!(input.expect_ident());
         try!(input.expect_colon());
-        parser.parse_value(&*name, input)
-    }).map_err(|()| start_position..input.position())
+        parser.parse_value(name, input)
+    }).map_err(|e| PreciseParseError {
+        error: e,
+        span: start_position..input.position()
+    })
 }
 
 
 /// Parse a single rule, such as for CSSOM’s `CSSStyleSheet.insertRule`.
-pub fn parse_one_rule<R, P>(input: &mut Parser, parser: &mut P) -> Result<R, ()>
-where P: QualifiedRuleParser<QualifiedRule = R> + AtRuleParser<AtRule = R> {
+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> {
     input.parse_entirely(|input| {
         loop {
             let start_position = input.position();
             match try!(input.next_including_whitespace_and_comments()) {
                 Token::WhiteSpace(_) | Token::Comment(_) => {}
                 Token::AtKeyword(name) => {
-                    return parse_at_rule(start_position, name, input, parser).map_err(|_| ())
+                    return parse_at_rule(start_position, name, input, parser).map_err(|e| e.error)
                 }
                 _ => {
                     input.reset(start_position);
-                    return parse_qualified_rule(input, parser).map_err(|_| ())
+                    return parse_qualified_rule(input, parser)
                 }
             }
         }
     })
 }
 
+pub struct PreciseParseError<'i, E: 'i> {
+    pub error: ParseError<'i, E>,
+    pub span: Range<SourcePosition>,
+}
 
-fn parse_at_rule<P>(start_position: SourcePosition, name: Cow<str>,
-                    input: &mut Parser, parser: &mut P)
-                    -> Result<<P as AtRuleParser>::AtRule, Range<SourcePosition>>
-                    where P: AtRuleParser {
+fn parse_at_rule<'i: 't, 't, P, E>(start_position: SourcePosition, name: Cow<'i, str>,
+                                   input: &mut Parser<'i, 't>, parser: &mut P)
+                                   -> Result<<P as AtRuleParser<'i>>::AtRule, PreciseParseError<'i, E>>
+                                   where P: AtRuleParser<'i, Error = E> {
     let delimiters = Delimiter::Semicolon | Delimiter::CurlyBracketBlock;
-    let result = input.parse_until_before(delimiters, |input| {
-        parser.parse_prelude(&*name, input)
+    // 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(rule)) => {
             match input.next() {
-                Ok(Token::Semicolon) | Err(()) => Ok(rule),
-                Ok(Token::CurlyBracketBlock) => Err(start_position..input.position()),
+                Ok(Token::Semicolon) | Err(_) => Ok(rule),
+                Ok(Token::CurlyBracketBlock) => Err(PreciseParseError {
+                    error: ParseError::Basic(BasicParseError::UnexpectedToken(Token::CurlyBracketBlock)),
+                    span: start_position..input.position(),
+                }),
                 Ok(_) => unreachable!()
             }
         }
         Ok(AtRuleType::WithBlock(prelude)) => {
             match input.next() {
                 Ok(Token::CurlyBracketBlock) => {
-                    input.parse_nested_block(move |input| parser.parse_block(prelude, input))
-                    .map_err(|()| start_position..input.position())
+                    // 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,
+                            span: start_position..input.position(),
+                        })
                 }
-                Ok(Token::Semicolon) | Err(()) => Err(start_position..input.position()),
+                Ok(Token::Semicolon) => Err(PreciseParseError {
+                    error: ParseError::Basic(BasicParseError::UnexpectedToken(Token::Semicolon)),
+                    span: start_position..input.position()
+                }),
+                Err(e) => Err(PreciseParseError {
+                    error: ParseError::Basic(e),
+                    span: start_position..input.position(),
+                }),
                 Ok(_) => unreachable!()
             }
         }
         Ok(AtRuleType::OptionalBlock(prelude)) => {
             match input.next() {
-                Ok(Token::Semicolon) | Err(()) => Ok(parser.rule_without_block(prelude)),
+                Ok(Token::Semicolon) | Err(_) => Ok(parser.rule_without_block(prelude)),
                 Ok(Token::CurlyBracketBlock) => {
-                    input.parse_nested_block(move |input| parser.parse_block(prelude, input))
-                    .map_err(|()| start_position..input.position())
+                    // 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,
+                            span: start_position..input.position(),
+                        })
                 }
                 _ => unreachable!()
             }
         }
-        Err(()) => {
+        Err(_) => {
             let end_position = input.position();
-            match input.next() {
-                Ok(Token::CurlyBracketBlock) | Ok(Token::Semicolon) | Err(()) => {}
+            let error = match input.next() {
+                Ok(Token::CurlyBracketBlock) => BasicParseError::UnexpectedToken(Token::CurlyBracketBlock),
+                Ok(Token::Semicolon) => BasicParseError::UnexpectedToken(Token::Semicolon),
+                Err(e) => e,
                 _ => unreachable!()
-            }
-            Err(start_position..end_position)
+            };
+            Err(PreciseParseError {
+                error: ParseError::Basic(error),
+                span: start_position..end_position,
+            })
         }
     }
 }
 
 
-fn parse_qualified_rule<P>(input: &mut Parser, parser: &mut P)
-                           -> Result<<P as QualifiedRuleParser>::QualifiedRule, ()>
-                           where P: QualifiedRuleParser {
-    let prelude = input.parse_until_before(Delimiter::CurlyBracketBlock, |input| {
+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> {
+    // FIXME: https://github.com/rust-lang/rust/issues/42508
+    let prelude = parse_until_before::<'i, 't, _, _, _>(input, Delimiter::CurlyBracketBlock, |input| {
         parser.parse_prelude(input)
     });
     match try!(input.next()) {
         Token::CurlyBracketBlock => {
             // Do this here so that we consume the `{` even if the prelude is `Err`.
             let prelude = try!(prelude);
-            input.parse_nested_block(move |input| parser.parse_block(prelude, input))
+            // FIXME: https://github.com/rust-lang/rust/issues/42508
+            parse_nested_block::<'i, 't, _, _, _>(input, move |input| parser.parse_block(prelude, input))
         }
         _ => unreachable!()
     }
 }
--- a/third_party/rust/cssparser/src/tests.rs
+++ b/third_party/rust/cssparser/src/tests.rs
@@ -7,19 +7,19 @@ extern crate test;
 
 use encoding_rs;
 use std::borrow::Cow::{self, Borrowed};
 use rustc_serialize::json::{self, Json, ToJson};
 
 #[cfg(feature = "bench")]
 use self::test::Bencher;
 
-use super::{Parser, Delimiter, Token, NumericValue, PercentageValue, SourceLocation,
-            DeclarationListParser, DeclarationParser, RuleListParser,
-            AtRuleType, AtRuleParser, QualifiedRuleParser,
+use super::{Parser, Delimiter, Token, NumericValue, PercentageValue, SourceLocation, ParseError,
+            DeclarationListParser, DeclarationParser, RuleListParser, BasicParseError,
+            AtRuleType, AtRuleParser, QualifiedRuleParser, ParserInput,
             parse_one_declaration, parse_one_rule, parse_important,
             stylesheet_encoding, EncodingSupport,
             TokenSerializationType,
             Color, RGBA, parse_nth, UnicodeRange, ToCss};
 
 macro_rules! JArray {
     ($($e: expr,)*) => { JArray![ $( $e ),* ] };
     ($($e: expr),*) => { Json::Array(vec!( $( $e.to_json() ),* )) }
@@ -57,25 +57,25 @@ fn normalize(json: &mut Json) {
             if *s == "extra-input" || *s == "empty" {
                 *s = "invalid".to_string()
             }
         }
         _ => {}
     }
 }
 
-fn assert_json_eq(results: json::Json, mut expected: json::Json, message: String) {
+fn assert_json_eq(results: json::Json, mut expected: json::Json, message: &str) {
     normalize(&mut expected);
     if !almost_equals(&results, &expected) {
         println!("{}", ::difference::Changeset::new(
             &results.pretty().to_string(),
             &expected.pretty().to_string(),
             "\n",
         ));
-        panic!(message)
+        panic!("{}", message)
     }
 }
 
 fn run_raw_json_tests<F: Fn(Json, Json) -> ()>(json_data: &str, run: F) {
     let items = match Json::from_str(json_data) {
         Ok(Json::Array(items)) => items,
         _ => panic!("Invalid JSON")
     };
@@ -92,18 +92,19 @@ fn run_raw_json_tests<F: Fn(Json, Json) 
     }
 }
 
 
 fn run_json_tests<F: Fn(&mut Parser) -> Json>(json_data: &str, parse: F) {
     run_raw_json_tests(json_data, |input, expected| {
         match input {
             Json::String(input) => {
-                let result = parse(&mut Parser::new(&input));
-                assert_json_eq(result, expected, input);
+                let mut parse_input = ParserInput::new(&input);
+                let result = parse(&mut Parser::new(&mut parse_input));
+                assert_json_eq(result, expected, &input);
             },
             _ => panic!("Unexpected JSON")
         }
     });
 }
 
 
 #[test]
@@ -112,19 +113,20 @@ fn component_value_list() {
         Json::Array(component_values_to_json(input))
     });
 }
 
 
 #[test]
 fn one_component_value() {
     run_json_tests(include_str!("css-parsing-tests/one_component_value.json"), |input| {
-        input.parse_entirely(|input| {
+        let result: Result<Json, ParseError<()>> = input.parse_entirely(|input| {
             Ok(one_component_value_to_json(try!(input.next()), input))
-        }).unwrap_or(JArray!["error", "invalid"])
+        });
+        result.unwrap_or(JArray!["error", "invalid"])
     });
 }
 
 
 #[test]
 fn declaration_list() {
     run_json_tests(include_str!("css-parsing-tests/declaration_list.json"), |input| {
         Json::Array(DeclarationListParser::new(input, JsonParser).map(|result| {
@@ -208,57 +210,71 @@ fn stylesheet_from_bytes() {
                 .map(|s| s.as_bytes());
             let environment_encoding = get_string(&map, "environment_encoding")
                 .map(|s| s.as_bytes())
                 .and_then(EncodingRs::from_label);
 
             let encoding = stylesheet_encoding::<EncodingRs>(
                 &css, protocol_encoding_label, environment_encoding);
             let (css_unicode, used_encoding, _) = encoding.decode(&css);
-            let input = &mut Parser::new(&css_unicode);
+            let mut input = ParserInput::new(&css_unicode);
+            let input = &mut Parser::new(&mut input);
             let rules = RuleListParser::new_for_stylesheet(input, JsonParser)
                         .map(|result| result.unwrap_or(JArray!["error", "invalid"]))
                         .collect::<Vec<_>>();
             JArray![rules, used_encoding.name().to_lowercase()]
         };
-        assert_json_eq(result, expected, Json::Object(map).to_string());
+        assert_json_eq(result, expected, &Json::Object(map).to_string());
     });
 
     fn get_string<'a>(map: &'a json::Object, key: &str) -> Option<&'a str> {
         match map.get(key) {
             Some(&Json::String(ref s)) => Some(s),
             Some(&Json::Null) => None,
             None => None,
             _ => panic!("Unexpected JSON"),
         }
     }
 }
 
 
 #[test]
 fn expect_no_error_token() {
-    assert!(Parser::new("foo 4px ( / { !bar }").expect_no_error_token().is_ok());
-    assert!(Parser::new(")").expect_no_error_token().is_err());
-    assert!(Parser::new("}").expect_no_error_token().is_err());
-    assert!(Parser::new("(a){]").expect_no_error_token().is_err());
-    assert!(Parser::new("'\n'").expect_no_error_token().is_err());
-    assert!(Parser::new("url('\n'").expect_no_error_token().is_err());
-    assert!(Parser::new("url(a b)").expect_no_error_token().is_err());
-    assert!(Parser::new("url(\u{7F})").expect_no_error_token().is_err());
+    let mut input = ParserInput::new("foo 4px ( / { !bar }");
+    assert!(Parser::new(&mut input).expect_no_error_token().is_ok());
+    let mut input = ParserInput::new(")");
+    assert!(Parser::new(&mut input).expect_no_error_token().is_err());
+    let mut input = ParserInput::new("}");
+    assert!(Parser::new(&mut input).expect_no_error_token().is_err());
+    let mut input = ParserInput::new("(a){]");
+    assert!(Parser::new(&mut input).expect_no_error_token().is_err());
+    let mut input = ParserInput::new("'\n'");
+    assert!(Parser::new(&mut input).expect_no_error_token().is_err());
+    let mut input = ParserInput::new("url('\n'");
+    assert!(Parser::new(&mut input).expect_no_error_token().is_err());
+    let mut input = ParserInput::new("url(a b)");
+    assert!(Parser::new(&mut input).expect_no_error_token().is_err());
+    let mut input = ParserInput::new("url(\u{7F}))");
+    assert!(Parser::new(&mut input).expect_no_error_token().is_err());
 }
 
 
 /// https://github.com/servo/rust-cssparser/issues/71
 #[test]
 fn outer_block_end_consumed() {
-    let mut input = Parser::new("(calc(true))");
+    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| input.expect_function_matching("calc")).is_ok());
+    assert!(input.parse_nested_block(|input| {
+        let result: Result<_, ParseError<()>> = input.expect_function_matching("calc")
+            .map_err(|e| ParseError::Basic(e));
+        result
+    }).is_ok());
     println!("{:?}", input.position());
-    assert_eq!(input.next(), Err(()));
+    assert!(input.next().is_err());
 }
 
 #[test]
 fn unquoted_url_escaping() {
     let token = Token::UnquotedUrl("\
         \x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\
         \x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f \
         !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]\
@@ -267,40 +283,51 @@ fn unquoted_url_escaping() {
     let serialized = token.to_css_string();
     assert_eq!(serialized, "\
         url(\
             \\1 \\2 \\3 \\4 \\5 \\6 \\7 \\8 \\9 \\A \\B \\C \\D \\E \\F \\10 \
             \\11 \\12 \\13 \\14 \\15 \\16 \\17 \\18 \\19 \\1A \\1B \\1C \\1D \\1E \\1F \\20 \
             !\\\"#$%&\\'\\(\\)*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]\
             ^_`abcdefghijklmnopqrstuvwxyz{|}~\\7F é\
         )\
-    ");
-    assert_eq!(Parser::new(&serialized).next(), Ok(token))
+        ");
+    let mut input = ParserInput::new(&serialized);
+    assert_eq!(Parser::new(&mut input).next(), Ok(token))
 }
 
 #[test]
 fn test_expect_url() {
-    fn parse(s: &str) -> Result<Cow<str>, ()> {
+    fn parse<'a>(s: &mut ParserInput<'a>) -> Result<Cow<'a, str>, BasicParseError<'a>> {
         Parser::new(s).expect_url()
     }
-    assert_eq!(parse("url()").unwrap(), "");
-    assert_eq!(parse("url( ").unwrap(), "");
-    assert_eq!(parse("url( abc").unwrap(), "abc");
-    assert_eq!(parse("url( abc \t)").unwrap(), "abc");
-    assert_eq!(parse("url( 'abc' \t)").unwrap(), "abc");
-    assert_eq!(parse("url(abc more stuff)"), Err(()));
+    let mut input = ParserInput::new("url()");
+    assert_eq!(parse(&mut input).unwrap(), "");
+    let mut input = ParserInput::new("url( ");
+    assert_eq!(parse(&mut input).unwrap(), "");
+    let mut input = ParserInput::new("url( abc");
+    assert_eq!(parse(&mut input).unwrap(), "abc");
+    let mut input = ParserInput::new("url( abc \t)");
+    assert_eq!(parse(&mut input).unwrap(), "abc");
+    let mut input = ParserInput::new("url( 'abc' \t)");
+    assert_eq!(parse(&mut input).unwrap(), "abc");
+    let mut input = ParserInput::new("url(abc more stuff)");
+    assert!(parse(&mut input).is_err());
     // The grammar at https://drafts.csswg.org/css-values/#urls plans for `<url-modifier>*`
     // at the position of "more stuff", but no such modifier is defined yet.
-    assert_eq!(parse("url('abc' more stuff)"), Err(()));
+    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| {
-        to_json(input.parse_entirely(Color::parse))
+        let result: Result<_, ParseError<()>> = input.parse_entirely(|i| {
+            Color::parse(i).map_err(|e| ParseError::Basic(e))
+        });
+        to_json(result.map_err(|_| ()))
     });
 }
 
 
 #[test]
 fn color3() {
     run_color_tests(include_str!("css-parsing-tests/color3.json"), |c| c.ok().to_json())
 }
@@ -317,32 +344,36 @@ fn color3_hsl() {
 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(parse_nth).ok().to_json()
+        input.parse_entirely(|i| {
+            let result: Result<_, ParseError<()>> = parse_nth(i).map_err(|e| ParseError::Basic(e));
+            result
+        }).ok().to_json()
     });
 }
 
 #[test]
 fn unicode_range() {
     run_json_tests(include_str!("css-parsing-tests/urange.json"), |input| {
-        input.parse_comma_separated(|input| {
+        let result: Result<_, ParseError<()>> = input.parse_comma_separated(|input| {
             let result = UnicodeRange::parse(input).ok().map(|r| (r.start, r.end));
             if input.is_exhausted() {
                 Ok(result)
             } else {
                 while let Ok(_) = input.next() {}
                 Ok(None)
             }
-        }).unwrap().to_json()
+        });
+        result.unwrap().to_json()
     });
 }
 
 
 #[test]
 fn serializer_not_preserving_comments() {
     serializer(false)
 }
@@ -371,27 +402,29 @@ fn serializer(preserve_comments: bool) {
                 token.to_css(string).unwrap();
                 let closing_token = match token {
                     Token::Function(_) | Token::ParenthesisBlock => Some(Token::CloseParenthesis),
                     Token::SquareBracketBlock => Some(Token::CloseSquareBracket),
                     Token::CurlyBracketBlock => Some(Token::CloseCurlyBracket),
                     _ => None
                 };
                 if let Some(closing_token) = closing_token {
-                    input.parse_nested_block(|input| {
+                    let result: Result<_, ParseError<()>> = input.parse_nested_block(|input| {
                         write_to(previous_token, input, string, preserve_comments);
                         Ok(())
-                    }).unwrap();
+                    });
+                    result.unwrap();
                     closing_token.to_css(string).unwrap();
                 }
             }
         }
         let mut serialized = String::new();
         write_to(TokenSerializationType::nothing(), input, &mut serialized, preserve_comments);
-        let parser = &mut Parser::new(&serialized);
+        let mut input = ParserInput::new(&serialized);
+        let parser = &mut Parser::new(&mut input);
         Json::Array(component_values_to_json(parser))
     });
 }
 
 #[test]
 fn serialize_current_color() {
     let c = Color::CurrentColor;
     assert!(c.to_css_string() == "currentColor");
@@ -412,17 +445,18 @@ fn serialize_rgba() {
 #[test]
 fn serialize_rgba_two_digit_float_if_roundtrips() {
     let c = Color::RGBA(RGBA::from_floats(0., 0., 0., 0.5));
     assert_eq!(c.to_css_string(), "rgba(0, 0, 0, 0.5)");
 }
 
 #[test]
 fn line_numbers() {
-    let mut input = Parser::new("foo bar\nbaz\r\n\n\"a\\\r\nb\"");
+    let mut input = ParserInput::new("foo bar\nbaz\r\n\n\"a\\\r\nb\"");
+    let mut input = Parser::new(&mut input);
     assert_eq!(input.current_source_location(), SourceLocation { line: 1, column: 1 });
     assert_eq!(input.next_including_whitespace(), Ok(Token::Ident(Borrowed("foo"))));
     assert_eq!(input.current_source_location(), SourceLocation { line: 1, column: 4 });
     assert_eq!(input.next_including_whitespace(), Ok(Token::WhiteSpace(" ")));
     assert_eq!(input.current_source_location(), SourceLocation { line: 1, column: 5 });
     assert_eq!(input.next_including_whitespace(), Ok(Token::Ident(Borrowed("bar"))));
     assert_eq!(input.current_source_location(), SourceLocation { line: 1, column: 8 });
     assert_eq!(input.next_including_whitespace(), Ok(Token::WhiteSpace("\n")));
@@ -433,17 +467,17 @@ fn line_numbers() {
 
     assert_eq!(input.next_including_whitespace(), Ok(Token::WhiteSpace("\r\n\n")));
     assert_eq!(input.current_source_location(), SourceLocation { line: 4, column: 1 });
 
     assert_eq!(input.source_location(position), SourceLocation { line: 2, column: 4 });
 
     assert_eq!(input.next_including_whitespace(), Ok(Token::QuotedString(Borrowed("ab"))));
     assert_eq!(input.current_source_location(), SourceLocation { line: 5, column: 3 });
-    assert_eq!(input.next_including_whitespace(), Err(()));
+    assert!(input.next_including_whitespace().is_err());
 }
 
 #[test]
 fn overflow() {
     use std::iter::repeat;
     use std::f32;
 
     let css = r"
@@ -465,17 +499,18 @@ fn overflow() {
          3.40282347e+38
          3.402824e+38
 
          -3.30282347e+38
          -3.40282347e+38
          -3.402824e+38
 
     ".replace("{309 zeros}", &repeat('0').take(309).collect::<String>());
-    let mut input = Parser::new(&css);
+    let mut input = ParserInput::new(&css);
+    let mut input = Parser::new(&mut input);
 
     assert_eq!(input.expect_integer(), Ok(2147483646));
     assert_eq!(input.expect_integer(), Ok(2147483647));
     assert_eq!(input.expect_integer(), Ok(2147483647)); // Clamp on overflow
     assert_eq!(input.expect_integer(), Ok(2147483647));
     assert_eq!(input.expect_integer(), Ok(2147483647));
     assert_eq!(input.expect_integer(), Ok(2147483647));
 
@@ -494,21 +529,25 @@ fn overflow() {
     assert_eq!(input.expect_number(), Ok(-3.30282347e+38));
     assert_eq!(input.expect_number(), Ok(f32::MIN));
     assert_eq!(input.expect_number(), Ok(f32::NEG_INFINITY));
     assert!(f32::MIN != f32::NEG_INFINITY);
 }
 
 #[test]
 fn line_delimited() {
-    let mut input = Parser::new(" { foo ; bar } baz;,");
+    let mut input = ParserInput::new(" { foo ; bar } baz;,");
+    let mut input = Parser::new(&mut input);
     assert_eq!(input.next(), Ok(Token::CurlyBracketBlock));
-    assert_eq!(input.parse_until_after(Delimiter::Semicolon, |_| Ok(42)), Err(()));
+    assert!({
+        let result: Result<_, ParseError<()>> = input.parse_until_after(Delimiter::Semicolon, |_| Ok(42));
+        result
+    }.is_err());
     assert_eq!(input.next(), Ok(Token::Comma));
-    assert_eq!(input.next(), Err(()));
+    assert!(input.next().is_err());
 }
 
 #[test]
 fn identifier_serialization() {
     // Null bytes
     assert_eq!(Token::Ident("\0".into()).to_css_string(), "\u{FFFD}");
     assert_eq!(Token::Ident("a\0".into()).to_css_string(), "a\u{FFFD}");
     assert_eq!(Token::Ident("\0b".into()).to_css_string(), "\u{FFFD}b");
@@ -591,17 +630,18 @@ impl ToJson for Color {
 
 #[cfg(feature = "bench")]
 const BACKGROUND_IMAGE: &'static str = include_str!("big-data-url.css");
 
 #[cfg(feature = "bench")]
 #[bench]
 fn unquoted_url(b: &mut Bencher) {
     b.iter(|| {
-        let mut input = Parser::new(BACKGROUND_IMAGE);
+        let mut input = ParserInput::new(BACKGROUND_IMAGE);
+        let mut input = Parser::new(&mut input);
         input.look_for_var_functions();
 
         let result = input.try(|input| input.expect_url());
 
         assert!(result.is_ok());
 
         input.seen_var_functions();
         (result.is_ok(), input.seen_var_functions())
@@ -609,39 +649,43 @@ fn unquoted_url(b: &mut Bencher) {
 }
 
 
 #[cfg(feature = "bench")]
 #[bench]
 fn numeric(b: &mut Bencher) {
     b.iter(|| {
         for _ in 0..1000000 {
-            let mut input = Parser::new("10px");
+            let mut input = ParserInput::new("10px");
+            let mut input = Parser::new(&mut input);
             let _ = test::black_box(input.next());
         }
     })
 }
 
 struct JsonParser;
 
 #[test]
 fn no_stack_overflow_multiple_nested_blocks() {
     let mut input: String = "{{".into();
     for _ in 0..20 {
         let dup = input.clone();
         input.push_str(&dup);
     }
-    let mut input = Parser::new(&input);
+    let mut input = ParserInput::new(&input);
+    let mut input = Parser::new(&mut input);
     while let Ok(..) = input.next() { }
 }
 
-impl DeclarationParser for JsonParser {
+impl<'i> DeclarationParser<'i> for JsonParser {
     type Declaration = Json;
+    type Error = ();
 
-    fn parse_value(&mut self, name: &str, input: &mut Parser) -> Result<Json, ()> {
+    fn parse_value<'t>(&mut self, name: Cow<'i, str>, input: &mut Parser<'i, 't>)
+                       -> Result<Json, ParseError<'i, ()>> {
         let mut value = vec![];
         let mut important = false;
         loop {
             let start_position = input.position();
             if let Ok(mut token) = input.next_including_whitespace() {
                 // Hack to deal with css-parsing-tests assuming that
                 // `!important` in the middle of a declaration value is OK.
                 // This can never happen per spec
@@ -666,49 +710,53 @@ impl DeclarationParser for JsonParser {
             "declaration",
             name,
             value,
             important,
         ])
     }
 }
 
-impl AtRuleParser for JsonParser {
+impl<'i> AtRuleParser<'i> for JsonParser {
     type Prelude = Vec<Json>;
     type AtRule = Json;
+    type Error = ();
 
-    fn parse_prelude(&mut self, name: &str, input: &mut Parser)
-                     -> Result<AtRuleType<Vec<Json>, Json>, ()> {
+    fn parse_prelude<'t>(&mut self, name: Cow<'i, str>, input: &mut Parser<'i, 't>)
+                         -> Result<AtRuleType<Vec<Json>, Json>, ParseError<'i, ()>> {
         Ok(AtRuleType::OptionalBlock(vec![
             "at-rule".to_json(),
             name.to_json(),
             Json::Array(component_values_to_json(input)),
         ]))
     }
 
-    fn parse_block(&mut self, mut prelude: Vec<Json>, input: &mut Parser) -> Result<Json, ()> {
+    fn parse_block<'t>(&mut self, mut prelude: Vec<Json>, input: &mut Parser<'i, 't>)
+                       -> Result<Json, ParseError<'i, ()>> {
         prelude.push(Json::Array(component_values_to_json(input)));
         Ok(Json::Array(prelude))
     }
 
     fn rule_without_block(&mut self, mut prelude: Vec<Json>) -> Json {
         prelude.push(Json::Null);
         Json::Array(prelude)
     }
 }
 
-impl QualifiedRuleParser for JsonParser {
+impl<'i> QualifiedRuleParser<'i> for JsonParser {
     type Prelude = Vec<Json>;
     type QualifiedRule = Json;
+    type Error = ();
 
-    fn parse_prelude(&mut self, input: &mut Parser) -> Result<Vec<Json>, ()> {
+    fn parse_prelude<'t>(&mut self, input: &mut Parser<'i, 't>) -> Result<Vec<Json>, ParseError<'i, ()>> {
         Ok(component_values_to_json(input))
     }
 
-    fn parse_block(&mut self, prelude: Vec<Json>, input: &mut Parser) -> Result<Json, ()> {
+    fn parse_block<'t>(&mut self, prelude: Vec<Json>, input: &mut Parser<'i, 't>)
+                       -> Result<Json, ParseError<'i, ()>> {
         Ok(JArray![
             "qualified rule",
             prelude,
             component_values_to_json(input),
         ])
     }
 }
 
@@ -725,17 +773,20 @@ fn one_component_value_to_json(token: To
         vec![
             Token::Number(value).to_css_string().to_json(),
             match value.int_value { Some(i) => i.to_json(), None => value.value.to_json() },
             match value.int_value { Some(_) => "integer", None => "number" }.to_json()
         ]
     }
 
     fn nested(input: &mut Parser) -> Vec<Json> {
-        input.parse_nested_block(|input| Ok(component_values_to_json(input))).unwrap()
+        let result: Result<_, ParseError<()>> = input.parse_nested_block(|input| {
+            Ok(component_values_to_json(input))
+        });
+        result.unwrap()
     }
 
     match token {
         Token::Ident(value) => JArray!["ident", value],
         Token::AtKeyword(value) => JArray!["at-keyword", value],
         Token::Hash(value) => JArray!["hash", value, "unrestricted"],
         Token::IDHash(value) => JArray!["hash", value, "id"],
         Token::QuotedString(value) => JArray!["string", value],
--- a/third_party/rust/cssparser/src/unicode_range.rs
+++ b/third_party/rust/cssparser/src/unicode_range.rs
@@ -1,15 +1,15 @@
 /* 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/. */
 
 //! https://drafts.csswg.org/css-syntax/#urange
 
-use {Parser, ToCss};
+use {Parser, ToCss, BasicParseError};
 use std::char;
 use std::cmp;
 use std::fmt;
 use tokenizer::Token;
 
 /// One contiguous range of code points.
 ///
 /// Can not be empty. Can represent a single code point when start == end.
@@ -19,17 +19,17 @@ pub struct UnicodeRange {
     pub start: u32,
 
     /// Inclusive end of the range. In [0, 0x10FFFF].
     pub end: u32,
 }
 
 impl UnicodeRange {
     /// https://drafts.csswg.org/css-syntax/#urange-syntax
-    pub fn parse(input: &mut Parser) -> Result<Self, ()> {
+    pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, BasicParseError<'i>> {
         // <urange> =
         //   u '+' <ident-token> '?'* |
         //   u <dimension-token> '?'* |
         //   u <number-token> '?'* |
         //   u <number-token> <dimension-token> |
         //   u <number-token> <number-token> |
         //   u '+' '?'+
 
@@ -37,48 +37,51 @@ impl UnicodeRange {
         let after_u = input.position();
         parse_tokens(input)?;
 
         // 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 = parse_concatenated(concatenated_tokens.as_bytes())?;
+        let range = match parse_concatenated(concatenated_tokens.as_bytes()) {
+            Ok(range) => range,
+            Err(()) => return Err(BasicParseError::UnexpectedToken(Token::Ident(concatenated_tokens.into()))),
+        };
         if range.end > char::MAX as u32 || range.start > range.end {
-            Err(())
+            Err(BasicParseError::UnexpectedToken(Token::Ident(concatenated_tokens.into())))
         } else {
             Ok(range)
         }
     }
 }
 
-fn parse_tokens(input: &mut Parser) -> Result<(), ()> {
+fn parse_tokens<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(), BasicParseError<'i>> {
     match input.next_including_whitespace()? {
         Token::Delim('+') => {
             match input.next_including_whitespace()? {
                 Token::Ident(_) => {}
                 Token::Delim('?') => {}
-                _ => return Err(())
+                t => return Err(BasicParseError::UnexpectedToken(t))
             }
             parse_question_marks(input)
         }
         Token::Dimension(..) => {
             parse_question_marks(input)
         }
         Token::Number(_) => {
             let after_number = input.position();
             match input.next_including_whitespace() {
                 Ok(Token::Delim('?')) => parse_question_marks(input),
                 Ok(Token::Dimension(..)) => {}
                 Ok(Token::Number(_)) => {}
                 _ => input.reset(after_number)
             }
         }
-        _ => return Err(())
+        t => return Err(BasicParseError::UnexpectedToken(t))
     }
     Ok(())
 }
 
 /// Consume as many '?' as possible
 fn parse_question_marks(input: &mut Parser) {
     loop {
         let position = input.position();
--- a/toolkit/library/gtest/rust/Cargo.lock
+++ b/toolkit/library/gtest/rust/Cargo.lock
@@ -213,17 +213,17 @@ source = "registry+https://github.com/ru
 dependencies = [
  "core-foundation 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "cssparser"
-version = "0.13.7"
+version = "0.14.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)",
  "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)",
  "quote 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
  "syn 0.11.8 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -334,17 +334,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.13.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "env_logger 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "nsstring_vendor 0.1.0",
  "parking_lot 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "selectors 0.19.0",
  "style 0.0.1",
  "style_traits 0.0.1",
@@ -766,17 +766,17 @@ dependencies = [
  "winapi 0.2.8 (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.13.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.14.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.6 (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)",
  "phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
  "precomputed-hash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "servo_arc 0.0.1",
  "smallvec 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -853,17 +853,17 @@ dependencies = [
  "arraydeque 0.2.3 (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.25.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "bit-vec 0.4.3 (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.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "cssparser 0.13.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "fnv 1.0.5 (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.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "nsstring_vendor 0.1.0",
@@ -897,18 +897,19 @@ dependencies = [
  "synstructure 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "style_traits"
 version = "0.0.1"
 dependencies = [
  "app_units 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "cssparser 0.13.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "selectors 0.19.0",
 ]
 
 [[package]]
 name = "syn"
 version = "0.11.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "quote 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1198,17 +1199,17 @@ dependencies = [
 "checksum cexpr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "393a5f0088efbe41f9d1fcd062f24e83c278608420e62109feb2c8abee07de7d"
 "checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c"
 "checksum clang-sys 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff7c2d1502c65748c7221f43ce670b3ba5c697acebfeb85a580827daca6975fc"
 "checksum clap 2.24.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6b8f69e518f967224e628896b54e41ff6acfb4dcfefc5076325c36525dac900f"
 "checksum core-foundation 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f51ce3b8ebe311c56de14231eb57572c15abebd2d32b3bcb99bcdb9c101f5ac3"
 "checksum core-foundation-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "41115a6aa5d3e1e5ef98148373f25971d1fad53818553f216495f9e67e90a624"
 "checksum core-graphics 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ead017dcf77f503dc991f6b52de6084eeea60a94b0a652baa9bf88654a28e83f"
 "checksum core-text 4.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0e9719616a10f717628e074744f8c55df7b450f7a34d29c196d14f4498aad05d"
-"checksum cssparser 0.13.7 (registry+https://github.com/rust-lang/crates.io-index)" = "ef6124306e5ebc5ab11891d063aeafdd0cdc308079b708c8b566125f3680292b"
+"checksum cssparser 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4a5ca71edbab09f8dc1e3d1c132717562c3b01c8598ab669183c5195bb1761"
 "checksum cssparser-macros 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "079adec4af52bb5275eadd004292028c79eb3c5f5b4ee8086a36d4197032f6df"
 "checksum deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1614659040e711785ed8ea24219140654da1729f3ec8a47a9719d041112fe7bf"
 "checksum dwrote 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "74114b6b49d6731835da7a28a3642651451e315f7f9b9d04e907e65a45681796"
 "checksum env_logger 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ed39959122ea027670b704fb70539f4286ddf4a49eefede23bf0b4b2a069ec03"
 "checksum euclid 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6083f113c422ff9cd855a1cf6cc8ec0903606c0eb43a0c6a0ced3bdc9731e4c1"
 "checksum fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc484842f1e2884faf56f529f960cc12ad8c71ce96cc7abba0a067c98fee344"
 "checksum freetype 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fde23272c687e4570aefec06cb71174ec0f5284b725deac4e77ba2665d635faf"
 "checksum futures 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "55f0008e13fc853f79ea8fc86e931486860d4c4c156cdffb59fa5f7fa833660a"
--- a/toolkit/library/rust/Cargo.lock
+++ b/toolkit/library/rust/Cargo.lock
@@ -211,17 +211,17 @@ source = "registry+https://github.com/ru
 dependencies = [
  "core-foundation 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "cssparser"
-version = "0.13.7"
+version = "0.14.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)",
  "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)",
  "quote 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
  "syn 0.11.8 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -332,17 +332,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.13.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "env_logger 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "nsstring_vendor 0.1.0",
  "parking_lot 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "selectors 0.19.0",
  "style 0.0.1",
  "style_traits 0.0.1",
@@ -753,17 +753,17 @@ dependencies = [
  "winapi 0.2.8 (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.13.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.14.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.6 (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)",
  "phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
  "precomputed-hash 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "servo_arc 0.0.1",
  "smallvec 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -840,17 +840,17 @@ dependencies = [
  "arraydeque 0.2.3 (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.25.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "bit-vec 0.4.3 (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.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "cssparser 0.13.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "fnv 1.0.5 (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.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "nsstring_vendor 0.1.0",
@@ -884,18 +884,19 @@ dependencies = [
  "synstructure 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "style_traits"
 version = "0.0.1"
 dependencies = [
  "app_units 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "cssparser 0.13.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "selectors 0.19.0",
 ]
 
 [[package]]
 name = "syn"
 version = "0.11.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "quote 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1185,17 +1186,17 @@ dependencies = [
 "checksum cexpr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "393a5f0088efbe41f9d1fcd062f24e83c278608420e62109feb2c8abee07de7d"
 "checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c"
 "checksum clang-sys 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff7c2d1502c65748c7221f43ce670b3ba5c697acebfeb85a580827daca6975fc"
 "checksum clap 2.24.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6b8f69e518f967224e628896b54e41ff6acfb4dcfefc5076325c36525dac900f"
 "checksum core-foundation 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f51ce3b8ebe311c56de14231eb57572c15abebd2d32b3bcb99bcdb9c101f5ac3"
 "checksum core-foundation-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "41115a6aa5d3e1e5ef98148373f25971d1fad53818553f216495f9e67e90a624"
 "checksum core-graphics 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ead017dcf77f503dc991f6b52de6084eeea60a94b0a652baa9bf88654a28e83f"
 "checksum core-text 4.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0e9719616a10f717628e074744f8c55df7b450f7a34d29c196d14f4498aad05d"
-"checksum cssparser 0.13.7 (registry+https://github.com/rust-lang/crates.io-index)" = "ef6124306e5ebc5ab11891d063aeafdd0cdc308079b708c8b566125f3680292b"
+"checksum cssparser 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4a5ca71edbab09f8dc1e3d1c132717562c3b01c8598ab669183c5195bb1761"
 "checksum cssparser-macros 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "079adec4af52bb5275eadd004292028c79eb3c5f5b4ee8086a36d4197032f6df"
 "checksum deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1614659040e711785ed8ea24219140654da1729f3ec8a47a9719d041112fe7bf"
 "checksum dwrote 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "74114b6b49d6731835da7a28a3642651451e315f7f9b9d04e907e65a45681796"
 "checksum env_logger 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ed39959122ea027670b704fb70539f4286ddf4a49eefede23bf0b4b2a069ec03"
 "checksum euclid 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6083f113c422ff9cd855a1cf6cc8ec0903606c0eb43a0c6a0ced3bdc9731e4c1"
 "checksum fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc484842f1e2884faf56f529f960cc12ad8c71ce96cc7abba0a067c98fee344"
 "checksum freetype 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fde23272c687e4570aefec06cb71174ec0f5284b725deac4e77ba2665d635faf"
 "checksum futures 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "55f0008e13fc853f79ea8fc86e931486860d4c4c156cdffb59fa5f7fa833660a"