servo: Merge #18808 - Use the current parser location for CSS error (from servo:error-location_); r=emilio
authorSimon Sapin <simon.sapin@exyr.org>
Tue, 10 Oct 2017 12:31:24 -0500
changeset 428055 c0005a5aa643e2894a639dda34bf9e6250f55c13
parent 428054 447614924cd21b68c55aef75f500e2659c9962f5
child 428056 eb7f98e69e26b838334556015835471aeaafa757
push id97
push userfmarier@mozilla.com
push dateSat, 14 Oct 2017 01:12:59 +0000
reviewersemilio
milestone58.0a1
servo: Merge #18808 - Use the current parser location for CSS error (from servo:error-location_); r=emilio … rather than the start location of the current construct. This likely places the error just *after* of the unexpected token whereas before would be best, but that’s likely a much bigger change. See https://bugzilla.mozilla.org/show_bug.cgi?id=1378861 Source-Repo: https://github.com/servo/servo Source-Revision: c79a54dbd9d3a590f5fd8191b8e57a0b9d1d0fdb
servo/Cargo.lock
servo/components/canvas/Cargo.toml
servo/components/canvas_traits/Cargo.toml
servo/components/malloc_size_of/Cargo.toml
servo/components/script/Cargo.toml
servo/components/script_layout_interface/Cargo.toml
servo/components/selectors/Cargo.toml
servo/components/selectors/parser.rs
servo/components/style/Cargo.toml
servo/components/style/counter_style/mod.rs
servo/components/style/custom_properties.rs
servo/components/style/error_reporting.rs
servo/components/style/font_face.rs
servo/components/style/gecko/media_queries.rs
servo/components/style/gecko/selector_parser.rs
servo/components/style/macros.rs
servo/components/style/media_queries.rs
servo/components/style/properties/declaration_block.rs
servo/components/style/properties/helpers.mako.rs
servo/components/style/properties/helpers/animated_properties.mako.rs
servo/components/style/properties/longhand/background.mako.rs
servo/components/style/properties/longhand/border.mako.rs
servo/components/style/properties/longhand/box.mako.rs
servo/components/style/properties/longhand/counters.mako.rs
servo/components/style/properties/longhand/font.mako.rs
servo/components/style/properties/longhand/inherited_box.mako.rs
servo/components/style/properties/longhand/inherited_svg.mako.rs
servo/components/style/properties/longhand/inherited_text.mako.rs
servo/components/style/properties/longhand/list.mako.rs
servo/components/style/properties/longhand/outline.mako.rs
servo/components/style/properties/longhand/pointing.mako.rs
servo/components/style/properties/longhand/position.mako.rs
servo/components/style/properties/longhand/table.mako.rs
servo/components/style/properties/longhand/text.mako.rs
servo/components/style/properties/longhand/ui.mako.rs
servo/components/style/properties/properties.mako.rs
servo/components/style/properties/shorthand/background.mako.rs
servo/components/style/properties/shorthand/border.mako.rs
servo/components/style/properties/shorthand/box.mako.rs
servo/components/style/properties/shorthand/column.mako.rs
servo/components/style/properties/shorthand/font.mako.rs
servo/components/style/properties/shorthand/inherited_text.mako.rs
servo/components/style/properties/shorthand/list.mako.rs
servo/components/style/properties/shorthand/mask.mako.rs
servo/components/style/properties/shorthand/outline.mako.rs
servo/components/style/properties/shorthand/position.mako.rs
servo/components/style/properties/shorthand/text.mako.rs
servo/components/style/servo/media_queries.rs
servo/components/style/servo/selector_parser.rs
servo/components/style/stylesheets/document_rule.rs
servo/components/style/stylesheets/font_feature_values_rule.rs
servo/components/style/stylesheets/keyframes_rule.rs
servo/components/style/stylesheets/rule_parser.rs
servo/components/style/stylesheets/stylesheet.rs
servo/components/style/stylesheets/supports_rule.rs
servo/components/style/stylesheets/viewport_rule.rs
servo/components/style/values/generics/grid.rs
servo/components/style/values/generics/mod.rs
servo/components/style/values/generics/svg.rs
servo/components/style/values/mod.rs
servo/components/style/values/specified/align.rs
servo/components/style/values/specified/angle.rs
servo/components/style/values/specified/background.rs
servo/components/style/values/specified/basic_shape.rs
servo/components/style/values/specified/border.rs
servo/components/style/values/specified/box.rs
servo/components/style/values/specified/calc.rs
servo/components/style/values/specified/color.rs
servo/components/style/values/specified/effects.rs
servo/components/style/values/specified/flex.rs
servo/components/style/values/specified/font.rs
servo/components/style/values/specified/grid.rs
servo/components/style/values/specified/image.rs
servo/components/style/values/specified/length.rs
servo/components/style/values/specified/mod.rs
servo/components/style/values/specified/percentage.rs
servo/components/style/values/specified/svg.rs
servo/components/style/values/specified/text.rs
servo/components/style/values/specified/time.rs
servo/components/style/values/specified/transform.rs
servo/components/style_traits/Cargo.toml
servo/components/style_traits/lib.rs
servo/components/style_traits/values.rs
servo/components/style_traits/viewport.rs
servo/ports/geckolib/Cargo.toml
servo/ports/geckolib/error_reporter.rs
servo/tests/unit/gfx/Cargo.toml
servo/tests/unit/style/Cargo.toml
servo/tests/unit/style/size_of.rs
servo/tests/unit/style/stylesheets.rs
servo/tests/unit/stylo/Cargo.toml
--- a/servo/Cargo.lock
+++ b/servo/Cargo.lock
@@ -304,33 +304,33 @@ source = "registry+https://github.com/ru
 
 [[package]]
 name = "canvas"
 version = "0.0.1"
 dependencies = [
  "azure 0.21.2 (git+https://github.com/servo/rust-azure)",
  "canvas_traits 0.0.1",
  "compositing 0.0.1",
- "cssparser 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.15.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "gleam 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "ipc-channel 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
  "offscreen_gl_context 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "webrender 0.52.0 (git+https://github.com/servo/webrender)",
  "webrender_api 0.52.0 (git+https://github.com/servo/webrender)",
 ]
 
 [[package]]
 name = "canvas_traits"
 version = "0.0.1"
 dependencies = [
- "cssparser 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.15.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize_derive 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "ipc-channel 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "offscreen_gl_context 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
  "servo_config 0.0.1",
@@ -568,17 +568,17 @@ source = "registry+https://github.com/ru
 dependencies = [
  "core-foundation 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "cssparser"
-version = "0.21.2"
+version = "0.22.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "cssparser-macros 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "dtoa-short 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1055,17 +1055,17 @@ dependencies = [
  "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "geckoservo"
 version = "0.0.1"
 dependencies = [
  "atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "cssparser 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "malloc_size_of 0.0.1",
  "nsstring_vendor 0.1.0",
  "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "selectors 0.19.0",
  "servo_arc 0.0.1",
@@ -1125,17 +1125,17 @@ dependencies = [
  "xi-unicode 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "xml5ever 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "gfx_tests"
 version = "0.0.1"
 dependencies = [
- "cssparser 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "gfx 0.0.1",
  "ipc-channel 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "style 0.0.1",
 ]
 
 [[package]]
 name = "gfx_traits"
 version = "0.0.1"
@@ -1732,17 +1732,17 @@ dependencies = [
  "libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "malloc_size_of"
 version = "0.0.1"
 dependencies = [
  "app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "cssparser 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.15.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "hashglobe 0.1.0",
  "servo_arc 0.0.1",
  "smallbitvec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -2589,17 +2589,17 @@ dependencies = [
  "base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bluetooth_traits 0.0.1",
  "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "canvas_traits 0.0.1",
  "caseless 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "cmake 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "cookie 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "cssparser 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "deny_public_fields 0.0.1",
  "devtools_traits 0.0.1",
  "dom_struct 0.0.1",
  "domobject_derive 0.0.1",
  "encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.15.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "gleam 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2662,17 +2662,17 @@ dependencies = [
 
 [[package]]
 name = "script_layout_interface"
 version = "0.0.1"
 dependencies = [
  "app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "canvas_traits 0.0.1",
- "cssparser 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.15.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "gfx_traits 0.0.1",
  "heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize_derive 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "html5ever 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "ipc-channel 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2735,17 +2735,17 @@ dependencies = [
  "webvr_traits 0.0.1",
 ]
 
 [[package]]
 name = "selectors"
 version = "0.19.0"
 dependencies = [
  "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "cssparser 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "malloc_size_of 0.0.1",
  "malloc_size_of_derive 0.0.1",
  "matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
  "precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -3140,17 +3140,17 @@ version = "0.0.1"
 dependencies = [
  "app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "arrayvec 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)",
  "atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bindgen 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "cssparser 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.15.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "fallible 0.0.1",
  "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "hashglobe 0.1.0",
  "heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize_derive 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "html5ever 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -3202,17 +3202,17 @@ dependencies = [
 ]
 
 [[package]]
 name = "style_tests"
 version = "0.0.1"
 dependencies = [
  "app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "cssparser 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.15.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "html5ever 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
  "selectors 0.19.0",
  "servo_arc 0.0.1",
  "servo_atoms 0.0.1",
@@ -3224,17 +3224,17 @@ dependencies = [
 ]
 
 [[package]]
 name = "style_traits"
 version = "0.0.1"
 dependencies = [
  "app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "cssparser 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.15.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize_derive 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "malloc_size_of 0.0.1",
  "malloc_size_of_derive 0.0.1",
  "selectors 0.19.0",
  "serde 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
  "servo_arc 0.0.1",
@@ -3242,17 +3242,17 @@ dependencies = [
  "webrender_api 0.52.0 (git+https://github.com/servo/webrender)",
 ]
 
 [[package]]
 name = "stylo_tests"
 version = "0.0.1"
 dependencies = [
  "atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "cssparser 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cssparser 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.15.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "geckoservo 0.0.1",
  "libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "malloc_size_of 0.0.1",
  "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "selectors 0.19.0",
@@ -3831,17 +3831,17 @@ dependencies = [
 "checksum cocoa 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "79e7105114e16c4b6f52b117f23b5d5892cd6c4e24276bcf92d305698e3e1ad6"
 "checksum color_quant 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a475fc4af42d83d28adf72968d9bcfaf035a1a9381642d8e85d8a04957767b0d"
 "checksum compiletest_rs 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "617b23d0ed4f57b3bcff6b5fe0a78f0010f1efb636298317665a960b6dbc0533"
 "checksum cookie 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "30b3493e12a550c2f96be785088d1da8d93189e7237c8a8d0d871bc9070334c3"
 "checksum core-foundation 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5909502e547762013619f4c4e01cc7393c20fe2d52d7fa471c1210adb2320dc7"
 "checksum core-foundation-sys 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bc9fb3d6cb663e6fd7cf1c63f9b144ee2b1e4a78595a0451dd34bff85b9a3387"
 "checksum core-graphics 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2fd47addfc77b7e574d24e5434f95bb64a863769dfd4f1d451ca4ff5530ba01a"
 "checksum core-text 7.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2a23bef779fab70e5e6af23e36eed03a48e1c1687dea8929505d405ea48d1f5e"
-"checksum cssparser 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)" = "05012fcdbdeb4c6f59f67db47c1f879929339274a691ee1fb90c26b297783629"
+"checksum cssparser 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44313341610282488e1156ad1fedebca51c54766c87a041d0287b10499c04ba1"
 "checksum cssparser-macros 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "079adec4af52bb5275eadd004292028c79eb3c5f5b4ee8086a36d4197032f6df"
 "checksum darling 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9861a8495606435477df581bc858ccf15a3469747edf175b94a4704fd9aaedac"
 "checksum darling_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1486a8b00b45062c997f767738178b43219133dd0c8c826cb811e60563810821"
 "checksum darling_macro 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a86ec160aa0c3dd492dd4a14ec8104ad8f1a9400a820624db857998cc1f80f9"
 "checksum dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97590ba53bcb8ac28279161ca943a924d1fd4a8fb3fa63302591647c4fc5b850"
 "checksum dbus 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4aee01fb76ada3e5e7ca642ea6664ebf7308a810739ca2aca44909a1191ac254"
 "checksum debug_unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9a032eac705ca39214d169f83e3d3da290af06d8d1d344d1baad2fd002dca4b3"
 "checksum deflate 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ebb02aaf4b775afc96684b8402510a338086974e38570a1f65bea8c202eb77a7"
--- a/servo/components/canvas/Cargo.toml
+++ b/servo/components/canvas/Cargo.toml
@@ -8,17 +8,17 @@ publish = false
 [lib]
 name = "canvas"
 path = "lib.rs"
 
 [dependencies]
 azure = {git = "https://github.com/servo/rust-azure"}
 canvas_traits = {path = "../canvas_traits"}
 compositing = {path = "../compositing"}
-cssparser = "0.21.1"
+cssparser = "0.22.0"
 euclid = "0.15"
 fnv = "1.0"
 gleam = "0.4"
 ipc-channel = "0.8"
 log = "0.3.5"
 num-traits = "0.1.32"
 offscreen_gl_context = { version = "0.11", features = ["serde", "osmesa"] }
 webrender = {git = "https://github.com/servo/webrender"}
--- a/servo/components/canvas_traits/Cargo.toml
+++ b/servo/components/canvas_traits/Cargo.toml
@@ -5,17 +5,17 @@ authors = ["The Servo Project Developers
 license = "MPL-2.0"
 publish = false
 
 [lib]
 name = "canvas_traits"
 path = "lib.rs"
 
 [dependencies]
-cssparser = "0.21.1"
+cssparser = "0.22.0"
 euclid = "0.15"
 heapsize = "0.4"
 heapsize_derive = "0.1"
 ipc-channel = "0.8"
 lazy_static = "0.2"
 offscreen_gl_context = { version = "0.11", features = ["serde"] }
 serde = "1.0"
 servo_config = {path = "../config"}
--- a/servo/components/malloc_size_of/Cargo.toml
+++ b/servo/components/malloc_size_of/Cargo.toml
@@ -5,14 +5,14 @@ authors = ["The Servo Project Developers
 license = "MIT/Apache-2.0"
 publish = false
 
 [lib]
 path = "lib.rs"
 
 [dependencies]
 app_units = "0.5.5"
-cssparser = "0.21.1"
+cssparser = "0.22.0"
 euclid = "0.15"
 hashglobe = { path = "../hashglobe" }
 servo_arc = { path = "../servo_arc" }
 smallbitvec = "1.0.3"
 smallvec = "0.4"
--- a/servo/components/script/Cargo.toml
+++ b/servo/components/script/Cargo.toml
@@ -28,17 +28,17 @@ app_units = "0.5"
 audio-video-metadata = "0.1.4"
 base64 = "0.6"
 bitflags = "0.7"
 bluetooth_traits = {path = "../bluetooth_traits"}
 byteorder = "1.0"
 canvas_traits = {path = "../canvas_traits"}
 caseless = "0.1.0"
 cookie = "0.6"
-cssparser = "0.21.1"
+cssparser = "0.22.0"
 deny_public_fields = {path = "../deny_public_fields"}
 devtools_traits = {path = "../devtools_traits"}
 dom_struct = {path = "../dom_struct"}
 domobject_derive = {path = "../domobject_derive"}
 encoding = "0.2"
 euclid = "0.15"
 fnv = "1.0"
 gleam = "0.4"
--- a/servo/components/script_layout_interface/Cargo.toml
+++ b/servo/components/script_layout_interface/Cargo.toml
@@ -8,17 +8,17 @@ publish = false
 [lib]
 name = "script_layout_interface"
 path = "lib.rs"
 
 [dependencies]
 app_units = "0.5"
 atomic_refcell = "0.1"
 canvas_traits = {path = "../canvas_traits"}
-cssparser = "0.21.1"
+cssparser = "0.22.0"
 euclid = "0.15"
 gfx_traits = {path = "../gfx_traits"}
 heapsize = "0.4"
 heapsize_derive = "0.1"
 html5ever = "0.20.0"
 ipc-channel = "0.8"
 libc = "0.2"
 log = "0.3.5"
--- a/servo/components/selectors/Cargo.toml
+++ b/servo/components/selectors/Cargo.toml
@@ -20,17 +20,17 @@ doctest = false
 
 [features]
 gecko_like_types = []
 unstable = []
 
 [dependencies]
 bitflags = "0.7"
 matches = "0.1"
-cssparser = "0.21.1"
+cssparser = "0.22.0"
 log = "0.3"
 fnv = "1.0"
 malloc_size_of = { path = "../malloc_size_of" }
 malloc_size_of_derive = { path = "../malloc_size_of_derive" }
 phf = "0.7.18"
 precomputed-hash = "0.1"
 servo_arc = { path = "../servo_arc" }
 smallvec = "0.4"
--- a/servo/components/selectors/parser.rs
+++ b/servo/components/selectors/parser.rs
@@ -2,17 +2,18 @@
  * 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 attr::{AttrSelectorWithNamespace, ParsedAttrSelectorOperation, AttrSelectorOperator};
 use attr::{ParsedCaseSensitivity, SELECTOR_WHITESPACE, NamespaceConstraint};
 use bloom::BLOOM_HASH_MASK;
 use builder::{SelectorBuilder, SpecificityAndFlags};
 use context::QuirksMode;
-use cssparser::{ParseError, BasicParseError, CowRcStr, Delimiter};
+use cssparser::{ParseError, ParseErrorKind, BasicParseError, BasicParseErrorKind};
+use cssparser::{SourceLocation, CowRcStr, Delimiter};
 use cssparser::{Token, Parser as CssParser, parse_nth, ToCss, serialize_identifier, CssStringWriter};
 use precomputed_hash::PrecomputedHash;
 use servo_arc::ThinArc;
 use sink::Push;
 use smallvec::SmallVec;
 use std::ascii::AsciiExt;
 use std::borrow::{Borrow, Cow};
 use std::fmt::{self, Display, Debug, Write};
@@ -41,18 +42,20 @@ fn to_ascii_lowercase(s: &str) -> Cow<st
         let mut string = s.to_owned();
         string[first_uppercase..].make_ascii_lowercase();
         string.into()
     } else {
         s.into()
     }
 }
 
+pub type SelectorParseError<'i> = ParseError<'i, SelectorParseErrorKind<'i>>;
+
 #[derive(Clone, Debug, PartialEq)]
-pub enum SelectorParseError<'i, T> {
+pub enum SelectorParseErrorKind<'i> {
     PseudoElementInComplexSelector,
     NoQualifiedNameInAttributeSelector(Token<'i>),
     EmptySelector,
     DanglingCombinator,
     NonSimpleSelectorInNegation,
     UnexpectedTokenInAttributeSelector(Token<'i>),
     PseudoElementExpectedColon(Token<'i>),
     PseudoElementExpectedIdent(Token<'i>),
@@ -61,23 +64,16 @@ pub enum SelectorParseError<'i, T> {
     UnexpectedIdent(CowRcStr<'i>),
     ExpectedNamespace(CowRcStr<'i>),
     ExpectedBarInAttr(Token<'i>),
     BadValueInAttr(Token<'i>),
     InvalidQualNameInAttr(Token<'i>),
     ExplicitNamespaceUnexpectedToken(Token<'i>),
     ClassNeedsIdent(Token<'i>),
     EmptyNegation,
-    Custom(T),
-}
-
-impl<'a, T> Into<ParseError<'a, SelectorParseError<'a, T>>> for SelectorParseError<'a, T> {
-    fn into(self) -> ParseError<'a, SelectorParseError<'a, T>> {
-        ParseError::Custom(self)
-    }
 }
 
 macro_rules! with_all_bounds {
     (
         [ $( $InSelector: tt )* ]
         [ $( $CommonBounds: tt )* ]
         [ $( $FromStr: tt )* ]
     ) => {
@@ -123,51 +119,52 @@ macro_rules! with_bounds {
 
 with_bounds! {
     [Clone + Eq]
     [for<'a> From<&'a str>]
 }
 
 pub trait Parser<'i> {
     type Impl: SelectorImpl;
-    type Error: 'i;
+    type Error: 'i + From<SelectorParseErrorKind<'i>>;
 
     /// Whether the name is a pseudo-element that can be specified with
     /// the single colon syntax in addition to the double-colon syntax.
     fn is_pseudo_element_allows_single_colon(name: &CowRcStr<'i>) -> bool {
         is_css2_pseudo_element(name)
     }
 
     /// This function can return an "Err" pseudo-element in order to support CSS2.1
     /// pseudo-elements.
-    fn parse_non_ts_pseudo_class(&self, name: CowRcStr<'i>)
+    fn parse_non_ts_pseudo_class(&self, location: SourceLocation, name: CowRcStr<'i>)
                                  -> Result<<Self::Impl as SelectorImpl>::NonTSPseudoClass,
-                                           ParseError<'i, SelectorParseError<'i, Self::Error>>> {
-        Err(ParseError::Custom(SelectorParseError::UnsupportedPseudoClassOrElement(name)))
+                                           ParseError<'i, Self::Error>>
+    {
+        Err(location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name)))
     }
 
     fn parse_non_ts_functional_pseudo_class<'t>
-        (&self, name: CowRcStr<'i>, _arguments: &mut CssParser<'i, 't>)
-         -> Result<<Self::Impl as SelectorImpl>::NonTSPseudoClass,
-                   ParseError<'i, SelectorParseError<'i, Self::Error>>>
+        (&self, name: CowRcStr<'i>, arguments: &mut CssParser<'i, 't>)
+         -> Result<<Self::Impl as SelectorImpl>::NonTSPseudoClass, ParseError<'i, Self::Error>>
     {
-        Err(ParseError::Custom(SelectorParseError::UnsupportedPseudoClassOrElement(name)))
+        Err(arguments.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name)))
     }
 
-    fn parse_pseudo_element(&self, name: CowRcStr<'i>)
+    fn parse_pseudo_element(&self, location: SourceLocation, name: CowRcStr<'i>)
                             -> Result<<Self::Impl as SelectorImpl>::PseudoElement,
-                                      ParseError<'i, SelectorParseError<'i, Self::Error>>> {
-        Err(ParseError::Custom(SelectorParseError::UnsupportedPseudoClassOrElement(name)))
+                                      ParseError<'i, Self::Error>>
+    {
+        Err(location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name)))
     }
 
     fn parse_functional_pseudo_element<'t>
-        (&self, name: CowRcStr<'i>, _arguments: &mut CssParser<'i, 't>)
-         -> Result<<Self::Impl as SelectorImpl>::PseudoElement,
-                   ParseError<'i, SelectorParseError<'i, Self::Error>>> {
-        Err(ParseError::Custom(SelectorParseError::UnsupportedPseudoClassOrElement(name)))
+        (&self, name: CowRcStr<'i>, arguments: &mut CssParser<'i, 't>)
+         -> Result<<Self::Impl as SelectorImpl>::PseudoElement, ParseError<'i, Self::Error>>
+    {
+        Err(arguments.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name)))
     }
 
     fn default_namespace(&self) -> Option<<Self::Impl as SelectorImpl>::NamespaceUrl> {
         None
     }
 
     fn namespace_for_prefix(&self, _prefix: &<Self::Impl as SelectorImpl>::NamespacePrefix)
                             -> Option<<Self::Impl as SelectorImpl>::NamespaceUrl> {
@@ -178,19 +175,19 @@ pub trait Parser<'i> {
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub struct SelectorList<Impl: SelectorImpl>(pub SmallVec<[Selector<Impl>; 1]>);
 
 impl<Impl: SelectorImpl> SelectorList<Impl> {
     /// Parse a comma-separated list of Selectors.
     /// https://drafts.csswg.org/selectors/#grouping
     ///
     /// Return the Selectors or Err if there is an invalid selector.
-    pub fn parse<'i, 't, P, E>(parser: &P, input: &mut CssParser<'i, 't>)
-                               -> Result<Self, ParseError<'i, SelectorParseError<'i, E>>>
-    where P: Parser<'i, Impl=Impl, Error=E> {
+    pub fn parse<'i, 't, P>(parser: &P, input: &mut CssParser<'i, 't>)
+                            -> Result<Self, ParseError<'i, P::Error>>
+    where P: Parser<'i, Impl=Impl> {
         let mut values = SmallVec::new();
         loop {
             values.push(input.parse_until_before(Delimiter::Comma, |input| parse_selector(parser, input))?);
             match input.next() {
                 Err(_) => return Ok(SelectorList(values)),
                 Ok(&Token::Comma) => continue,
                 Ok(_) => unreachable!(),
             }
@@ -1047,34 +1044,38 @@ fn display_to_css_identifier<T: Display,
 
     serialize_identifier(&string, dest)
 }
 
 /// Build up a Selector.
 /// selector : simple_selector_sequence [ combinator simple_selector_sequence ]* ;
 ///
 /// `Err` means invalid selector.
-fn parse_selector<'i, 't, P, E, Impl>(
+fn parse_selector<'i, 't, P, Impl>(
         parser: &P,
         input: &mut CssParser<'i, 't>)
-        -> Result<Selector<Impl>, ParseError<'i, SelectorParseError<'i, E>>>
-    where P: Parser<'i, Impl=Impl, Error=E>, Impl: SelectorImpl
+        -> Result<Selector<Impl>, ParseError<'i, P::Error>>
+    where P: Parser<'i, Impl=Impl>, Impl: SelectorImpl
 {
     let mut builder = SelectorBuilder::default();
 
-    let mut parsed_pseudo_element;
+    let mut has_pseudo_element;
     'outer_loop: loop {
         // Parse a sequence of simple selectors.
-        parsed_pseudo_element = match parse_compound_selector(parser, input, &mut builder) {
-            Ok(result) => result,
-            Err(ParseError::Custom(SelectorParseError::EmptySelector)) if builder.has_combinators() =>
-                return Err(SelectorParseError::DanglingCombinator.into()),
-            Err(e) => return Err(e),
+        has_pseudo_element = match parse_compound_selector(parser, input, &mut builder)? {
+            Some(has_pseudo_element) => has_pseudo_element,
+            None => {
+                return Err(input.new_custom_error(if builder.has_combinators() {
+                    SelectorParseErrorKind::DanglingCombinator
+                } else {
+                    SelectorParseErrorKind::EmptySelector
+                }))
+            }
         };
-        if parsed_pseudo_element {
+        if has_pseudo_element {
             break;
         }
 
         // Parse a combinator.
         let combinator;
         let mut any_whitespace = false;
         loop {
             let before_this_token = input.state();
@@ -1102,44 +1103,44 @@ fn parse_selector<'i, 't, P, E, Impl>(
                         break 'outer_loop
                     }
                 }
             }
         }
         builder.push_combinator(combinator);
     }
 
-    Ok(Selector(builder.build(parsed_pseudo_element)))
+    Ok(Selector(builder.build(has_pseudo_element)))
 }
 
 impl<Impl: SelectorImpl> Selector<Impl> {
     /// Parse a selector, without any pseudo-element.
-    pub fn parse<'i, 't, P, E>(parser: &P, input: &mut CssParser<'i, 't>)
-                               -> Result<Self, ParseError<'i, SelectorParseError<'i, E>>>
-        where P: Parser<'i, Impl=Impl, Error=E>
+    pub fn parse<'i, 't, P>(parser: &P, input: &mut CssParser<'i, 't>)
+                            -> Result<Self, ParseError<'i, P::Error>>
+        where P: Parser<'i, Impl=Impl>
     {
         let selector = parse_selector(parser, input)?;
         if selector.has_pseudo_element() {
-            return Err(ParseError::Custom(SelectorParseError::PseudoElementInComplexSelector))
+            return Err(input.new_custom_error(SelectorParseErrorKind::PseudoElementInComplexSelector))
         }
         Ok(selector)
     }
 }
 
 /// * `Err(())`: Invalid selector, abort
 /// * `Ok(false)`: Not a type selector, could be something else. `input` was not consumed.
 /// * `Ok(true)`: Length 0 (`*|*`), 1 (`*|E` or `ns|*`) or 2 (`|E` or `ns|E`)
-fn parse_type_selector<'i, 't, P, E, Impl, S>(parser: &P, input: &mut CssParser<'i, 't>, sink: &mut S)
-                                              -> Result<bool, ParseError<'i, SelectorParseError<'i, E>>>
-    where P: Parser<'i, Impl=Impl, Error=E>,
+fn parse_type_selector<'i, 't, P, Impl, S>(parser: &P, input: &mut CssParser<'i, 't>, sink: &mut S)
+                                           -> Result<bool, ParseError<'i, P::Error>>
+    where P: Parser<'i, Impl=Impl>,
           Impl: SelectorImpl,
           S: Push<Component<Impl>>,
 {
     match parse_qualified_name(parser, input, /* in_attr_selector = */ false) {
-        Err(ParseError::Basic(BasicParseError::EndOfInput)) |
+        Err(ParseError { kind: ParseErrorKind::Basic(BasicParseErrorKind::EndOfInput), .. }) |
         Ok(OptionalQName::None(_)) => Ok(false),
         Ok(OptionalQName::Some(namespace, local_name)) => {
             match namespace {
                 QNamePrefix::ImplicitAnyNamespace => {}
                 QNamePrefix::ImplicitDefaultNamespace(url) => {
                     sink.push(Component::DefaultNamespace(url))
                 }
                 QNamePrefix::ExplicitNamespace(prefix, url) => {
@@ -1209,56 +1210,64 @@ enum OptionalQName<'i, Impl: SelectorImp
     Some(QNamePrefix<Impl>, Option<CowRcStr<'i>>),
     None(Token<'i>),
 }
 
 /// * `Err(())`: Invalid selector, abort
 /// * `Ok(None(token))`: Not a simple selector, could be something else. `input` was not consumed,
 ///                      but the token is still returned.
 /// * `Ok(Some(namespace, local_name))`: `None` for the local name means a `*` universal selector
-fn parse_qualified_name<'i, 't, P, E, Impl>
+fn parse_qualified_name<'i, 't, P, Impl>
                        (parser: &P, input: &mut CssParser<'i, 't>,
                         in_attr_selector: bool)
-                        -> Result<OptionalQName<'i, Impl>,
-                                  ParseError<'i, SelectorParseError<'i, E>>>
-    where P: Parser<'i, Impl=Impl, Error=E>, Impl: SelectorImpl
+                        -> Result<OptionalQName<'i, Impl>, ParseError<'i, P::Error>>
+    where P: Parser<'i, Impl=Impl>, Impl: SelectorImpl
 {
     let default_namespace = |local_name| {
         let namespace = match parser.default_namespace() {
             Some(url) => QNamePrefix::ImplicitDefaultNamespace(url),
             None => QNamePrefix::ImplicitAnyNamespace,
         };
         Ok(OptionalQName::Some(namespace, local_name))
     };
 
     let explicit_namespace = |input: &mut CssParser<'i, 't>, namespace| {
+        let location = input.current_source_location();
         match input.next_including_whitespace() {
             Ok(&Token::Delim('*')) if !in_attr_selector => {
                 Ok(OptionalQName::Some(namespace, None))
-            },
+            }
             Ok(&Token::Ident(ref local_name)) => {
                 Ok(OptionalQName::Some(namespace, Some(local_name.clone())))
-            },
-            Ok(t) if in_attr_selector => Err(SelectorParseError::InvalidQualNameInAttr(t.clone()).into()),
-            Ok(t) => Err(SelectorParseError::ExplicitNamespaceUnexpectedToken(t.clone()).into()),
-            Err(e) => Err(ParseError::Basic(e)),
+            }
+            Ok(t) if in_attr_selector => {
+                Err(location.new_custom_error(
+                    SelectorParseErrorKind::InvalidQualNameInAttr(t.clone())
+                ))
+            }
+            Ok(t) => {
+                Err(location.new_custom_error(
+                    SelectorParseErrorKind::ExplicitNamespaceUnexpectedToken(t.clone())
+                ))
+            }
+            Err(e) => Err(e.into()),
         }
     };
 
     let start = input.state();
     // FIXME: remove clone() when lifetimes are non-lexical
     match input.next_including_whitespace().map(|t| t.clone()) {
         Ok(Token::Ident(value)) => {
             let after_ident = input.state();
             match input.next_including_whitespace() {
                 Ok(&Token::Delim('|')) => {
                     let prefix = value.as_ref().into();
                     let result = parser.namespace_for_prefix(&prefix);
-                    let url = result.ok_or(ParseError::Custom(
-                        SelectorParseError::ExpectedNamespace(value)))?;
+                    let url = result.ok_or(after_ident.source_location().new_custom_error(
+                        SelectorParseErrorKind::ExpectedNamespace(value)))?;
                     explicit_namespace(input, QNamePrefix::ExplicitNamespace(prefix, url))
                 },
                 _ => {
                     input.reset(&after_ident);
                     if in_attr_selector {
                         Ok(OptionalQName::Some(QNamePrefix::ImplicitNoNamespace, Some(value)))
                     } else {
                         default_namespace(Some(value))
@@ -1272,18 +1281,20 @@ fn parse_qualified_name<'i, 't, P, E, Im
             match input.next_including_whitespace().map(|t| t.clone()) {
                 Ok(Token::Delim('|')) => {
                     explicit_namespace(input, QNamePrefix::ExplicitAnyNamespace)
                 }
                 result => {
                     input.reset(&after_star);
                     if in_attr_selector {
                         match result {
-                            Ok(t) => Err(SelectorParseError::ExpectedBarInAttr(t).into()),
-                            Err(e) => Err(ParseError::Basic(e)),
+                            Ok(t) => Err(after_star.source_location().new_custom_error(
+                                SelectorParseErrorKind::ExpectedBarInAttr(t)
+                            )),
+                            Err(e) => Err(e.into()),
                         }
                     } else {
                         default_namespace(None)
                     }
                 },
             }
         },
         Ok(Token::Delim('|')) => {
@@ -1296,26 +1307,28 @@ fn parse_qualified_name<'i, 't, P, E, Im
         Err(e) => {
             input.reset(&start);
             Err(e.into())
         }
     }
 }
 
 
-fn parse_attribute_selector<'i, 't, P, E, Impl>(parser: &P, input: &mut CssParser<'i, 't>)
-                                                -> Result<Component<Impl>,
-                                                          ParseError<'i, SelectorParseError<'i, E>>>
-    where P: Parser<'i, Impl=Impl, Error=E>, Impl: SelectorImpl
+fn parse_attribute_selector<'i, 't, P, Impl>(parser: &P, input: &mut CssParser<'i, 't>)
+                                             -> Result<Component<Impl>, ParseError<'i, P::Error>>
+    where P: Parser<'i, Impl=Impl>, Impl: SelectorImpl
 {
     let namespace;
     let local_name;
     match parse_qualified_name(parser, input, /* in_attr_selector = */ true)? {
-        OptionalQName::None(t) =>
-            return Err(ParseError::Custom(SelectorParseError::NoQualifiedNameInAttributeSelector(t))),
+        OptionalQName::None(t) => {
+            return Err(input.new_custom_error(
+                SelectorParseErrorKind::NoQualifiedNameInAttributeSelector(t)
+            ))
+        }
         OptionalQName::Some(_, None) => unreachable!(),
         OptionalQName::Some(ns, Some(ln)) => {
             local_name = ln;
             namespace = match ns {
                 QNamePrefix::ImplicitNoNamespace |
                 QNamePrefix::ExplicitNoNamespace => {
                     None
                 }
@@ -1328,16 +1341,17 @@ fn parse_attribute_selector<'i, 't, P, E
                 QNamePrefix::ImplicitAnyNamespace |
                 QNamePrefix::ImplicitDefaultNamespace(_) => {
                     unreachable!()  // Not returned with in_attr_selector = true
                 }
             }
         }
     }
 
+    let location = input.current_source_location();
     let operator = match input.next() {
         // [foo]
         Err(_) => {
             let local_name_lower = to_ascii_lowercase(&local_name).as_ref().into();
             let local_name = local_name.as_ref().into();
             if let Some(namespace) = namespace {
                 return Ok(Component::AttributeOther(Box::new(AttrSelectorWithNamespace {
                     namespace: namespace,
@@ -1361,23 +1375,26 @@ fn parse_attribute_selector<'i, 't, P, E
         // [foo|=bar]
         Ok(&Token::DashMatch) => AttrSelectorOperator::DashMatch,
         // [foo^=bar]
         Ok(&Token::PrefixMatch) => AttrSelectorOperator::Prefix,
         // [foo*=bar]
         Ok(&Token::SubstringMatch) => AttrSelectorOperator::Substring,
         // [foo$=bar]
         Ok(&Token::SuffixMatch) => AttrSelectorOperator::Suffix,
-        Ok(t) => return Err(SelectorParseError::UnexpectedTokenInAttributeSelector(t.clone()).into())
+        Ok(t) => return Err(location.new_custom_error(
+            SelectorParseErrorKind::UnexpectedTokenInAttributeSelector(t.clone())
+        ))
     };
 
     let value = match input.expect_ident_or_string() {
         Ok(t) => t.clone(),
-        Err(BasicParseError::UnexpectedToken(t)) =>
-            return Err(SelectorParseError::BadValueInAttr(t.clone()).into()),
+        Err(BasicParseError { kind: BasicParseErrorKind::UnexpectedToken(t), location }) => {
+            return Err(location.new_custom_error(SelectorParseErrorKind::BadValueInAttr(t)))
+        }
         Err(e) => return Err(e.into()),
     };
     let never_matches = match operator {
         AttrSelectorOperator::Equal |
         AttrSelectorOperator::DashMatch => false,
 
         AttrSelectorOperator::Includes => {
             value.is_empty() || value.contains(SELECTOR_WHITESPACE)
@@ -1426,84 +1443,86 @@ fn parse_attribute_selector<'i, 't, P, E
             value: value,
             case_sensitivity: case_sensitivity,
             never_matches: never_matches,
         })
     }
 }
 
 
-fn parse_attribute_flags<'i, 't, E>(input: &mut CssParser<'i, 't>)
-                                    -> Result<ParsedCaseSensitivity,
-                                              ParseError<'i, SelectorParseError<'i, E>>> {
+fn parse_attribute_flags<'i, 't>(input: &mut CssParser<'i, 't>)
+                                 -> Result<ParsedCaseSensitivity, BasicParseError<'i>>
+{
+    let location = input.current_source_location();
     match input.next() {
         Err(_) => {
             // Selectors spec says language-defined, but HTML says sensitive.
             Ok(ParsedCaseSensitivity::CaseSensitive)
         }
         Ok(&Token::Ident(ref value)) if value.eq_ignore_ascii_case("i") => {
             Ok(ParsedCaseSensitivity::AsciiCaseInsensitive)
         }
-        Ok(t) => Err(ParseError::Basic(BasicParseError::UnexpectedToken(t.clone())))
+        Ok(t) => Err(location.new_basic_unexpected_token_error(t.clone()))
     }
 }
 
 
 /// Level 3: Parse **one** simple_selector.  (Though we might insert a second
 /// implied "<defaultns>|*" type selector.)
-fn parse_negation<'i, 't, P, E, Impl>(parser: &P,
-                                      input: &mut CssParser<'i, 't>)
-                                      -> Result<Component<Impl>,
-                                                ParseError<'i, SelectorParseError<'i, E>>>
-    where P: Parser<'i, Impl=Impl, Error=E>, Impl: SelectorImpl
+fn parse_negation<'i, 't, P, Impl>(parser: &P,
+                                   input: &mut CssParser<'i, 't>)
+                                   -> Result<Component<Impl>, ParseError<'i, P::Error>>
+    where P: Parser<'i, Impl=Impl>, Impl: SelectorImpl
 {
     // We use a sequence because a type selector may be represented as two Components.
     let mut sequence = SmallVec::<[Component<Impl>; 2]>::new();
 
     input.skip_whitespace();
 
     // Get exactly one simple selector. The parse logic in the caller will verify
     // that there are no trailing tokens after we're done.
     let is_type_sel = match parse_type_selector(parser, input, &mut sequence) {
         Ok(result) => result,
-        Err(ParseError::Basic(BasicParseError::EndOfInput)) =>
-            return Err(SelectorParseError::EmptyNegation.into()),
+        Err(ParseError { kind: ParseErrorKind::Basic(BasicParseErrorKind::EndOfInput), .. }) => {
+            return Err(input.new_custom_error(SelectorParseErrorKind::EmptyNegation))
+        }
         Err(e) => return Err(e.into()),
     };
     if !is_type_sel {
         match parse_one_simple_selector(parser, input, /* inside_negation = */ true)? {
             Some(SimpleSelectorParseResult::SimpleSelector(s)) => {
                 sequence.push(s);
             },
             None => {
-                return Err(ParseError::Custom(SelectorParseError::EmptyNegation));
+                return Err(input.new_custom_error(SelectorParseErrorKind::EmptyNegation));
             },
             Some(SimpleSelectorParseResult::PseudoElement(_)) => {
-                return Err(ParseError::Custom(SelectorParseError::NonSimpleSelectorInNegation));
+                return Err(input.new_custom_error(SelectorParseErrorKind::NonSimpleSelectorInNegation));
             }
         }
     }
 
     // Success.
     Ok(Component::Negation(sequence.into_vec().into_boxed_slice()))
 }
 
 /// simple_selector_sequence
 /// : [ type_selector | universal ] [ HASH | class | attrib | pseudo | negation ]*
 /// | [ HASH | class | attrib | pseudo | negation ]+
 ///
 /// `Err(())` means invalid selector.
+/// `Ok(None)` is an empty selector
 ///
 /// The boolean represent whether a pseudo-element has been parsed.
-fn parse_compound_selector<'i, 't, P, E, Impl>(
+fn parse_compound_selector<'i, 't, P, Impl>(
     parser: &P,
     input: &mut CssParser<'i, 't>,
     builder: &mut SelectorBuilder<Impl>)
-    -> Result<bool, ParseError<'i, SelectorParseError<'i, E>>>
-    where P: Parser<'i, Impl=Impl, Error=E>, Impl: SelectorImpl
+    -> Result<Option<bool>, ParseError<'i, P::Error>>
+    where P: Parser<'i, Impl=Impl>, Impl: SelectorImpl
 {
     input.skip_whitespace();
 
     let mut empty = true;
     if !parse_type_selector(parser, input, builder)? {
         if let Some(url) = parser.default_namespace() {
             // If there was no explicit type selector, but there is a
             // default namespace, there is an implicit "<defaultns>|*" type
@@ -1523,34 +1542,42 @@ fn parse_compound_selector<'i, 't, P, E,
                 empty = false
             }
             Some(SimpleSelectorParseResult::PseudoElement(p)) => {
                 // Try to parse state to its right. There are only 3 allowable
                 // state selectors that can go on pseudo-elements.
                 let mut state_selectors = SmallVec::<[Component<Impl>; 3]>::new();
 
                 loop {
+                    let location = input.current_source_location();
                     match input.next_including_whitespace() {
                         Ok(&Token::Colon) => {},
                         Ok(&Token::WhiteSpace(_)) | Err(_) => break,
                         Ok(t) =>
-                            return Err(SelectorParseError::PseudoElementExpectedColon(t.clone()).into()),
+                            return Err(location.new_custom_error(
+                                SelectorParseErrorKind::PseudoElementExpectedColon(t.clone())
+                            )),
                     }
 
+                    let location = input.current_source_location();
                     // TODO(emilio): Functional pseudo-classes too?
                     // We don't need it for now.
                     let name = match input.next_including_whitespace()? {
                         &Token::Ident(ref name) => name.clone(),
-                        t => return Err(SelectorParseError::NoIdentForPseudo(t.clone()).into()),
+                        t => return Err(location.new_custom_error(
+                            SelectorParseErrorKind::NoIdentForPseudo(t.clone())
+                        )),
                     };
 
                     let pseudo_class =
-                        P::parse_non_ts_pseudo_class(parser, name.clone())?;
+                        P::parse_non_ts_pseudo_class(parser, location, name.clone())?;
                     if !p.supports_pseudo_class(&pseudo_class) {
-                        return Err(SelectorParseError::UnsupportedPseudoClassOrElement(name).into());
+                        return Err(input.new_custom_error(
+                            SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name)
+                        ));
                     }
                     state_selectors.push(Component::NonTSPseudoClass(pseudo_class));
                 }
 
                 if !builder.is_empty() {
                     builder.push_combinator(Combinator::PseudoElement);
                 }
 
@@ -1562,51 +1589,51 @@ fn parse_compound_selector<'i, 't, P, E,
                 pseudo = true;
                 empty = false;
                 break
             }
         }
     }
     if empty {
         // An empty selector is invalid.
-        Err(ParseError::Custom(SelectorParseError::EmptySelector))
+        Ok(None)
     } else {
-        Ok(pseudo)
+        Ok(Some(pseudo))
     }
 }
 
-fn parse_functional_pseudo_class<'i, 't, P, E, Impl>(parser: &P,
-                                                     input: &mut CssParser<'i, 't>,
-                                                     name: CowRcStr<'i>,
-                                                     inside_negation: bool)
-                                                     -> Result<Component<Impl>,
-                                                               ParseError<'i, SelectorParseError<'i, E>>>
-    where P: Parser<'i, Impl=Impl, Error=E>, Impl: SelectorImpl
+fn parse_functional_pseudo_class<'i, 't, P, Impl>(parser: &P,
+                                                  input: &mut CssParser<'i, 't>,
+                                                  name: CowRcStr<'i>,
+                                                  inside_negation: bool)
+                                                  -> Result<Component<Impl>, ParseError<'i, P::Error>>
+    where P: Parser<'i, Impl=Impl>, Impl: SelectorImpl
 {
     match_ignore_ascii_case! { &name,
-        "nth-child" => return parse_nth_pseudo_class(input, Component::NthChild),
-        "nth-of-type" => return parse_nth_pseudo_class(input, Component::NthOfType),
-        "nth-last-child" => return parse_nth_pseudo_class(input, Component::NthLastChild),
-        "nth-last-of-type" => return parse_nth_pseudo_class(input, Component::NthLastOfType),
+        "nth-child" => return Ok(parse_nth_pseudo_class(input, Component::NthChild)?),
+        "nth-of-type" => return Ok(parse_nth_pseudo_class(input, Component::NthOfType)?),
+        "nth-last-child" => return Ok(parse_nth_pseudo_class(input, Component::NthLastChild)?),
+        "nth-last-of-type" => return Ok(parse_nth_pseudo_class(input, Component::NthLastOfType)?),
         "not" => {
             if inside_negation {
-                return Err(ParseError::Custom(SelectorParseError::UnexpectedIdent("not".into())));
+                return Err(input.new_custom_error(
+                    SelectorParseErrorKind::UnexpectedIdent("not".into())
+                ));
             }
             return parse_negation(parser, input)
         },
         _ => {}
     }
     P::parse_non_ts_functional_pseudo_class(parser, name, input)
         .map(Component::NonTSPseudoClass)
 }
 
 
-fn parse_nth_pseudo_class<'i, 't, Impl, F, E>(input: &mut CssParser<'i, 't>, selector: F)
-                                              -> Result<Component<Impl>,
-                                                        ParseError<'i, SelectorParseError<'i, E>>>
+fn parse_nth_pseudo_class<'i, 't, Impl, F>(input: &mut CssParser<'i, 't>, selector: F)
+                                           -> Result<Component<Impl>, BasicParseError<'i>>
 where Impl: SelectorImpl, F: FnOnce(i32, i32) -> Component<Impl> {
     let (a, b) = parse_nth(input)?;
     Ok(selector(a, b))
 }
 
 
 /// Returns whether the name corresponds to a CSS2 pseudo-element that
 /// can be specified with the single colon syntax (in addition to the
@@ -1619,100 +1646,106 @@ pub fn is_css2_pseudo_element<'i>(name: 
     }
 }
 
 /// Parse a simple selector other than a type selector.
 ///
 /// * `Err(())`: Invalid selector, abort
 /// * `Ok(None)`: Not a simple selector, could be something else. `input` was not consumed.
 /// * `Ok(Some(_))`: Parsed a simple selector or pseudo-element
-fn parse_one_simple_selector<'i, 't, P, E, Impl>(parser: &P,
-                                                 input: &mut CssParser<'i, 't>,
-                                                 inside_negation: bool)
-                                                 -> Result<Option<SimpleSelectorParseResult<Impl>>,
-                                                           ParseError<'i, SelectorParseError<'i, E>>>
-    where P: Parser<'i, Impl=Impl, Error=E>, Impl: SelectorImpl
+fn parse_one_simple_selector<'i, 't, P, Impl>(parser: &P,
+                                              input: &mut CssParser<'i, 't>,
+                                              inside_negation: bool)
+                                              -> Result<Option<SimpleSelectorParseResult<Impl>>,
+                                                        ParseError<'i, P::Error>>
+    where P: Parser<'i, Impl=Impl>, Impl: SelectorImpl
 {
     let start = input.state();
     // FIXME: remove clone() when lifetimes are non-lexical
     match input.next_including_whitespace().map(|t| t.clone()) {
         Ok(Token::IDHash(id)) => {
             let id = Component::ID(id.as_ref().into());
             Ok(Some(SimpleSelectorParseResult::SimpleSelector(id)))
         }
         Ok(Token::Delim('.')) => {
+            let location = input.current_source_location();
             match *input.next_including_whitespace()? {
                 Token::Ident(ref class) => {
                     let class = Component::Class(class.as_ref().into());
                     Ok(Some(SimpleSelectorParseResult::SimpleSelector(class)))
                 }
-                ref t => Err(SelectorParseError::ClassNeedsIdent(t.clone()).into()),
+                ref t => Err(location.new_custom_error(
+                    SelectorParseErrorKind::ClassNeedsIdent(t.clone())
+                )),
             }
         }
         Ok(Token::SquareBracketBlock) => {
             let attr = input.parse_nested_block(|input| parse_attribute_selector(parser, input))?;
             Ok(Some(SimpleSelectorParseResult::SimpleSelector(attr)))
         }
         Ok(Token::Colon) => {
+            let location = input.current_source_location();
             let (is_single_colon, next_token) = match input.next_including_whitespace()?.clone() {
                 Token::Colon => (false, input.next_including_whitespace()?.clone()),
                 t => (true, t),
             };
             let (name, is_functional) = match next_token {
                 Token::Ident(name) => (name, false),
                 Token::Function(name) => (name, true),
-                t => return Err(SelectorParseError::PseudoElementExpectedIdent(t).into()),
+                t => return Err(input.new_custom_error(
+                    SelectorParseErrorKind::PseudoElementExpectedIdent(t)
+                )),
             };
             let is_pseudo_element = !is_single_colon ||
                 P::is_pseudo_element_allows_single_colon(&name);
             if is_pseudo_element {
                 let pseudo_element = if is_functional {
                     input.parse_nested_block(|input| {
                         P::parse_functional_pseudo_element(parser, name, input)
                     })?
                 } else {
-                    P::parse_pseudo_element(parser, name)?
+                    P::parse_pseudo_element(parser, location, name)?
                 };
                 Ok(Some(SimpleSelectorParseResult::PseudoElement(pseudo_element)))
             } else {
                 let pseudo_class = if is_functional {
                     input.parse_nested_block(|input| {
                         parse_functional_pseudo_class(parser, input, name, inside_negation)
                     })?
                 } else {
-                    parse_simple_pseudo_class(parser, name)?
+                    parse_simple_pseudo_class(parser, location, name)?
                 };
                 Ok(Some(SimpleSelectorParseResult::SimpleSelector(pseudo_class)))
             }
         }
         _ => {
             input.reset(&start);
             Ok(None)
         }
     }
 }
 
-fn parse_simple_pseudo_class<'i, P, E, Impl>(parser: &P, name: CowRcStr<'i>)
-                                             -> Result<Component<Impl>,
-                                                       ParseError<'i, SelectorParseError<'i, E>>>
-    where P: Parser<'i, Impl=Impl, Error=E>, Impl: SelectorImpl
+fn parse_simple_pseudo_class<'i, P, Impl>(parser: &P, location: SourceLocation,
+                                          name: CowRcStr<'i>)
+                                          -> Result<Component<Impl>, ParseError<'i, P::Error>>
+    where P: Parser<'i, Impl=Impl>, Impl: SelectorImpl
 {
     (match_ignore_ascii_case! { &name,
         "first-child" => Ok(Component::FirstChild),
         "last-child"  => Ok(Component::LastChild),
         "only-child"  => Ok(Component::OnlyChild),
         "root" => Ok(Component::Root),
         "empty" => Ok(Component::Empty),
         "scope" => Ok(Component::Scope),
         "first-of-type" => Ok(Component::FirstOfType),
         "last-of-type"  => Ok(Component::LastOfType),
         "only-of-type"  => Ok(Component::OnlyOfType),
         _ => Err(())
     }).or_else(|()| {
-        P::parse_non_ts_pseudo_class(parser, name)
+        P::parse_non_ts_pseudo_class(parser, location, name)
             .map(Component::NonTSPseudoClass)
     })
 }
 
 // NB: pub module in order to access the DummyParser
 #[cfg(test)]
 pub mod tests {
     use builder::HAS_PSEUDO_BIT;
@@ -1838,77 +1871,78 @@ pub mod tests {
     impl PrecomputedHash for DummyAtom {
         fn precomputed_hash(&self) -> u32 {
             return 0
         }
     }
 
     impl<'i> Parser<'i> for DummyParser {
         type Impl = DummySelectorImpl;
-        type Error = ();
+        type Error = SelectorParseErrorKind<'i>;
 
-        fn parse_non_ts_pseudo_class(&self, name: CowRcStr<'i>)
-                                     -> Result<PseudoClass,
-                                               ParseError<'i, SelectorParseError<'i, ()>>> {
+        fn parse_non_ts_pseudo_class(&self, location: SourceLocation, name: CowRcStr<'i>)
+                                     -> Result<PseudoClass, SelectorParseError<'i>> {
             match_ignore_ascii_case! { &name,
-                "hover" => Ok(PseudoClass::Hover),
-                "active" => Ok(PseudoClass::Active),
-                _ => Err(SelectorParseError::Custom(()).into())
+                "hover" => return Ok(PseudoClass::Hover),
+                "active" => return Ok(PseudoClass::Active),
+                _ => {}
             }
+            Err(location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name)))
         }
 
         fn parse_non_ts_functional_pseudo_class<'t>(&self, name: CowRcStr<'i>,
                                                     parser: &mut CssParser<'i, 't>)
-                                                    -> Result<PseudoClass,
-                                                              ParseError<'i, SelectorParseError<'i, ()>>> {
+                                                    -> Result<PseudoClass, SelectorParseError<'i>> {
             match_ignore_ascii_case! { &name,
-                "lang" => Ok(PseudoClass::Lang(parser.expect_ident_or_string()?.as_ref().to_owned())),
-                _ => Err(SelectorParseError::Custom(()).into())
+                "lang" => return Ok(PseudoClass::Lang(parser.expect_ident_or_string()?.as_ref().to_owned())),
+                _ => {}
             }
+            Err(parser.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name)))
         }
 
-        fn parse_pseudo_element(&self, name: CowRcStr<'i>)
+        fn parse_pseudo_element(&self, location: SourceLocation, name: CowRcStr<'i>)
                                 -> Result<PseudoElement,
-                                          ParseError<'i, SelectorParseError<'i, ()>>> {
+                                          SelectorParseError<'i>> {
             match_ignore_ascii_case! { &name,
-                "before" => Ok(PseudoElement::Before),
-                "after" => Ok(PseudoElement::After),
-                _ => Err(SelectorParseError::Custom(()).into())
+                "before" => return Ok(PseudoElement::Before),
+                "after" => return Ok(PseudoElement::After),
+                _ => {}
             }
+            Err(location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name)))
         }
 
         fn default_namespace(&self) -> Option<DummyAtom> {
             self.default_ns.clone()
         }
 
         fn namespace_for_prefix(&self, prefix: &DummyAtom) -> Option<DummyAtom> {
             self.ns_prefixes.get(prefix).cloned()
         }
     }
 
     fn parse<'i>(input: &'i str)
-                 -> Result<SelectorList<DummySelectorImpl>, ParseError<'i, SelectorParseError<'i, ()>>> {
+                 -> Result<SelectorList<DummySelectorImpl>, SelectorParseError<'i>> {
         parse_ns(input, &DummyParser::default())
     }
 
     fn parse_expected<'i, 'a>(input: &'i str, expected: Option<&'a str>)
-                              -> Result<SelectorList<DummySelectorImpl>, ParseError<'i, SelectorParseError<'i, ()>>> {
+                              -> Result<SelectorList<DummySelectorImpl>, SelectorParseError<'i>> {
         parse_ns_expected(input, &DummyParser::default(), expected)
     }
 
     fn parse_ns<'i>(input: &'i str, parser: &DummyParser)
-                    -> Result<SelectorList<DummySelectorImpl>, ParseError<'i, SelectorParseError<'i, ()>>> {
+                    -> Result<SelectorList<DummySelectorImpl>, SelectorParseError<'i>> {
         parse_ns_expected(input, parser, None)
     }
 
     fn parse_ns_expected<'i, 'a>(
         input: &'i str,
         parser: &DummyParser,
         expected: Option<&'a str>
-    ) -> Result<SelectorList<DummySelectorImpl>, ParseError<'i, SelectorParseError<'i, ()>>> {
+    ) -> Result<SelectorList<DummySelectorImpl>, SelectorParseError<'i>> {
         let mut parser_input = ParserInput::new(input);
         let result = SelectorList::parse(parser, &mut CssParser::new(&mut parser_input));
         if let Ok(ref selectors) = result {
             assert_eq!(selectors.0.len(), 1);
             // We can't assume that the serialized parsed selector will equal
             // the input; for example, if there is no default namespace, '*|foo'
             // should serialize to 'foo'.
             assert_eq!(
--- a/servo/components/style/Cargo.toml
+++ b/servo/components/style/Cargo.toml
@@ -31,17 +31,17 @@ gecko_debug = ["nsstring_vendor/gecko_de
 
 [dependencies]
 app_units = "0.5.5"
 arrayvec = "0.3.20"
 atomic_refcell = "0.1"
 bitflags = "0.7"
 byteorder = "1.0"
 cfg-if = "0.1.0"
-cssparser = "0.21.2"
+cssparser = "0.22.0"
 encoding = {version = "0.2", optional = true}
 euclid = "0.15"
 fallible = { path = "../fallible" }
 fnv = "1.0"
 hashglobe = { path = "../hashglobe" }
 heapsize = {version = "0.4", optional = true}
 heapsize_derive = {version = "0.1", optional = true}
 itertools = "0.5"
--- a/servo/components/style/counter_style/mod.rs
+++ b/servo/components/style/counter_style/mod.rs
@@ -3,50 +3,51 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! The [`@counter-style`][counter-style] at-rule.
 //!
 //! [counter-style]: https://drafts.csswg.org/css-counter-styles/
 
 use Atom;
 use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser};
-use cssparser::{Parser, Token, serialize_identifier, BasicParseError, CowRcStr};
+use cssparser::{Parser, Token, serialize_identifier, CowRcStr};
 use error_reporting::{ContextualParseError, ParseErrorReporter};
 #[cfg(feature = "gecko")] use gecko::rules::CounterStyleDescriptors;
 #[cfg(feature = "gecko")] use gecko_bindings::structs::nsCSSCounterDesc;
 use parser::{ParserContext, ParserErrorContext, Parse};
-use selectors::parser::SelectorParseError;
+use selectors::parser::SelectorParseErrorKind;
 use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
 use std::ascii::AsciiExt;
 use std::borrow::Cow;
 use std::fmt;
 use std::ops::Range;
-use style_traits::{Comma, OneOrMoreSeparated, ParseError, StyleParseError, ToCss};
+use style_traits::{Comma, OneOrMoreSeparated, ParseError, StyleParseErrorKind, ToCss};
 use values::CustomIdent;
 
 /// Parse the prelude of an @counter-style rule
 pub fn parse_counter_style_name<'i, 't>(input: &mut Parser<'i, 't>) -> Result<CustomIdent, ParseError<'i>> {
     macro_rules! predefined {
         ($($name: expr,)+) => {
             {
                 ascii_case_insensitive_phf_map! {
                     // FIXME: use static atoms https://github.com/rust-lang/rust/issues/33156
                     predefined -> &'static str = {
                         $(
                             $name => $name,
                         )+
                     }
                 }
 
+                let location = input.current_source_location();
                 let ident = input.expect_ident()?;
                 if let Some(&lower_cased) = predefined(&ident) {
                     Ok(CustomIdent(Atom::from(lower_cased)))
                 } else {
                     // https://github.com/w3c/csswg-drafts/issues/1295 excludes "none"
-                    CustomIdent::from_ident(ident, &["none"])
+                    CustomIdent::from_ident(location, ident, &["none"])
                 }
             }
         }
     }
     include!("predefined.rs")
 }
 
 /// Parse the body (inside `{}`) of an @counter-style rule
@@ -61,19 +62,20 @@ pub fn parse_counter_style_body<'i, 't, 
     let mut rule = CounterStyleRuleData::empty(name);
     {
         let parser = CounterStyleRuleParser {
             context: context,
             rule: &mut rule,
         };
         let mut iter = DeclarationListParser::new(input, parser);
         while let Some(declaration) = iter.next() {
-            if let Err(err) = declaration {
-                let error = ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(err.slice, err.error);
-                context.log_css_error(error_context, err.location, error)
+            if let Err((error, slice)) = declaration {
+                let location = error.location;
+                let error = ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(slice, error);
+                context.log_css_error(error_context, location, error)
             }
         }
     }
     let error = match *rule.system() {
         ref system @ System::Cyclic |
         ref system @ System::Fixed { .. } |
         ref system @ System::Symbolic |
         ref system @ System::Alphabetic |
@@ -96,33 +98,33 @@ pub fn parse_counter_style_body<'i, 't, 
         }
         System::Extends(_) if rule.additive_symbols.is_some() => {
             Some(ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols)
         }
         _ => None
     };
     if let Some(error) = error {
         context.log_css_error(error_context, start, error);
-        Err(StyleParseError::UnspecifiedError.into())
+        Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     } else {
         Ok(rule)
     }
 }
 
 struct CounterStyleRuleParser<'a, 'b: 'a> {
     context: &'a ParserContext<'b>,
     rule: &'a mut CounterStyleRuleData,
 }
 
 /// Default methods reject all at rules.
 impl<'a, 'b, 'i> AtRuleParser<'i> for CounterStyleRuleParser<'a, 'b> {
     type PreludeNoBlock = ();
     type PreludeBlock = ();
     type AtRule = ();
-    type Error = SelectorParseError<'i, StyleParseError<'i>>;
+    type Error = StyleParseErrorKind<'i>;
 }
 
 macro_rules! accessor {
     (#[$doc: meta] $name: tt $ident: ident: $ty: ty = !) => {
         #[$doc]
         pub fn $ident(&self) -> Option<&$ty> {
             self.$ident.as_ref()
         }
@@ -181,32 +183,32 @@ macro_rules! counter_style_descriptors {
                         descriptors[nsCSSCounterDesc::$gecko_ident as usize].set_from(value)
                     }
                 )*
             }
         }
 
         impl<'a, 'b, 'i> DeclarationParser<'i> for CounterStyleRuleParser<'a, 'b> {
             type Declaration = ();
-            type Error = SelectorParseError<'i, StyleParseError<'i>>;
+            type Error = StyleParseErrorKind<'i>;
 
             fn parse_value<'t>(&mut self, name: CowRcStr<'i>, input: &mut Parser<'i, 't>)
                                -> Result<(), ParseError<'i>> {
                 match_ignore_ascii_case! { &*name,
                     $(
                         $name => {
                             // DeclarationParser also calls parse_entirely
                             // so we’d normally not need to,
                             // but in this case we do because we set the value as a side effect
                             // rather than returning it.
                             let value = input.parse_entirely(|i| Parse::parse(self.context, i))?;
                             self.rule.$ident = Some(value)
                         }
                     )*
-                    _ => return Err(SelectorParseError::UnexpectedIdent(name.clone()).into())
+                    _ => return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
                 }
                 Ok(())
             }
         }
 
         impl ToCssWithGuard for CounterStyleRuleData {
             fn to_css<W>(&self, _guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
             where W: fmt::Write {
@@ -294,17 +296,17 @@ pub enum System {
         first_symbol_value: Option<i32>
     },
     /// 'extends <counter-style-name>'
     Extends(CustomIdent),
 }
 
 impl Parse for System {
     fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
-        try_match_ident_ignore_ascii_case! { input.expect_ident_cloned()?,
+        try_match_ident_ignore_ascii_case! { input,
             "cyclic" => Ok(System::Cyclic),
             "numeric" => Ok(System::Numeric),
             "alphabetic" => Ok(System::Alphabetic),
             "symbolic" => Ok(System::Symbolic),
             "additive" => Ok(System::Additive),
             "fixed" => {
                 let first_symbol_value = input.try(|i| i.expect_integer()).ok();
                 Ok(System::Fixed { first_symbol_value: first_symbol_value })
@@ -351,20 +353,21 @@ pub enum Symbol {
     Ident(String),
     // Not implemented:
     // /// <image>
     // Image(Image),
 }
 
 impl Parse for Symbol {
     fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
+        let location = input.current_source_location();
         match input.next() {
             Ok(&Token::QuotedString(ref s)) => Ok(Symbol::String(s.as_ref().to_owned())),
             Ok(&Token::Ident(ref s)) => Ok(Symbol::Ident(s.as_ref().to_owned())),
-            Ok(t) => Err(BasicParseError::UnexpectedToken(t.clone()).into()),
+            Ok(t) => Err(location.new_unexpected_token_error(t.clone())),
             Err(e) => Err(e.into()),
         }
     }
 }
 
 impl ToCss for Symbol {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         match *self {
@@ -409,30 +412,31 @@ impl Parse for Ranges {
         if input.try(|input| input.expect_ident_matching("auto")).is_ok() {
             Ok(Ranges(Vec::new()))
         } else {
             input.parse_comma_separated(|input| {
                 let opt_start = parse_bound(input)?;
                 let opt_end = parse_bound(input)?;
                 if let (Some(start), Some(end)) = (opt_start, opt_end) {
                     if start > end {
-                        return Err(StyleParseError::UnspecifiedError.into())
+                        return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
                     }
                 }
                 Ok(opt_start..opt_end)
             }).map(Ranges)
         }
     }
 }
 
 fn parse_bound<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Option<i32>, ParseError<'i>> {
+    let location = input.current_source_location();
     match input.next() {
         Ok(&Token::Number { int_value: Some(v), .. }) => Ok(Some(v)),
         Ok(&Token::Ident(ref ident)) if ident.eq_ignore_ascii_case("infinite") => Ok(None),
-        Ok(t) => Err(BasicParseError::UnexpectedToken(t.clone()).into()),
+        Ok(t) => Err(location.new_unexpected_token_error(t.clone())),
         Err(e) => Err(e.into()),
     }
 }
 
 impl ToCss for Ranges {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         let mut iter = self.0.iter();
         if let Some(first) = iter.next() {
@@ -467,17 +471,17 @@ fn bound_to_css<W>(range: Option<i32>, d
 #[derive(Clone, Debug, ToCss)]
 pub struct Pad(pub u32, pub Symbol);
 
 impl Parse for Pad {
     fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
         let pad_with = input.try(|input| Symbol::parse(context, input));
         let min_length = input.expect_integer()?;
         if min_length < 0 {
-            return Err(StyleParseError::UnspecifiedError.into())
+            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         }
         let pad_with = pad_with.or_else(|_| Symbol::parse(context, input))?;
         Ok(Pad(min_length as u32, pad_with))
     }
 }
 
 /// https://drafts.csswg.org/css-counter-styles/#counter-style-fallback
 #[derive(Clone, Debug, ToCss)]
@@ -497,17 +501,17 @@ pub struct Symbols(pub Vec<Symbol>);
 impl Parse for Symbols {
     fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
         let mut symbols = Vec::new();
         loop {
             if let Ok(s) = input.try(|input| Symbol::parse(context, input)) {
                 symbols.push(s)
             } else {
                 if symbols.is_empty() {
-                    return Err(StyleParseError::UnspecifiedError.into())
+                    return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
                 } else {
                     return Ok(Symbols(symbols))
                 }
             }
         }
     }
 }
 
@@ -528,17 +532,17 @@ impl ToCss for Symbols {
 #[derive(Clone, Debug, ToCss)]
 pub struct AdditiveSymbols(pub Vec<AdditiveTuple>);
 
 impl Parse for AdditiveSymbols {
     fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
         let tuples = Vec::<AdditiveTuple>::parse(context, input)?;
         // FIXME maybe? https://github.com/w3c/csswg-drafts/issues/1220
         if tuples.windows(2).any(|window| window[0].weight <= window[1].weight) {
-            return Err(StyleParseError::UnspecifiedError.into())
+            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         }
         Ok(AdditiveSymbols(tuples))
     }
 }
 
 /// <integer> && <symbol>
 #[derive(Clone, Debug, ToCss)]
 pub struct AdditiveTuple {
@@ -552,17 +556,17 @@ impl OneOrMoreSeparated for AdditiveTupl
     type S = Comma;
 }
 
 impl Parse for AdditiveTuple {
     fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
         let symbol = input.try(|input| Symbol::parse(context, input));
         let weight = input.expect_integer()?;
         if weight < 0 {
-            return Err(StyleParseError::UnspecifiedError.into())
+            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         }
         let symbol = symbol.or_else(|_| Symbol::parse(context, input))?;
         Ok(AdditiveTuple {
             weight: weight as u32,
             symbol: symbol,
         })
     }
 }
@@ -599,15 +603,15 @@ impl Parse for SpeakAs {
                     Err(())
                 }
                 _ => Err(()),
             }
         });
         if is_spell_out {
             // spell-out is not supported, but don’t parse it as a <counter-style-name>.
             // See bug 1024178.
-            return Err(StyleParseError::UnspecifiedError.into())
+            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         }
         result.or_else(|_| {
             Ok(SpeakAs::Other(parse_counter_style_name(input)?))
         })
     }
 }
--- a/servo/components/style/custom_properties.rs
+++ b/servo/components/style/custom_properties.rs
@@ -6,24 +6,24 @@
 //!
 //! [custom]: https://drafts.csswg.org/css-variables/
 
 use Atom;
 use cssparser::{Delimiter, Parser, ParserInput, SourcePosition, Token, TokenSerializationType};
 use precomputed_hash::PrecomputedHash;
 use properties::{CSSWideKeyword, DeclaredValue};
 use selector_map::{PrecomputedHashSet, PrecomputedHashMap, PrecomputedDiagnosticHashMap};
-use selectors::parser::SelectorParseError;
+use selectors::parser::SelectorParseErrorKind;
 use servo_arc::Arc;
 use smallvec::SmallVec;
 use std::ascii::AsciiExt;
 use std::borrow::{Borrow, Cow};
 use std::fmt;
 use std::hash::Hash;
-use style_traits::{ToCss, StyleParseError, ParseError};
+use style_traits::{ToCss, StyleParseErrorKind, ParseError};
 
 /// A custom property name is just an `Atom`.
 ///
 /// Note that this does not include the `--` prefix
 pub type Name = Atom;
 
 /// Parse a custom property name.
 ///
@@ -352,26 +352,37 @@ fn parse_declaration_value_block<'i, 't>
             Token::Comment(_) => {
                 let token_slice = input.slice_from(token_start);
                 if !token_slice.ends_with("*/") {
                     missing_closing_characters.push_str(
                         if token_slice.ends_with('*') { "/" } else { "*/" })
                 }
                 token.serialization_type()
             }
-            Token::BadUrl(u) =>
-                return Err(StyleParseError::BadUrlInDeclarationValueBlock(u).into()),
-            Token::BadString(s) =>
-                return Err(StyleParseError::BadStringInDeclarationValueBlock(s).into()),
-            Token::CloseParenthesis =>
-                return Err(StyleParseError::UnbalancedCloseParenthesisInDeclarationValueBlock.into()),
-            Token::CloseSquareBracket =>
-                return Err(StyleParseError::UnbalancedCloseSquareBracketInDeclarationValueBlock.into()),
-            Token::CloseCurlyBracket =>
-                return Err(StyleParseError::UnbalancedCloseCurlyBracketInDeclarationValueBlock.into()),
+            Token::BadUrl(u) => {
+                return Err(input.new_custom_error(StyleParseErrorKind::BadUrlInDeclarationValueBlock(u)))
+            }
+            Token::BadString(s) => {
+                return Err(input.new_custom_error(StyleParseErrorKind::BadStringInDeclarationValueBlock(s)))
+            }
+            Token::CloseParenthesis => {
+                return Err(input.new_custom_error(
+                    StyleParseErrorKind::UnbalancedCloseParenthesisInDeclarationValueBlock
+                ))
+            }
+            Token::CloseSquareBracket => {
+                return Err(input.new_custom_error(
+                    StyleParseErrorKind::UnbalancedCloseSquareBracketInDeclarationValueBlock
+                ))
+            }
+            Token::CloseCurlyBracket => {
+                return Err(input.new_custom_error(
+                    StyleParseErrorKind::UnbalancedCloseCurlyBracketInDeclarationValueBlock
+                ))
+            }
             Token::Function(ref name) => {
                 if name.eq_ignore_ascii_case("var") {
                     let args_start = input.state();
                     input.parse_nested_block(|input| {
                         parse_var_function(
                             input,
                             references.as_mut().map(|r| &mut **r),
                         )
@@ -441,17 +452,17 @@ fn parse_declaration_value_block<'i, 't>
 // If the var function is valid, return Ok((custom_property_name, fallback))
 fn parse_var_function<'i, 't>(
     input: &mut Parser<'i, 't>,
     references: Option<&mut PrecomputedHashSet<Name>>
 ) -> Result<(), ParseError<'i>> {
     let name = input.expect_ident_cloned()?;
     let name: Result<_, ParseError> =
         parse_name(&name)
-        .map_err(|()| SelectorParseError::UnexpectedIdent(name.clone()).into());
+        .map_err(|()| input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())));
     let name = name?;
     if input.try(|input| input.expect_comma()).is_ok() {
         // Exclude `!` and `;` at the top level
         // https://drafts.csswg.org/css-syntax/#typedef-declaration-value
         input.parse_until_before(Delimiter::Bang | Delimiter::Semicolon, |input| {
             // At least one non-comment token.
             input.next_including_whitespace()?;
             // Skip until the end.
--- a/servo/components/style/error_reporting.rs
+++ b/servo/components/style/error_reporting.rs
@@ -1,18 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Types used to report parsing errors.
 
 #![deny(missing_docs)]
 
-use cssparser::{BasicParseError, Token, SourceLocation};
-use cssparser::ParseError as CssParseError;
+use cssparser::{Token, SourceLocation, ParseErrorKind, BasicParseErrorKind};
 use log;
 use std::fmt;
 use style_traits::ParseError;
 use stylesheets::UrlExtraData;
 
 /// Errors that can be encountered while parsing CSS.
 pub enum ContextualParseError<'a> {
     /// A property declaration was not recognized.
@@ -86,34 +85,34 @@ impl<'a> fmt::Display for ContextualPars
                 Token::BadString(ref _s) => write!(f, "bad string parse error"),
                 Token::CloseParenthesis => write!(f, "unmatched close parenthesis"),
                 Token::CloseSquareBracket => write!(f, "unmatched close square bracket"),
                 Token::CloseCurlyBracket => write!(f, "unmatched close curly bracket"),
             }
         }
 
         fn parse_error_to_str(err: &ParseError, f: &mut fmt::Formatter) -> fmt::Result {
-            match *err {
-                CssParseError::Basic(BasicParseError::UnexpectedToken(ref t)) => {
+            match err.kind {
+                ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(ref t)) => {
                     write!(f, "found unexpected ")?;
                     token_to_str(t, f)
                 }
-                CssParseError::Basic(BasicParseError::EndOfInput) => {
+                ParseErrorKind::Basic(BasicParseErrorKind::EndOfInput) => {
                     write!(f, "unexpected end of input")
                 }
-                CssParseError::Basic(BasicParseError::AtRuleInvalid(ref i)) => {
+                ParseErrorKind::Basic(BasicParseErrorKind::AtRuleInvalid(ref i)) => {
                     write!(f, "@ rule invalid: {}", i)
                 }
-                CssParseError::Basic(BasicParseError::AtRuleBodyInvalid) => {
+                ParseErrorKind::Basic(BasicParseErrorKind::AtRuleBodyInvalid) => {
                     write!(f, "@ rule invalid")
                 }
-                CssParseError::Basic(BasicParseError::QualifiedRuleInvalid) => {
+                ParseErrorKind::Basic(BasicParseErrorKind::QualifiedRuleInvalid) => {
                     write!(f, "qualified rule invalid")
                 }
-                CssParseError::Custom(ref err) => {
+                ParseErrorKind::Custom(ref err) => {
                     write!(f, "{:?}", err)
                 }
             }
         }
 
         match *self {
             ContextualParseError::UnsupportedPropertyDeclaration(decl, ref err) => {
                 write!(f, "Unsupported property declaration: '{}', ", decl)?;
--- a/servo/components/style/font_face.rs
+++ b/servo/components/style/font_face.rs
@@ -14,20 +14,20 @@ use computed_values::font_family::Family
 use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser};
 use cssparser::{SourceLocation, CowRcStr};
 use error_reporting::{ContextualParseError, ParseErrorReporter};
 #[cfg(feature = "gecko")] use gecko_bindings::structs::CSSFontFaceDescriptors;
 #[cfg(feature = "gecko")] use cssparser::UnicodeRange;
 use parser::{ParserContext, ParserErrorContext, Parse};
 #[cfg(feature = "gecko")]
 use properties::longhands::font_language_override;
-use selectors::parser::SelectorParseError;
+use selectors::parser::SelectorParseErrorKind;
 use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
 use std::fmt;
-use style_traits::{Comma, OneOrMoreSeparated, ParseError, StyleParseError, ToCss};
+use style_traits::{Comma, OneOrMoreSeparated, ParseError, StyleParseErrorKind, ToCss};
 use values::specified::url::SpecifiedUrl;
 
 /// A source for a font-face rule.
 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
 #[derive(Clone, Debug, Eq, PartialEq, ToCss)]
 pub enum Source {
     /// A `url()` source.
     Url(UrlSource),
@@ -95,17 +95,17 @@ impl Parse for FontWeight {
                 "normal" => Ok(FontWeight::Normal),
                 "bold" => Ok(FontWeight::Bold),
                 _ => Err(())
             }
         });
         result.or_else(|_| {
             font_weight::T::from_int(input.expect_integer()?)
                 .map(FontWeight::Weight)
-                .map_err(|()| StyleParseError::UnspecifiedError.into())
+                .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         })
     }
 }
 
 /// Parse the block inside a `@font-face` rule.
 ///
 /// Note that the prelude parsing code lives in the `stylesheets` module.
 pub fn parse_font_face_block<R>(context: &ParserContext,
@@ -118,19 +118,20 @@ pub fn parse_font_face_block<R>(context:
     let mut rule = FontFaceRuleData::empty(location);
     {
         let parser = FontFaceRuleParser {
             context: context,
             rule: &mut rule,
         };
         let mut iter = DeclarationListParser::new(input, parser);
         while let Some(declaration) = iter.next() {
-            if let Err(err) = declaration {
-                let error = ContextualParseError::UnsupportedFontFaceDescriptor(err.slice, err.error);
-                context.log_css_error(error_context, err.location, error)
+            if let Err((error, slice)) = declaration {
+                let location = error.location;
+                let error = ContextualParseError::UnsupportedFontFaceDescriptor(slice, error);
+                context.log_css_error(error_context, location, error)
             }
         }
     }
     rule
 }
 
 /// A @font-face rule that is known to have font-family and src declarations.
 #[cfg(feature = "servo")]
@@ -181,17 +182,17 @@ struct FontFaceRuleParser<'a, 'b: 'a> {
     rule: &'a mut FontFaceRuleData,
 }
 
 /// Default methods reject all at rules.
 impl<'a, 'b, 'i> AtRuleParser<'i> for FontFaceRuleParser<'a, 'b> {
     type PreludeNoBlock = ();
     type PreludeBlock = ();
     type AtRule = ();
-    type Error = SelectorParseError<'i, StyleParseError<'i>>;
+    type Error = StyleParseErrorKind<'i>;
 }
 
 impl Parse for Source {
     fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
                      -> Result<Source, ParseError<'i>> {
         if input.try(|input| input.expect_function_matching("local")).is_ok() {
             return input.parse_nested_block(|input| {
                 FamilyName::parse(context, input)
@@ -282,32 +283,32 @@ macro_rules! font_face_descriptors_commo
                     }
                 )*
                 dest.write_str("}")
             }
         }
 
        impl<'a, 'b, 'i> DeclarationParser<'i> for FontFaceRuleParser<'a, 'b> {
            type Declaration = ();
-           type Error = SelectorParseError<'i, StyleParseError<'i>>;
+           type Error = StyleParseErrorKind<'i>;
 
            fn parse_value<'t>(&mut self, name: CowRcStr<'i>, input: &mut Parser<'i, 't>)
                               -> Result<(), ParseError<'i>> {
                 match_ignore_ascii_case! { &*name,
                     $(
                         $name if is_descriptor_enabled!($name) => {
                             // DeclarationParser also calls parse_entirely
                             // so we’d normally not need to,
                             // but in this case we do because we set the value as a side effect
                             // rather than returning it.
                             let value = input.parse_entirely(|i| Parse::parse(self.context, i))?;
                             self.rule.$ident = Some(value)
                         }
                     )*
-                    _ => return Err(SelectorParseError::UnexpectedIdent(name.clone()).into())
+                    _ => return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
                 }
                 Ok(())
             }
         }
     }
 }
 
 macro_rules! font_face_descriptors {
--- a/servo/components/style/gecko/media_queries.rs
+++ b/servo/components/style/gecko/media_queries.rs
@@ -2,17 +2,17 @@
  * 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/. */
 
 //! Gecko's media-query device and expression representation.
 
 use app_units::AU_PER_PX;
 use app_units::Au;
 use context::QuirksMode;
-use cssparser::{CssStringWriter, Parser, RGBA, Token, BasicParseError};
+use cssparser::{CssStringWriter, Parser, RGBA, Token, BasicParseErrorKind};
 use euclid::ScaleFactor;
 use euclid::Size2D;
 use font_metrics::get_metrics_provider_for_product;
 use gecko::values::{convert_nscolor_to_rgba, convert_rgba_to_nscolor};
 use gecko_bindings::bindings;
 use gecko_bindings::structs;
 use gecko_bindings::structs::{nsCSSKeyword, nsCSSProps_KTableEntry, nsCSSValue, nsCSSUnit};
 use gecko_bindings::structs::{nsMediaExpression_Range, nsMediaFeature};
@@ -25,17 +25,17 @@ use properties::longhands::font_size;
 use rule_cache::RuleCacheConditions;
 use servo_arc::Arc;
 use std::cell::RefCell;
 use std::fmt::{self, Write};
 use std::sync::atomic::{AtomicBool, AtomicIsize, AtomicUsize, Ordering};
 use str::starts_with_ignore_ascii_case;
 use string_cache::Atom;
 use style_traits::{CSSPixel, DevicePixel};
-use style_traits::{ToCss, ParseError, StyleParseError};
+use style_traits::{ToCss, ParseError, StyleParseErrorKind};
 use style_traits::viewport::ViewportConstraints;
 use stylesheets::Origin;
 use values::{CSSFloat, CustomIdent, serialize_dimension};
 use values::computed::{self, ToComputedValue};
 use values::specified::Length;
 
 /// The `Device` in Gecko wraps a pres context, has a default values computed,
 /// and contains all the viewport rule state.
@@ -280,33 +280,34 @@ impl Resolution {
         match *self {
             Resolution::Dpi(f) => f,
             Resolution::Dppx(f) => f * 96.0,
             Resolution::Dpcm(f) => f * 2.54,
         }
     }
 
     fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
+        let location = input.current_source_location();
         let (value, unit) = match *input.next()? {
             Token::Dimension { value, ref unit, .. } => {
                 (value, unit)
             },
-            ref t => return Err(BasicParseError::UnexpectedToken(t.clone()).into()),
+            ref t => return Err(location.new_unexpected_token_error(t.clone())),
         };
 
         if value <= 0. {
-            return Err(StyleParseError::UnspecifiedError.into())
+            return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         }
 
         (match_ignore_ascii_case! { &unit,
             "dpi" => Ok(Resolution::Dpi(value)),
             "dppx" => Ok(Resolution::Dppx(value)),
             "dpcm" => Ok(Resolution::Dpcm(value)),
             _ => Err(())
-        }).map_err(|()| StyleParseError::UnexpectedDimension(unit.clone()).into())
+        }).map_err(|()| location.new_custom_error(StyleParseErrorKind::UnexpectedDimension(unit.clone())))
     }
 }
 
 impl ToCss for Resolution {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result
         where W: fmt::Write,
     {
         match *self {
@@ -493,80 +494,78 @@ fn parse_feature_value<'i, 't>(feature: 
     let value = match feature_value_type {
         nsMediaFeature_ValueType::eLength => {
            let length = Length::parse_non_negative(context, input)?;
            // FIXME(canaltinova): See bug 1396057. Gecko doesn't support calc
            // inside media queries. This check is for temporarily remove it
            // for parity with gecko. We should remove this check when we want
            // to support it.
            if let Length::Calc(_) = length {
-               return Err(StyleParseError::UnspecifiedError.into())
+               return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
            }
            MediaExpressionValue::Length(length)
         },
         nsMediaFeature_ValueType::eInteger => {
            // FIXME(emilio): We should use `Integer::parse` to handle `calc`
            // properly in integer expressions. Note that calc is still not
            // supported in media queries per FIXME above.
            let i = input.expect_integer()?;
            if i < 0 {
-               return Err(StyleParseError::UnspecifiedError.into())
+               return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
            }
            MediaExpressionValue::Integer(i as u32)
         }
         nsMediaFeature_ValueType::eBoolInteger => {
            let i = input.expect_integer()?;
            if i < 0 || i > 1 {
-               return Err(StyleParseError::UnspecifiedError.into())
+               return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
            }
            MediaExpressionValue::BoolInteger(i == 1)
         }
         nsMediaFeature_ValueType::eFloat => {
            MediaExpressionValue::Float(input.expect_number()?)
         }
         nsMediaFeature_ValueType::eIntRatio => {
            let a = input.expect_integer()?;
            if a <= 0 {
-               return Err(StyleParseError::UnspecifiedError.into())
+               return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
            }
 
            input.expect_delim('/')?;
 
            let b = input.expect_integer()?;
            if b <= 0 {
-               return Err(StyleParseError::UnspecifiedError.into())
+               return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
            }
            MediaExpressionValue::IntRatio(a as u32, b as u32)
         }
         nsMediaFeature_ValueType::eResolution => {
            MediaExpressionValue::Resolution(Resolution::parse(input)?)
         }
         nsMediaFeature_ValueType::eEnumerated => {
-           let keyword = input.expect_ident()?;
-           let keyword = unsafe {
-               bindings::Gecko_LookupCSSKeyword(keyword.as_bytes().as_ptr(),
-               keyword.len() as u32)
-           };
-
-           let first_table_entry: *const nsCSSProps_KTableEntry = unsafe {
-               *feature.mData.mKeywordTable.as_ref()
-           };
+            let location = input.current_source_location();
+            let keyword = input.expect_ident()?;
+            let keyword = unsafe {
+                bindings::Gecko_LookupCSSKeyword(keyword.as_bytes().as_ptr(),
+                keyword.len() as u32)
+            };
 
-           let value =
-               match unsafe { find_in_table(first_table_entry, |kw, _| kw == keyword) } {
-                   Some((_kw, value)) => {
-                       value
-                   }
-                   None => return Err(StyleParseError::UnspecifiedError.into()),
-               };
+            let first_table_entry: *const nsCSSProps_KTableEntry = unsafe {
+                *feature.mData.mKeywordTable.as_ref()
+            };
 
-           MediaExpressionValue::Enumerated(value)
+            let value = match unsafe { find_in_table(first_table_entry, |kw, _| kw == keyword) } {
+                Some((_kw, value)) => value,
+                None => return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
+            };
+
+            MediaExpressionValue::Enumerated(value)
         }
         nsMediaFeature_ValueType::eIdent => {
-           MediaExpressionValue::Ident(input.expect_ident()?.as_ref().to_owned())
+            MediaExpressionValue::Ident(input.expect_ident()?.as_ref().to_owned())
         }
     };
 
     Ok(value)
 }
 
 impl Expression {
     /// Trivially construct a new expression.
@@ -585,32 +584,33 @@ impl Expression {
     /// ```
     /// (media-feature: media-value)
     /// ```
     pub fn parse<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
         input.expect_parenthesis_block().map_err(|err|
-            match err {
-                BasicParseError::UnexpectedToken(t) => StyleParseError::ExpectedIdentifier(t),
-                _ => StyleParseError::UnspecifiedError,
-            }
+            err.location.new_custom_error(match err.kind {
+                BasicParseErrorKind::UnexpectedToken(t) => StyleParseErrorKind::ExpectedIdentifier(t),
+                _ => StyleParseErrorKind::UnspecifiedError,
+            })
         )?;
 
         input.parse_nested_block(|input| {
             // FIXME: remove extra indented block when lifetimes are non-lexical
             let feature;
             let range;
             {
+                let location = input.current_source_location();
                 let ident = input.expect_ident().map_err(|err|
-                    match err {
-                        BasicParseError::UnexpectedToken(t) => StyleParseError::ExpectedIdentifier(t),
-                        _ => StyleParseError::UnspecifiedError,
-                    }
+                    err.location.new_custom_error(match err.kind {
+                        BasicParseErrorKind::UnexpectedToken(t) => StyleParseErrorKind::ExpectedIdentifier(t),
+                        _ => StyleParseErrorKind::UnspecifiedError,
+                    })
                 )?;
 
                 let mut flags = 0;
 
                 if context.in_chrome_stylesheet() ||
                     context.stylesheet_origin == Origin::UserAgent {
                     flags |= nsMediaFeature_RequirementFlags::eUserAgentAndChromeOnly as u8;
                 }
@@ -643,48 +643,54 @@ impl Expression {
                         None => Err(()),
                     }
                 };
 
                 match result {
                     Ok((f, r)) => {
                         feature = f;
                         range = r;
-                    },
+                    }
                     Err(()) => {
-                        return Err(StyleParseError::MediaQueryExpectedFeatureName(ident.clone()).into())
-                    },
+                        return Err(location.new_custom_error(
+                            StyleParseErrorKind::MediaQueryExpectedFeatureName(ident.clone())
+                        ))
+                    }
                 }
 
                 if (feature.mReqFlags & !flags) != 0 {
-                    return Err(StyleParseError::MediaQueryExpectedFeatureName(ident.clone()).into());
+                    return Err(location.new_custom_error(
+                        StyleParseErrorKind::MediaQueryExpectedFeatureName(ident.clone())
+                    ))
                 }
 
                 if range != nsMediaExpression_Range::eEqual &&
                    feature.mRangeType != nsMediaFeature_RangeType::eMinMaxAllowed {
-                    return Err(StyleParseError::MediaQueryExpectedFeatureName(ident.clone()).into());
+                    return Err(location.new_custom_error(
+                        StyleParseErrorKind::MediaQueryExpectedFeatureName(ident.clone())
+                    ))
                 }
             }
 
             // If there's no colon, this is a media query of the form
             // '(<feature>)', that is, there's no value specified.
             //
             // Gecko doesn't allow ranged expressions without a value, so just
             // reject them here too.
             if input.try(|i| i.expect_colon()).is_err() {
                 if range != nsMediaExpression_Range::eEqual {
-                    return Err(StyleParseError::RangedExpressionWithNoValue.into())
+                    return Err(input.new_custom_error(StyleParseErrorKind::RangedExpressionWithNoValue))
                 }
                 return Ok(Expression::new(feature, None, range));
             }
 
             let value = parse_feature_value(feature,
                                             feature.mValueType,
-                                            context, input).map_err(|_|
-                StyleParseError::MediaQueryExpectedFeatureValue
+                                            context, input).map_err(|err|
+                err.location.new_custom_error(StyleParseErrorKind::MediaQueryExpectedFeatureValue)
             )?;
 
             Ok(Expression::new(feature, Some(value), range))
         })
     }
 
     /// Returns whether this media query evaluates to true for the given device.
     pub fn matches(&self, device: &Device, quirks_mode: QuirksMode) -> bool {
--- a/servo/components/style/gecko/selector_parser.rs
+++ b/servo/components/style/gecko/selector_parser.rs
@@ -1,26 +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/. */
 
 //! Gecko-specific bits for selector-parsing.
 
-use cssparser::{BasicParseError, Parser, ToCss, Token, CowRcStr};
+use cssparser::{BasicParseError, BasicParseErrorKind, Parser, ToCss, Token, CowRcStr, SourceLocation};
 use element_state::ElementState;
 use gecko_bindings::structs::CSSPseudoClassType;
 use gecko_bindings::structs::RawServoSelectorList;
 use gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI};
 use selector_parser::{SelectorParser, PseudoElementCascadeType};
 use selectors::SelectorList;
-use selectors::parser::{Selector, SelectorMethods, SelectorParseError};
+use selectors::parser::{Selector, SelectorMethods, SelectorParseErrorKind};
 use selectors::visitor::SelectorVisitor;
 use std::fmt;
 use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
-use style_traits::{ParseError, StyleParseError};
+use style_traits::{ParseError, StyleParseErrorKind};
 
 pub use gecko::pseudo_element::{PseudoElement, EAGER_PSEUDOS, EAGER_PSEUDO_COUNT, SIMPLE_PSEUDO_COUNT};
 pub use gecko::snapshot::SnapshotMap;
 
 bitflags! {
     // See NonTSPseudoClass::is_enabled_in()
     flags NonTSPseudoClassFlag: u8 {
         const PSEUDO_CLASS_ENABLED_IN_UA_SHEETS = 1 << 0,
@@ -293,41 +293,42 @@ impl<'a> SelectorParser<'a> {
              pseudo_class.has_any_flag(PSEUDO_CLASS_ENABLED_IN_UA_SHEETS)) ||
             (self.in_chrome_stylesheet() &&
              pseudo_class.has_any_flag(PSEUDO_CLASS_ENABLED_IN_CHROME))
     }
 }
 
 impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
     type Impl = SelectorImpl;
-    type Error = StyleParseError<'i>;
+    type Error = StyleParseErrorKind<'i>;
 
     fn is_pseudo_element_allows_single_colon(name: &CowRcStr<'i>) -> bool {
         ::selectors::parser::is_css2_pseudo_element(name) ||
             name.starts_with("-moz-tree-") // tree pseudo-elements
     }
 
-    fn parse_non_ts_pseudo_class(&self, name: CowRcStr<'i>)
+    fn parse_non_ts_pseudo_class(&self, location: SourceLocation, name: CowRcStr<'i>)
                                  -> Result<NonTSPseudoClass, ParseError<'i>> {
         macro_rules! pseudo_class_parse {
             (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
              string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*],
              keyword: [$(($k_css:expr, $k_name:ident, $k_gecko_type:tt, $k_state:tt, $k_flags:tt),)*]) => {
                 match_ignore_ascii_case! { &name,
                     $($css => NonTSPseudoClass::$name,)*
-                    _ => return Err(::selectors::parser::SelectorParseError::UnsupportedPseudoClassOrElement(
-                        name.clone()).into())
+                    _ => return Err(location.new_custom_error(
+                        SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name.clone())
+                    ))
                 }
             }
         }
         let pseudo_class = apply_non_ts_list!(pseudo_class_parse);
         if self.is_pseudo_class_enabled(&pseudo_class) {
             Ok(pseudo_class)
         } else {
-            Err(SelectorParseError::UnsupportedPseudoClassOrElement(name).into())
+            Err(location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name)))
         }
     }
 
     fn parse_non_ts_functional_pseudo_class<'t>(&self,
                                                 name: CowRcStr<'i>,
                                                 parser: &mut Parser<'i, 't>)
                                                 -> Result<NonTSPseudoClass, ParseError<'i>> {
         macro_rules! pseudo_class_string_parse {
@@ -350,59 +351,65 @@ impl<'a, 'i> ::selectors::Parser<'i> for
                         NonTSPseudoClass::$k_name(utf16.into_boxed_slice())
                     }, )*
                     "-moz-any" => {
                         let selectors = parser.parse_comma_separated(|input| {
                             Selector::parse(self, input)
                         })?;
                         // Selectors inside `:-moz-any` may not include combinators.
                         if selectors.iter().flat_map(|x| x.iter_raw_match_order()).any(|s| s.is_combinator()) {
-                            return Err(SelectorParseError::UnexpectedIdent("-moz-any".into()).into())
+                            return Err(parser.new_custom_error(
+                                SelectorParseErrorKind::UnexpectedIdent("-moz-any".into())
+                            ))
                         }
                         NonTSPseudoClass::MozAny(selectors.into_boxed_slice())
                     }
-                    _ => return Err(SelectorParseError::UnsupportedPseudoClassOrElement(name.clone()).into())
+                    _ => return Err(parser.new_custom_error(
+                        SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name.clone())
+                    ))
                 }
             }
         }
         let pseudo_class = apply_non_ts_list!(pseudo_class_string_parse);
         if self.is_pseudo_class_enabled(&pseudo_class) {
             Ok(pseudo_class)
         } else {
-            Err(SelectorParseError::UnsupportedPseudoClassOrElement(name).into())
+            Err(parser.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name)))
         }
     }
 
-    fn parse_pseudo_element(&self, name: CowRcStr<'i>) -> Result<PseudoElement, ParseError<'i>> {
+    fn parse_pseudo_element(&self, location: SourceLocation, name: CowRcStr<'i>)
+                            -> Result<PseudoElement, ParseError<'i>> {
         PseudoElement::from_slice(&name, self.in_user_agent_stylesheet())
-            .ok_or(SelectorParseError::UnsupportedPseudoClassOrElement(name.clone()).into())
+            .ok_or(location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name.clone())))
     }
 
     fn parse_functional_pseudo_element<'t>(&self, name: CowRcStr<'i>,
                                            parser: &mut Parser<'i, 't>)
                                            -> Result<PseudoElement, ParseError<'i>> {
         if name.starts_with("-moz-tree-") {
             // Tree pseudo-elements can have zero or more arguments,
             // separated by either comma or space.
             let mut args = Vec::new();
             loop {
+                let location = parser.current_source_location();
                 match parser.next() {
                     Ok(&Token::Ident(ref ident)) => args.push(ident.as_ref().to_owned()),
                     Ok(&Token::Comma) => {},
-                    Ok(t) => return Err(BasicParseError::UnexpectedToken(t.clone()).into()),
-                    Err(BasicParseError::EndOfInput) => break,
+                    Ok(t) => return Err(location.new_unexpected_token_error(t.clone())),
+                    Err(BasicParseError { kind: BasicParseErrorKind::EndOfInput, .. }) => break,
                     _ => unreachable!("Parser::next() shouldn't return any other error"),
                 }
             }
             let args = args.into_boxed_slice();
             if let Some(pseudo) = PseudoElement::tree_pseudo_element(&name, args) {
                 return Ok(pseudo);
             }
         }
-        Err(SelectorParseError::UnsupportedPseudoClassOrElement(name.clone()).into())
+        Err(parser.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name.clone())))
     }
 
     fn default_namespace(&self) -> Option<Namespace> {
         self.namespaces.default.clone().as_ref().map(|&(ref ns, _)| ns.clone())
     }
 
     fn namespace_for_prefix(&self, prefix: &Atom) -> Option<Namespace> {
         self.namespaces.prefixes.get(prefix).map(|&(ref ns, _)| ns.clone())
--- a/servo/components/style/macros.rs
+++ b/servo/components/style/macros.rs
@@ -21,24 +21,27 @@ macro_rules! trivial_to_computed_value {
 }
 
 /// A macro to parse an identifier, or return an `UnexpectedIndent` error
 /// otherwise.
 ///
 /// FIXME(emilio): The fact that `UnexpectedIdent` is a `SelectorParseError`
 /// doesn't make a lot of sense to me.
 macro_rules! try_match_ident_ignore_ascii_case {
-    ($ident:expr, $( $match_body:tt )*) => {
-        let __ident = $ident;
-        (match_ignore_ascii_case! { &*__ident,
+    ($input:expr, $( $match_body:tt )*) => {
+        let location = $input.current_source_location();
+        let ident = $input.expect_ident_cloned()?;
+        (match_ignore_ascii_case! { &ident,
             $( $match_body )*
             _ => Err(()),
         })
         .map_err(|()| {
-            ::selectors::parser::SelectorParseError::UnexpectedIdent(__ident.clone()).into()
+            location.new_custom_error(
+                ::selectors::parser::SelectorParseErrorKind::UnexpectedIdent(ident.clone())
+            )
         })
     }
 }
 
 macro_rules! define_numbered_css_keyword_enum {
     ($name: ident: $( $css: expr => $variant: ident = $value: expr ),+,) => {
         define_numbered_css_keyword_enum!($name: $( $css => $variant = $value ),+);
     };
@@ -51,17 +54,17 @@ macro_rules! define_numbered_css_keyword
             $( $variant = $value ),+
         }
 
         impl $crate::parser::Parse for $name {
             fn parse<'i, 't>(
                 _context: &$crate::parser::ParserContext,
                 input: &mut ::cssparser::Parser<'i, 't>,
             ) -> Result<$name, ::style_traits::ParseError<'i>> {
-                try_match_ident_ignore_ascii_case! { input.expect_ident()?,
+                try_match_ident_ignore_ascii_case! { input,
                     $( $css => Ok($name::$variant), )+
                 }
             }
         }
 
         impl ::style_traits::values::ToCss for $name {
             fn to_css<W>(&self, dest: &mut W) -> ::std::fmt::Result
             where
--- a/servo/components/style/media_queries.rs
+++ b/servo/components/style/media_queries.rs
@@ -7,21 +7,21 @@
 //! [mq]: https://drafts.csswg.org/mediaqueries/
 
 use Atom;
 use context::QuirksMode;
 use cssparser::{Delimiter, Parser};
 use cssparser::{Token, ParserInput};
 use error_reporting::{ContextualParseError, ParseErrorReporter};
 use parser::{ParserContext, ParserErrorContext};
-use selectors::parser::SelectorParseError;
+use selectors::parser::SelectorParseErrorKind;
 use serialize_comma_separated_list;
 use std::fmt;
 use str::string_as_ascii_lowercase;
-use style_traits::{ToCss, ParseError, StyleParseError};
+use style_traits::{ToCss, ParseError, StyleParseErrorKind};
 use values::CustomIdent;
 
 #[cfg(feature = "servo")]
 pub use servo::media_queries::{Device, Expression};
 #[cfg(feature = "gecko")]
 pub use gecko::media_queries::{Device, Expression};
 
 /// A type that encapsulates a media query list.
@@ -205,23 +205,23 @@ impl MediaQuery {
             Some(Qualifier::Not)
         } else {
             None
         };
 
         let media_type = match input.try(|i| i.expect_ident_cloned()) {
             Ok(ident) => {
                 let result: Result<_, ParseError> = MediaQueryType::parse(&*ident)
-                    .map_err(|()| SelectorParseError::UnexpectedIdent(ident.clone()).into());
+                    .map_err(|()| input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())));
                 result?
             }
             Err(_) => {
                 // Media type is only optional if qualifier is not specified.
                 if qualifier.is_some() {
-                    return Err(StyleParseError::UnspecifiedError.into())
+                    return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
                 }
 
                 // Without a media type, require at least one expression.
                 expressions.push(Expression::parse(context, input)?);
 
                 MediaQueryType::All
             }
         };
@@ -252,27 +252,27 @@ where
 {
     if input.is_exhausted() {
         return MediaList::empty()
     }
 
     let mut media_queries = vec![];
     loop {
         let start_position = input.position();
-        let start_location = input.current_source_location();
         match input.parse_until_before(Delimiter::Comma, |i| MediaQuery::parse(context, i)) {
             Ok(mq) => {
                 media_queries.push(mq);
             },
             Err(err) => {
                 media_queries.push(MediaQuery::never_matching());
+                let location = err.location;
                 let error = ContextualParseError::InvalidMediaRule(
                     input.slice_from(start_position), err);
                 let error_context = ParserErrorContext { error_reporter };
-                context.log_css_error(&error_context, start_location, error);
+                context.log_css_error(&error_context, location, error);
             },
         }
 
         match input.next() {
             Ok(&Token::Comma) => {},
             Ok(_) => unreachable!(),
             Err(_) => break,
         }
--- a/servo/components/style/properties/declaration_block.rs
+++ b/servo/components/style/properties/declaration_block.rs
@@ -3,29 +3,28 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! A property declaration block.
 
 #![deny(missing_docs)]
 
 use context::QuirksMode;
 use cssparser::{DeclarationListParser, parse_important, ParserInput, CowRcStr};
-use cssparser::{Parser, AtRuleParser, DeclarationParser, Delimiter, ParseError as CssParseError};
+use cssparser::{Parser, AtRuleParser, DeclarationParser, Delimiter, ParseErrorKind};
 use custom_properties::CustomPropertiesBuilder;
 use error_reporting::{ParseErrorReporter, ContextualParseError};
 use parser::{ParserContext, ParserErrorContext};
 use properties::animated_properties::AnimationValue;
-use selectors::parser::SelectorParseError;
 use shared_lock::Locked;
 use smallbitvec::{self, SmallBitVec};
 use smallvec::SmallVec;
 use std::fmt;
 use std::iter::{DoubleEndedIterator, Zip};
 use std::slice::Iter;
-use style_traits::{PARSING_MODE_DEFAULT, ToCss, ParseError, ParsingMode, StyleParseError};
+use style_traits::{PARSING_MODE_DEFAULT, ToCss, ParseError, ParsingMode, StyleParseErrorKind};
 use stylesheets::{CssRuleType, Origin, UrlExtraData};
 use super::*;
 use values::computed::Context;
 #[cfg(feature = "gecko")] use properties::animated_properties::AnimationValueMap;
 
 /// The animation rules.
 ///
 /// The first one is for Animation cascade level, and the second one is for
@@ -1030,65 +1029,65 @@ pub fn parse_one_declaration_into<R>(dec
     let context = ParserContext::new(Origin::Author,
                                      url_data,
                                      Some(CssRuleType::Style),
                                      parsing_mode,
                                      quirks_mode);
     let mut input = ParserInput::new(input);
     let mut parser = Parser::new(&mut input);
     let start_position = parser.position();
-    let start_location = parser.current_source_location();
     parser.parse_entirely(|parser| {
         let name = id.name().into();
         PropertyDeclaration::parse_into(declarations, id, name, &context, parser)
             .map_err(|e| e.into())
     }).map_err(|err| {
+        let location = err.location;
         let error = ContextualParseError::UnsupportedPropertyDeclaration(
             parser.slice_from(start_position), err);
         let error_context = ParserErrorContext { error_reporter: error_reporter };
-        context.log_css_error(&error_context, start_location, error);
+        context.log_css_error(&error_context, location, error);
     })
 }
 
 /// A struct to parse property declarations.
 struct PropertyDeclarationParser<'a, 'b: 'a> {
     context: &'a ParserContext<'b>,
     declarations: &'a mut SourcePropertyDeclaration,
 }
 
 
 /// Default methods reject all at rules.
 impl<'a, 'b, 'i> AtRuleParser<'i> for PropertyDeclarationParser<'a, 'b> {
     type PreludeNoBlock = ();
     type PreludeBlock = ();
     type AtRule = Importance;
-    type Error = SelectorParseError<'i, StyleParseError<'i>>;
+    type Error = StyleParseErrorKind<'i>;
 }
 
 /// Based on NonMozillaVendorIdentifier from Gecko's CSS parser.
 fn is_non_mozilla_vendor_identifier(name: &str) -> bool {
     (name.starts_with("-") && !name.starts_with("-moz-")) ||
         name.starts_with("_")
 }
 
 impl<'a, 'b, 'i> DeclarationParser<'i> for PropertyDeclarationParser<'a, 'b> {
     type Declaration = Importance;
-    type Error = SelectorParseError<'i, StyleParseError<'i>>;
+    type Error = StyleParseErrorKind<'i>;
 
     fn parse_value<'t>(&mut self, name: CowRcStr<'i>, input: &mut Parser<'i, 't>)
                        -> Result<Importance, ParseError<'i>> {
         let prop_context = PropertyParserContext::new(self.context);
         let id = match PropertyId::parse(&name, Some(&prop_context)) {
             Ok(id) => id,
             Err(()) => {
-                return Err(if is_non_mozilla_vendor_identifier(&name) {
-                    PropertyDeclarationParseError::UnknownVendorProperty
+                return Err(input.new_custom_error(if is_non_mozilla_vendor_identifier(&name) {
+                    StyleParseErrorKind::UnknownVendorProperty
                 } else {
-                    PropertyDeclarationParseError::UnknownProperty(name)
-                }.into());
+                    StyleParseErrorKind::UnknownProperty(name)
+                }));
             }
         };
         input.parse_until_before(Delimiter::Bang, |input| {
             PropertyDeclaration::parse_into(self.declarations, id, name, self.context, input)
                 .map_err(|e| e.into())
         })?;
         let importance = match input.try(parse_important) {
             Ok(()) => Importance::Important,
@@ -1116,26 +1115,25 @@ pub fn parse_property_declaration_list<R
         declarations: &mut declarations,
     };
     let mut iter = DeclarationListParser::new(input, parser);
     while let Some(declaration) = iter.next() {
         match declaration {
             Ok(importance) => {
                 block.extend(iter.parser.declarations.drain(), importance);
             }
-            Err(err) => {
+            Err((error, slice)) => {
                 iter.parser.declarations.clear();
 
                 // If the unrecognized property looks like a vendor-specific property,
                 // silently ignore it instead of polluting the error output.
-                if let CssParseError::Custom(SelectorParseError::Custom(
-                    StyleParseError::PropertyDeclaration(
-                        PropertyDeclarationParseError::UnknownVendorProperty))) = err.error {
+                if let ParseErrorKind::Custom(StyleParseErrorKind::UnknownVendorProperty) = error.kind {
                     continue;
                 }
 
-                let error = ContextualParseError::UnsupportedPropertyDeclaration(err.slice, err.error);
-                context.log_css_error(error_context, err.location, error);
+                let location = error.location;
+                let error = ContextualParseError::UnsupportedPropertyDeclaration(slice, error);
+                context.log_css_error(error_context, location, error);
             }
         }
     }
     block
 }
--- a/servo/components/style/properties/helpers.mako.rs
+++ b/servo/components/style/properties/helpers.mako.rs
@@ -88,19 +88,19 @@
         pub mod single_value {
             #[allow(unused_imports)]
             use cssparser::{Parser, BasicParseError};
             #[allow(unused_imports)]
             use parser::{Parse, ParserContext};
             #[allow(unused_imports)]
             use properties::ShorthandId;
             #[allow(unused_imports)]
-            use selectors::parser::SelectorParseError;
+            use selectors::parser::SelectorParseErrorKind;
             #[allow(unused_imports)]
-            use style_traits::{ParseError, StyleParseError};
+            use style_traits::{ParseError, StyleParseErrorKind};
             #[allow(unused_imports)]
             use values::computed::{Context, ToComputedValue};
             #[allow(unused_imports)]
             use values::{computed, specified};
             #[allow(unused_imports)]
             use values::{Auto, Either, None_, Normal};
             ${caller.body()}
         }
@@ -280,21 +280,21 @@
         use properties::longhands;
         #[allow(unused_imports)]
         use properties::{DeclaredValue, LonghandId, LonghandIdSet};
         #[allow(unused_imports)]
         use properties::{CSSWideKeyword, ComputedValues, PropertyDeclaration};
         #[allow(unused_imports)]
         use properties::style_structs;
         #[allow(unused_imports)]
-        use selectors::parser::SelectorParseError;
+        use selectors::parser::SelectorParseErrorKind;
         #[allow(unused_imports)]
         use servo_arc::Arc;
         #[allow(unused_imports)]
-        use style_traits::{ParseError, StyleParseError};
+        use style_traits::{ParseError, StyleParseErrorKind};
         #[allow(unused_imports)]
         use values::computed::{Context, ToComputedValue};
         #[allow(unused_imports)]
         use values::{computed, generics, specified};
         #[allow(unused_imports)]
         use Atom;
         ${caller.body()}
         #[allow(unused_variables)]
@@ -694,21 +694,21 @@
 %>
     % if shorthand:
     /// ${shorthand.spec}
     pub mod ${shorthand.ident} {
         use cssparser::Parser;
         use parser::ParserContext;
         use properties::{PropertyDeclaration, SourcePropertyDeclaration, MaybeBoxed, longhands};
         #[allow(unused_imports)]
-        use selectors::parser::SelectorParseError;
+        use selectors::parser::SelectorParseErrorKind;
         #[allow(unused_imports)]
         use std::fmt;
         #[allow(unused_imports)]
-        use style_traits::{ParseError, StyleParseError};
+        use style_traits::{ParseError, StyleParseErrorKind};
         #[allow(unused_imports)]
         use style_traits::ToCss;
 
         pub struct Longhands {
             % for sub_property in shorthand.sub_properties:
                 pub ${sub_property.ident}:
                     % if sub_property.boxed:
                         Box<
@@ -987,17 +987,17 @@
             % if logical:
             let ret = ${length_type}::parse(context, input);
             % else:
             let ret = ${length_type}::parse_quirky(context, input, AllowQuirks::Yes);
             % endif
             // Keyword values don't make sense in the block direction; don't parse them
             % if "block" in name:
                 if let Ok(${length_type}::ExtremumLength(..)) = ret {
-                    return Err(StyleParseError::UnspecifiedError.into())
+                    return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
                 }
             % endif
             ret.map(SpecifiedValue)
         }
 
         impl ToComputedValue for SpecifiedValue {
             type ComputedValue = computed_value::T;
             #[inline]
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -20,17 +20,17 @@ use properties::longhands::font_stretch:
 use properties::longhands::font_variation_settings::computed_value::T as FontVariationSettings;
 use properties::longhands::transform::computed_value::ComputedMatrix;
 use properties::longhands::transform::computed_value::ComputedOperation as TransformOperation;
 use properties::longhands::transform::computed_value::T as TransformList;
 use properties::longhands::visibility::computed_value::T as Visibility;
 #[cfg(feature = "gecko")]
 use properties::PropertyId;
 use properties::{LonghandId, ShorthandId};
-use selectors::parser::SelectorParseError;
+use selectors::parser::SelectorParseErrorKind;
 use servo_arc::Arc;
 use smallvec::SmallVec;
 use std::borrow::Cow;
 use std::cmp;
 use std::fmt;
 #[cfg(feature = "gecko")] use hash::FnvHashMap;
 use style_traits::{ParseError, ToCss};
 use super::ComputedValues;
@@ -129,27 +129,28 @@ impl TransitionProperty {
                 }
             % endif
         % endfor
         false
     }
 
     /// Parse a transition-property value.
     pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
+        let location = input.current_source_location();
         let ident = input.expect_ident()?;
         match_ignore_ascii_case! { &ident,
             "all" => Ok(TransitionProperty::All),
             % for prop in data.shorthands_except_all():
                 "${prop.name}" => Ok(TransitionProperty::Shorthand(ShorthandId::${prop.camel_case})),
             % endfor
             % for prop in data.longhands:
                 "${prop.name}" => Ok(TransitionProperty::Longhand(LonghandId::${prop.camel_case})),
             % endfor
-            "none" => Err(SelectorParseError::UnexpectedIdent(ident.clone()).into()),
-            _ => CustomIdent::from_ident(ident, &[]).map(TransitionProperty::Unsupported),
+            "none" => Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone()))),
+            _ => CustomIdent::from_ident(location, ident, &[]).map(TransitionProperty::Unsupported),
         }
     }
 
 
     /// Convert TransitionProperty to nsCSSPropertyID.
     #[cfg(feature = "gecko")]
     pub fn to_nscsspropertyid(&self) -> Result<nsCSSPropertyID, ()> {
         Ok(match *self {
--- a/servo/components/style/properties/longhand/background.mako.rs
+++ b/servo/components/style/properties/longhand/background.mako.rs
@@ -127,17 +127,17 @@
                          -> Result<SpecifiedValue, ParseError<'i>> {
         let ident = input.expect_ident_cloned()?;
         (match_ignore_ascii_case! { &ident,
             "repeat-x" => Ok(SpecifiedValue::RepeatX),
             "repeat-y" => Ok(SpecifiedValue::RepeatY),
             _ => Err(()),
         }).or_else(|()| {
             let horizontal: Result<_, ParseError> = RepeatKeyword::from_ident(&ident)
-                .map_err(|()| SelectorParseError::UnexpectedIdent(ident.clone()).into());
+                .map_err(|()| input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())));
             let horizontal = horizontal?;
             let vertical = input.try(RepeatKeyword::parse).ok();
             Ok(SpecifiedValue::Other(horizontal, vertical))
         })
     }
 </%helpers:vector_longhand>
 
 ${helpers.single_keyword("background-attachment",
--- a/servo/components/style/properties/longhand/border.mako.rs
+++ b/servo/components/style/properties/longhand/border.mako.rs
@@ -182,17 +182,17 @@
             let mut result = Vec::new();
             while let Ok(value) = input.try(|i| RGBAColor::parse(context, i)) {
                 result.push(value);
             }
 
             if !result.is_empty() {
                 Ok(SpecifiedValue::Colors(result))
             } else {
-                Err(StyleParseError::UnspecifiedError.into())
+                Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
             }
         }
     </%helpers:longhand>
 % endfor
 
 ${helpers.single_keyword("box-decoration-break", "slice clone",
                          gecko_enum_prefix="StyleBoxDecorationBreak",
                          spec="https://drafts.csswg.org/css-break/#propdef-box-decoration-break",
--- a/servo/components/style/properties/longhand/box.mako.rs
+++ b/servo/components/style/properties/longhand/box.mako.rs
@@ -160,17 +160,17 @@
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         computed_value::T::${to_rust_ident(values[0])}
     }
 
     /// Parse a display value.
     pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                          -> Result<SpecifiedValue, ParseError<'i>> {
-        try_match_ident_ignore_ascii_case! { input.expect_ident()?,
+        try_match_ident_ignore_ascii_case! { input,
             % for value in values:
                 "${value}" => {
                     Ok(computed_value::T::${to_rust_ident(value)})
                 },
             % endfor
             % for value in webkit_prefixed_values:
                 "-webkit-${value}" => {
                     Ok(computed_value::T::${to_rust_ident(value)})
@@ -540,17 +540,17 @@
         fn parse<'i, 't>(_context: &ParserContext, input: &mut ::cssparser::Parser<'i, 't>)
                          -> Result<Self, ParseError<'i>> {
             if input.try(|input| input.expect_ident_matching("infinite")).is_ok() {
                 return Ok(SpecifiedValue::Infinite)
             }
 
             let number = input.expect_number()?;
             if number < 0.0 {
-                return Err(StyleParseError::UnspecifiedError.into());
+                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
             }
 
             Ok(SpecifiedValue::Number(number))
         }
     }
 
 
     #[inline]
@@ -1172,17 +1172,17 @@
                     },
                     "perspective" => {
                         let d = specified::Length::parse_non_negative(context, input)?;
                         Ok(SpecifiedOperation::Perspective(d))
                     },
                     _ => Err(()),
                 };
                 result
-                    .map_err(|()| StyleParseError::UnexpectedFunction(function.clone()).into())
+                    .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnexpectedFunction(function.clone())))
             })
         })?))
     }
 
     /// Parses `transform` property.
     #[inline]
     pub fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
                          -> Result<SpecifiedValue,ParseError<'i>> {
@@ -1725,25 +1725,25 @@
             let flag = match_ignore_ascii_case! { &name,
                 "layout" => Some(LAYOUT),
                 "style" => Some(STYLE),
                 "paint" => Some(PAINT),
                 _ => None
             };
             let flag = match flag {
                 Some(flag) if !result.contains(flag) => flag,
-                _ => return Err(SelectorParseError::UnexpectedIdent(name.clone()).into())
+                _ => return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
             };
             result.insert(flag);
         }
 
         if !result.is_empty() {
             Ok(result)
         } else {
-            Err(StyleParseError::UnspecifiedError.into())
+            Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         }
     }
 </%helpers:longhand>
 
 // Non-standard
 ${helpers.single_keyword("-moz-appearance",
                          """none button button-arrow-down button-arrow-next button-arrow-previous button-arrow-up
                             button-bevel button-focus caret checkbox checkbox-container checkbox-label checkmenuitem
@@ -1834,17 +1834,18 @@
 
     /// auto | <animateable-feature>#
     pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                          -> Result<SpecifiedValue, ParseError<'i>> {
         if input.try(|input| input.expect_ident_matching("auto")).is_ok() {
             Ok(computed_value::T::Auto)
         } else {
             input.parse_comma_separated(|i| {
-                CustomIdent::from_ident(i.expect_ident()?, &[
+                let location = i.current_source_location();
+                CustomIdent::from_ident(location, i.expect_ident()?, &[
                     "will-change",
                     "none",
                     "all",
                     "auto",
                 ])
             }).map(SpecifiedValue::AnimateableFeatures)
         }
     }
@@ -1909,17 +1910,17 @@
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         TOUCH_ACTION_AUTO
     }
 
     pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                          -> Result<SpecifiedValue, ParseError<'i>> {
         // FIXME: remove clone() when lifetimes are non-lexical
-        try_match_ident_ignore_ascii_case! { input.expect_ident()?.clone(),
+        try_match_ident_ignore_ascii_case! { input,
             "auto" => Ok(TOUCH_ACTION_AUTO),
             "none" => Ok(TOUCH_ACTION_NONE),
             "manipulation" => Ok(TOUCH_ACTION_MANIPULATION),
             "pan-x" => {
                 if input.try(|i| i.expect_ident_matching("pan-y")).is_ok() {
                     Ok(TOUCH_ACTION_PAN_X | TOUCH_ACTION_PAN_Y)
                 } else {
                     Ok(TOUCH_ACTION_PAN_X)
--- a/servo/components/style/properties/longhand/counters.mako.rs
+++ b/servo/components/style/properties/longhand/counters.mako.rs
@@ -200,38 +200,40 @@
                             "attr" => Some(input.parse_nested_block(|input| {
                                 Ok(ContentItem::Attr(Attr::parse_function(context, input)?))
                             })),
                         % endif
                         _ => None
                     };
                     match result {
                         Some(result) => content.push(result?),
-                        None => return Err(StyleParseError::UnexpectedFunction(name.clone()).into())
+                        None => return Err(input.new_custom_error(
+                            StyleParseErrorKind::UnexpectedFunction(name.clone())
+                        ))
                     }
                 }
                 Ok(Token::Ident(ref ident)) => {
                     let valid = match_ignore_ascii_case! { &ident,
                         "open-quote" => { content.push(ContentItem::OpenQuote); true },
                         "close-quote" => { content.push(ContentItem::CloseQuote); true },
                         "no-open-quote" => { content.push(ContentItem::NoOpenQuote); true },
                         "no-close-quote" => { content.push(ContentItem::NoCloseQuote); true },
 
                         _ => false,
                     };
                     if !valid {
-                        return Err(SelectorParseError::UnexpectedIdent(ident.clone()).into())
+                        return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())))
                     }
                 }
                 Err(_) => break,
-                Ok(t) => return Err(BasicParseError::UnexpectedToken(t).into())
+                Ok(t) => return Err(input.new_unexpected_token_error(t))
             }
         }
         if content.is_empty() {
-            return Err(StyleParseError::UnspecifiedError.into());
+            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
         }
         Ok(SpecifiedValue::Items(content))
     }
 </%helpers:longhand>
 
 <%helpers:longhand name="counter-increment" animation_value_type="discrete"
                    spec="https://drafts.csswg.org/css-lists/#propdef-counter-increment">
     use std::fmt;
@@ -327,30 +329,31 @@
     pub fn parse_common<'i, 't>(context: &ParserContext, default_value: i32, input: &mut Parser<'i, 't>)
                                 -> Result<SpecifiedValue, ParseError<'i>> {
         if input.try(|input| input.expect_ident_matching("none")).is_ok() {
             return Ok(SpecifiedValue(Vec::new()))
         }
 
         let mut counters = Vec::new();
         loop {
+            let location = input.current_source_location();
             let counter_name = match input.next() {
-                Ok(&Token::Ident(ref ident)) => CustomIdent::from_ident(ident, &["none"])?,
-                Ok(t) => return Err(BasicParseError::UnexpectedToken(t.clone()).into()),
+                Ok(&Token::Ident(ref ident)) => CustomIdent::from_ident(location, ident, &["none"])?,
+                Ok(t) => return Err(location.new_unexpected_token_error(t.clone())),
                 Err(_) => break,
             };
             let counter_delta = input.try(|input| specified::parse_integer(context, input))
                                      .unwrap_or(specified::Integer::new(default_value));
             counters.push((counter_name, counter_delta))
         }
 
         if !counters.is_empty() {
             Ok(SpecifiedValue(counters))
         } else {
-            Err(StyleParseError::UnspecifiedError.into())
+            Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         }
     }
 </%helpers:longhand>
 
 <%helpers:longhand name="counter-reset" animation_value_type="discrete"
                    spec="https://drafts.csswg.org/css-lists-3/#propdef-counter-reset">
     pub use super::counter_increment::{SpecifiedValue, computed_value, get_initial_value};
     use super::counter_increment::parse_common;
--- a/servo/components/style/properties/longhand/font.mako.rs
+++ b/servo/components/style/properties/longhand/font.mako.rs
@@ -582,17 +582,17 @@ macro_rules! impl_gecko_keyword_conversi
     }
 
     /// `FamilyName::parse` is based on `FontFamily::parse` and not the other way around
     /// because we want the former to exclude generic family keywords.
     impl Parse for FamilyName {
         fn parse<'i, 't>(_: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
             match FontFamily::parse(input) {
                 Ok(FontFamily::FamilyName(name)) => Ok(name),
-                Ok(FontFamily::Generic(_)) => Err(StyleParseError::UnspecifiedError.into()),
+                Ok(FontFamily::Generic(_)) => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
                 Err(e) => Err(e)
             }
         }
     }
 </%helpers:longhand>
 
 ${helpers.single_keyword_system("font-style",
                                 "normal italic oblique",
@@ -739,17 +739,17 @@ macro_rules! impl_gecko_keyword_conversi
             }
         }
     }
 
     impl Parse for computed_value::T {
         fn parse<'i, 't>(_: &ParserContext, input: &mut Parser<'i, 't>)
             -> Result<Self, ParseError<'i>> {
                 Self::from_int(input.expect_integer()?)
-                    .map_err(|_| StyleParseError::UnspecifiedError.into())
+                    .map_err(|_| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
             }
     }
 
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         computed_value::T::normal()
     }
 
@@ -832,17 +832,17 @@ macro_rules! impl_gecko_keyword_conversi
         if let Ok(lop) = input.try(|i| LengthOrPercentage::parse_non_negative_quirky(context, i, allow_quirks)) {
             return Ok(SpecifiedValue::Length(lop))
         }
 
         if let Ok(kw) = input.try(KeywordSize::parse) {
             return Ok(SpecifiedValue::Keyword(kw.into()))
         }
 
-        try_match_ident_ignore_ascii_case! { input.expect_ident()?,
+        try_match_ident_ignore_ascii_case! { input,
             "smaller" => Ok(SpecifiedValue::Smaller),
             "larger" => Ok(SpecifiedValue::Larger),
         }
     }
 
     #[allow(unused_mut)]
     pub fn cascade_specified_font_size(context: &mut Context,
                                        specified_value: &SpecifiedValue,
@@ -1077,17 +1077,17 @@ macro_rules! impl_gecko_keyword_conversi
     pub fn get_initial_value() -> computed_value::T {
         SpecifiedValue { weight: true, style: true }
     }
 
     pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                          -> Result<SpecifiedValue, ParseError<'i>> {
         let mut result = SpecifiedValue { weight: false, style: false };
         // FIXME: remove clone() when lifetimes are non-lexical
-        try_match_ident_ignore_ascii_case! { input.expect_ident()?.clone(),
+        try_match_ident_ignore_ascii_case! { input,
             "none" => Ok(result),
             "weight" => {
                 result.weight = true;
                 if input.try(|input| input.expect_ident_matching("style")).is_ok() {
                     result.style = true;
                 }
                 Ok(result)
             },
@@ -1293,65 +1293,68 @@ macro_rules! impl_gecko_keyword_conversi
                          -> Result<SpecifiedValue, ParseError<'i>> {
         let mut alternates = Vec::new();
         if input.try(|input| input.expect_ident_matching("normal")).is_ok() {
             return Ok(SpecifiedValue::Value(VariantAlternatesList(alternates.into_boxed_slice())));
         }
 
         let mut parsed_alternates = ParsingFlags::empty();
         macro_rules! check_if_parsed(
-            ($flag:ident) => (
+            ($input:expr, $flag:ident) => (
                 if parsed_alternates.contains($flag) {
-                    return Err(StyleParseError::UnspecifiedError.into())
+                    return Err($input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
                 }
                 parsed_alternates |= $flag;
             )
         );
         while let Ok(_) = input.try(|input| {
             // FIXME: remove clone() when lifetimes are non-lexical
             match input.next()?.clone() {
                 Token::Ident(ref ident) => {
                     if *ident == "historical-forms" {
-                        check_if_parsed!(HISTORICAL_FORMS);
+                        check_if_parsed!(input, HISTORICAL_FORMS);
                         alternates.push(VariantAlternates::HistoricalForms);
                         Ok(())
                     } else {
-                        return Err(StyleParseError::UnspecifiedError.into());
+                        return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
                     }
                 },
                 Token::Function(ref name) => {
                     input.parse_nested_block(|i| {
                         match_ignore_ascii_case! { &name,
                             % for value in "swash stylistic ornaments annotation".split():
                             "${value}" => {
-                                check_if_parsed!(${value.upper()});
-                                let ident = CustomIdent::from_ident(i.expect_ident()?, &[])?;
+                                check_if_parsed!(i, ${value.upper()});
+                                let location = i.current_source_location();
+                                let ident = CustomIdent::from_ident(location, i.expect_ident()?, &[])?;
                                 alternates.push(VariantAlternates::${to_camel_case(value)}(ident));
                                 Ok(())
                             },
                             % endfor
                             % for value in "styleset character-variant".split():
                             "${value}" => {
-                                check_if_parsed!(${to_rust_ident(value).upper()});
-                                let idents = i.parse_comma_separated(|i|
-                                    CustomIdent::from_ident(i.expect_ident()?, &[]))?;
+                                check_if_parsed!(i, ${to_rust_ident(value).upper()});
+                                let idents = i.parse_comma_separated(|i| {
+                                    let location = i.current_source_location();
+                                    CustomIdent::from_ident(location, i.expect_ident()?, &[])
+                                })?;
                                 alternates.push(VariantAlternates::${to_camel_case(value)}(idents.into_boxed_slice()));
                                 Ok(())
                             },
                             % endfor
-                            _ => return Err(StyleParseError::UnspecifiedError.into()),
+                            _ => return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
                         }
                     })
                 },
-                _ => Err(StyleParseError::UnspecifiedError.into()),
+                _ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
             }
         }) { }
 
         if parsed_alternates.is_empty() {
-            return Err(StyleParseError::UnspecifiedError.into());
+            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
         }
         Ok(SpecifiedValue::Value(VariantAlternatesList(alternates.into_boxed_slice())))
     }
 </%helpers:longhand>
 
 #[cfg(feature = "gecko")]
 macro_rules! exclusive_value {
     (($value:ident, $set:expr) => $ident:ident) => {
@@ -1496,17 +1499,17 @@ macro_rules! exclusive_value {
             })
         }) {
             result.insert(flag);
         }
 
         if !result.is_empty() {
             Ok(SpecifiedValue::Value(result))
         } else {
-            Err(StyleParseError::UnspecifiedError.into())
+            Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         }
     }
 
     #[cfg(feature = "gecko")]
     impl_gecko_keyword_conversions!(VariantEastAsian, u16);
 </%helpers:longhand>
 
 <%helpers:longhand name="font-variant-ligatures" products="gecko" animation_value_type="discrete"
@@ -1656,17 +1659,17 @@ macro_rules! exclusive_value {
             })
         }) {
             result.insert(flag);
         }
 
         if !result.is_empty() {
             Ok(SpecifiedValue::Value(result))
         } else {
-            Err(StyleParseError::UnspecifiedError.into())
+            Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         }
     }
 
     #[cfg(feature = "gecko")]
     impl_gecko_keyword_conversions!(VariantLigatures, u16);
 </%helpers:longhand>
 
 <%helpers:longhand name="font-variant-numeric" products="gecko" animation_value_type="discrete"
@@ -1804,17 +1807,17 @@ macro_rules! exclusive_value {
             })
         }) {
             result.insert(flag);
         }
 
         if !result.is_empty() {
             Ok(SpecifiedValue::Value(result))
         } else {
-            Err(StyleParseError::UnspecifiedError.into())
+            Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         }
     }
 
     #[cfg(feature = "gecko")]
     impl_gecko_keyword_conversions!(VariantNumeric, u8);
 </%helpers:longhand>
 
 ${helpers.single_keyword_system("font-variant-position",
@@ -2077,20 +2080,20 @@ https://drafts.csswg.org/css-fonts-4/#lo
         pub struct T(pub Atom);
     }
 
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         computed_value::T(atom!(""))
     }
 
-    pub fn parse<'i, 't>(_context: &ParserContext, _input: &mut Parser<'i, 't>)
+    pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                          -> Result<SpecifiedValue, ParseError<'i>> {
         debug_assert!(false, "Should be set directly by presentation attributes only.");
-        Err(StyleParseError::UnspecifiedError.into())
+        Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     }
 </%helpers:longhand>
 
 // MathML properties
 <%helpers:longhand name="-moz-script-size-multiplier" products="gecko" animation_value_type="none"
                    predefined_type="Number" gecko_ffi_name="mScriptSizeMultiplier"
                    spec="Internal (not web-exposed)"
                    internal="True">
@@ -2100,20 +2103,20 @@ https://drafts.csswg.org/css-fonts-4/#lo
         pub type T = f32;
     }
 
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         ::gecko_bindings::structs::NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER as f32
     }
 
-    pub fn parse<'i, 't>(_context: &ParserContext, _input: &mut Parser<'i, 't>)
+    pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                          -> Result<SpecifiedValue, ParseError<'i>> {
         debug_assert!(false, "Should be set directly by presentation attributes only.");
-        Err(StyleParseError::UnspecifiedError.into())
+        Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     }
 </%helpers:longhand>
 
 <%helpers:longhand name="-moz-script-level" products="gecko" animation_value_type="none"
                    predefined_type="Integer" gecko_ffi_name="mScriptLevel"
                    spec="Internal (not web-exposed)"
                    internal="True">
     use std::fmt;
@@ -2249,20 +2252,20 @@ https://drafts.csswg.org/css-fonts-4/#lo
         }
     }
 
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         Length::new(NS_MATHML_DEFAULT_SCRIPT_MIN_SIZE_PT as f32 * (AU_PER_PT / AU_PER_PX))
     }
 
-    pub fn parse<'i, 't>(_context: &ParserContext, _input: &mut Parser<'i, 't>)
+    pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                          -> Result<SpecifiedValue, ParseError<'i>> {
         debug_assert!(false, "Should be set directly by presentation attributes only.");
-        Err(StyleParseError::UnspecifiedError.into())
+        Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     }
 </%helpers:longhand>
 
 <%helpers:longhand name="-x-text-zoom" products="gecko" animation_value_type="none" internal="True"
                    spec="Internal (not web-exposed)">
     pub use self::computed_value::T as SpecifiedValue;
 
     pub mod computed_value {
@@ -2282,20 +2285,20 @@ https://drafts.csswg.org/css-fonts-4/#lo
         pub struct T(pub bool);
     }
 
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         computed_value::T(true)
     }
 
-    pub fn parse<'i, 't>(_context: &ParserContext, _input: &mut Parser<'i, 't>)
+    pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                          -> Result<SpecifiedValue, ParseError<'i>> {
         debug_assert!(false, "Should be set directly by presentation attributes only.");
-        Err(StyleParseError::UnspecifiedError.into())
+        Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     }
 </%helpers:longhand>
 
 % if product == "gecko":
     pub mod system_font {
         //! We deal with system fonts here
         //!
         //! System fonts can only be set as a group via the font shorthand.
@@ -2440,17 +2443,17 @@ https://drafts.csswg.org/css-fonts-4/#lo
                 pub ${name}: longhands::${name}::computed_value::T,
             % endfor
             pub system_font: SystemFont,
             pub default_font_type: FontFamilyType,
         }
 
         impl SystemFont {
             pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
-                try_match_ident_ignore_ascii_case! { input.expect_ident()?,
+                try_match_ident_ignore_ascii_case! { input,
                     % for font in system_fonts:
                         "${font}" => Ok(SystemFont::${to_camel_case(font)}),
                     % endfor
                 }
             }
         }
 
         impl ToCss for SystemFont {
--- a/servo/components/style/properties/longhand/inherited_box.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_box.mako.rs
@@ -236,16 +236,16 @@
             Ok(SpecifiedValue { angle: None, flipped: false })
         } else if input.try(|input| input.expect_ident_matching("flip")).is_ok() {
             // Handle flip
             Ok(SpecifiedValue { angle: Some(Angle::zero()), flipped: true })
         } else {
             // Handle <angle> | <angle> flip
             let angle = input.try(|input| Angle::parse(context, input)).ok();
             if angle.is_none() {
-                return Err(StyleParseError::UnspecifiedError.into());
+                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
             }
 
             let flipped = input.try(|input| input.expect_ident_matching("flip")).is_ok();
             Ok(SpecifiedValue { angle: angle, flipped: flipped })
         }
     }
 </%helpers:longhand>
--- a/servo/components/style/properties/longhand/inherited_svg.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_svg.mako.rs
@@ -202,42 +202,42 @@
 
         let mut value = 0;
         // bitfield representing what we've seen so far
         // bit 1 is fill, bit 2 is stroke, bit 3 is markers
         let mut seen = 0;
         let mut pos = 0;
 
         loop {
-            let result: Result<_, ParseError> = input.try(|i| {
-                try_match_ident_ignore_ascii_case! { i.expect_ident()?,
+            let result: Result<_, ParseError> = input.try(|input| {
+                try_match_ident_ignore_ascii_case! { input,
                     "fill" => Ok(PaintOrder::Fill),
                     "stroke" => Ok(PaintOrder::Stroke),
                     "markers" => Ok(PaintOrder::Markers),
                 }
             });
 
             match result {
                 Ok(val) => {
                     if (seen & (1 << val as u8)) != 0 {
                         // don't parse the same ident twice
-                        return Err(StyleParseError::UnspecifiedError.into())
+                        return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
                     }
 
                     value |= (val as u8) << (pos * SHIFT);
                     seen |= 1 << (val as u8);
                     pos += 1;
                 }
                 Err(_) => break,
             }
         }
 
         if value == 0 {
             // Couldn't find any keyword
-            return Err(StyleParseError::UnspecifiedError.into())
+            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         }
 
         // fill in rest
         for i in pos..COUNT {
             for paint in 0..COUNT {
                 // if not seen, set bit at position, mark as seen
                 if (seen & (1 << paint)) == 0 {
                     seen |= 1 << paint;
@@ -288,12 +288,13 @@
 
     pub mod computed_value {
         pub type T = super::SpecifiedValue;
     }
 
 
     pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                          -> Result<SpecifiedValue, ParseError<'i>> {
+        let location = input.current_source_location();
         let i = input.expect_ident()?;
-        CustomIdent::from_ident(i, &["all", "none", "auto"])
+        CustomIdent::from_ident(location, i, &["all", "none", "auto"])
     }
 </%helpers:vector_longhand>
--- a/servo/components/style/properties/longhand/inherited_text.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_text.mako.rs
@@ -580,17 +580,17 @@
             shape = input.try(ShapeKeyword::parse);
         }
 
         // At least one of shape or fill must be handled
         let keyword_value = match (fill, shape) {
             (Some(fill), Ok(shape)) => KeywordValue::FillAndShape(fill,shape),
             (Some(fill), Err(_)) => KeywordValue::Fill(fill),
             (None, Ok(shape)) => KeywordValue::Shape(shape),
-            _ => return Err(StyleParseError::UnspecifiedError.into()),
+            _ => return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
         };
         Ok(SpecifiedValue::Keyword(keyword_value))
     }
 </%helpers:longhand>
 
 <%helpers:longhand name="text-emphasis-position" animation_value_type="discrete" products="gecko"
                    spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-position">
     use style_traits::ToCss;
--- a/servo/components/style/properties/longhand/list.mako.rs
+++ b/servo/components/style/properties/longhand/list.mako.rs
@@ -183,32 +183,34 @@
     pub fn parse<'i, 't>(_: &ParserContext, input: &mut Parser<'i, 't>)
                          -> Result<SpecifiedValue,ParseError<'i>> {
         if input.try(|input| input.expect_ident_matching("none")).is_ok() {
             return Ok(SpecifiedValue(Vec::new()))
         }
 
         let mut quotes = Vec::new();
         loop {
+            let location = input.current_source_location();
             let first = match input.next() {
                 Ok(&Token::QuotedString(ref value)) => value.as_ref().to_owned(),
-                Ok(t) => return Err(BasicParseError::UnexpectedToken(t.clone()).into()),
+                Ok(t) => return Err(location.new_unexpected_token_error(t.clone())),
                 Err(_) => break,
             };
+            let location = input.current_source_location();
             let second = match input.next() {
                 Ok(&Token::QuotedString(ref value)) => value.as_ref().to_owned(),
-                Ok(t) => return Err(BasicParseError::UnexpectedToken(t.clone()).into()),
+                Ok(t) => return Err(location.new_unexpected_token_error(t.clone())),
                 Err(e) => return Err(e.into()),
             };
             quotes.push((first, second))
         }
         if !quotes.is_empty() {
             Ok(SpecifiedValue(quotes))
         } else {
-            Err(StyleParseError::UnspecifiedError.into())
+            Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         }
     }
 </%helpers:longhand>
 
 ${helpers.predefined_type("-moz-image-region",
                           "ClipRectOrAuto",
                           "computed::ClipRectOrAuto::auto()",
                           animation_value_type="ComputedValue",
--- a/servo/components/style/properties/longhand/outline.mako.rs
+++ b/servo/components/style/properties/longhand/outline.mako.rs
@@ -53,17 +53,17 @@
     pub fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
                          -> Result<SpecifiedValue, ParseError<'i>> {
         SpecifiedValue::parse(context, input)
             .and_then(|result| {
                 if let Either::Second(BorderStyle::hidden) = result {
                     // The outline-style property accepts the same values as
                     // border-style, except that 'hidden' is not a legal outline
                     // style.
-                    Err(SelectorParseError::UnexpectedIdent("hidden".into()).into())
+                    Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent("hidden".into())))
                 } else {
                     Ok(result)
                 }
             })
     }
 </%helpers:longhand>
 
 ${helpers.predefined_type("outline-width",
--- a/servo/components/style/properties/longhand/pointing.mako.rs
+++ b/servo/components/style/properties/longhand/pointing.mako.rs
@@ -87,23 +87,24 @@
         }
     }
 
     impl Parse for computed_value::Keyword {
         fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                          -> Result<computed_value::Keyword, ParseError<'i>> {
             use std::ascii::AsciiExt;
             use style_traits::cursor::Cursor;
+            let location = input.current_source_location();
             let ident = input.expect_ident()?;
             if ident.eq_ignore_ascii_case("auto") {
                 Ok(computed_value::Keyword::Auto)
             } else {
                 Cursor::from_css_keyword(&ident)
                     .map(computed_value::Keyword::Cursor)
-                    .map_err(|()| SelectorParseError::UnexpectedIdent(ident.clone()).into())
+                    .map_err(|()| location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())))
             }
         }
     }
 
     #[cfg(feature = "gecko")]
     fn parse_image<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
                            -> Result<computed_value::Image, ParseError<'i>> {
         Ok(computed_value::Image {
--- a/servo/components/style/properties/longhand/position.mako.rs
+++ b/servo/components/style/properties/longhand/position.mako.rs
@@ -335,16 +335,17 @@ macro_rules! impl_align_conversions {
     pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                          -> Result<SpecifiedValue, ParseError<'i>> {
         use self::computed_value::AutoFlow;
 
         let mut value = None;
         let mut dense = false;
 
         while !input.is_exhausted() {
+            let location = input.current_source_location();
             let ident = input.expect_ident()?;
             let success = match_ignore_ascii_case! { &ident,
                 "row" if value.is_none() => {
                     value = Some(AutoFlow::Row);
                     true
                 },
                 "column" if value.is_none() => {
                     value = Some(AutoFlow::Column);
@@ -352,27 +353,27 @@ macro_rules! impl_align_conversions {
                 },
                 "dense" if !dense => {
                     dense = true;
                     true
                 },
                 _ => false
             };
             if !success {
-                return Err(SelectorParseError::UnexpectedIdent(ident.clone()).into());
+                return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())));
             }
         }
 
         if value.is_some() || dense {
             Ok(computed_value::T {
                 autoflow: value.unwrap_or(AutoFlow::Row),
                 dense: dense,
             })
         } else {
-            Err(StyleParseError::UnspecifiedError.into())
+            Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         }
     }
 
     #[cfg(feature = "gecko")]
     impl From<u8> for SpecifiedValue {
         fn from(bits: u8) -> SpecifiedValue {
             use gecko_bindings::structs;
             use self::computed_value::AutoFlow;
@@ -460,17 +461,17 @@ macro_rules! impl_align_conversions {
             input: &mut Parser<'i, 't>,
         ) -> Result<Self, ParseError<'i>> {
             let mut strings = vec![];
             while let Ok(string) = input.try(|i| i.expect_string().map(|s| s.as_ref().into())) {
                 strings.push(string);
             }
 
             TemplateAreas::from_vec(strings)
-                .map_err(|()| StyleParseError::UnspecifiedError.into())
+                .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         }
     }
 
     impl TemplateAreas {
         pub fn from_vec(strings: Vec<Box<str>>) -> Result<TemplateAreas, ()> {
             if strings.is_empty() {
                 return Err(());
             }
--- a/servo/components/style/properties/longhand/table.mako.rs
+++ b/servo/components/style/properties/longhand/table.mako.rs
@@ -34,13 +34,13 @@
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         computed_value::T(1)
     }
 
     // never parse it, only set via presentation attribute
     fn parse<'i, 't>(
         _: &ParserContext,
-        _: &mut Parser<'i, 't>,
+        input: &mut Parser<'i, 't>,
     ) -> Result<SpecifiedValue, ParseError<'i>> {
-        Err(StyleParseError::UnspecifiedError.into())
+        Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     }
 </%helpers:longhand>
--- a/servo/components/style/properties/longhand/text.mako.rs
+++ b/servo/components/style/properties/longhand/text.mako.rs
@@ -114,27 +114,31 @@
         Ok(SpecifiedValue {
             first: first,
             second: second,
         })
     }
     impl Parse for Side {
         fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                          -> Result<Side, ParseError<'i>> {
+            let location = input.current_source_location();
             match *input.next()? {
                 Token::Ident(ref ident) => {
-                    try_match_ident_ignore_ascii_case! { ident,
+                    match_ignore_ascii_case! { ident,
                         "clip" => Ok(Side::Clip),
                         "ellipsis" => Ok(Side::Ellipsis),
+                        _ => Err(location.new_custom_error(
+                            SelectorParseErrorKind::UnexpectedIdent(ident.clone())
+                        ))
                     }
                 }
                 Token::QuotedString(ref v) => {
                     Ok(Side::String(v.as_ref().to_owned().into_boxed_str()))
                 }
-                ref t => Err(BasicParseError::UnexpectedToken(t.clone()).into()),
+                ref t => Err(location.new_unexpected_token_error(t.clone())),
             }
         }
     }
 </%helpers:longhand>
 
 ${helpers.single_keyword("unicode-bidi",
                          "normal embed isolate bidi-override isolate-override plaintext",
                          animation_value_type="discrete",
@@ -217,39 +221,42 @@
         let mut result = SpecifiedValue::empty();
         if input.try(|input| input.expect_ident_matching("none")).is_ok() {
             return Ok(result)
         }
         let mut empty = true;
 
         loop {
             let result: Result<_, ParseError> = input.try(|input| {
+                let location = input.current_source_location();
                 match input.expect_ident() {
                     Ok(ident) => {
                         (match_ignore_ascii_case! { &ident,
                             "underline" => if result.contains(UNDERLINE) { Err(()) }
                                            else { empty = false; result.insert(UNDERLINE); Ok(()) },
                             "overline" => if result.contains(OVERLINE) { Err(()) }
                                           else { empty = false; result.insert(OVERLINE); Ok(()) },
                             "line-through" => if result.contains(LINE_THROUGH) { Err(()) }
                                               else { empty = false; result.insert(LINE_THROUGH); Ok(()) },
                             "blink" => if result.contains(BLINK) { Err(()) }
                                        else { empty = false; result.insert(BLINK); Ok(()) },
                             _ => Err(())
-                        }).map_err(|()| SelectorParseError::UnexpectedIdent(ident.clone()).into())
+                        }).map_err(|()| {
+                            location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone()))
+                        })
                     }
                     Err(e) => return Err(e.into())
                 }
             });
             if result.is_err() {
                 break;
             }
         }
 
-        if !empty { Ok(result) } else { Err(StyleParseError::UnspecifiedError.into()) }
+        if !empty { Ok(result) } else { Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) }
     }
 
     % if product == "servo":
         fn cascade_property_custom(_declaration: &PropertyDeclaration,
                                    context: &mut computed::Context) {
             longhands::_servo_text_decorations_in_effect::derive_from_text_decoration(context);
         }
     % endif
--- a/servo/components/style/properties/longhand/ui.mako.rs
+++ b/servo/components/style/properties/longhand/ui.mako.rs
@@ -72,17 +72,17 @@
         computed_value::T(false)
     }
 
     pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                          -> Result<SpecifiedValue, ParseError<'i>> {
         match input.expect_integer()? {
             0 => Ok(computed_value::T(false)),
             1 => Ok(computed_value::T(true)),
-            _ => Err(StyleParseError::UnspecifiedError.into()),
+            _ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
         }
     }
 
     impl From<u8> for SpecifiedValue {
         fn from(bits: u8) -> SpecifiedValue {
             SpecifiedValue(bits == 1)
         }
     }
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -30,21 +30,20 @@ use font_metrics::FontMetricsProvider;
 #[cfg(feature = "gecko")] use gecko_bindings::structs::{self, nsCSSPropertyID};
 #[cfg(feature = "servo")] use logical_geometry::LogicalMargin;
 use logical_geometry::WritingMode;
 use media_queries::Device;
 use parser::ParserContext;
 #[cfg(feature = "gecko")] use properties::longhands::system_font::SystemFont;
 use rule_cache::{RuleCache, RuleCacheConditions};
 use selector_parser::PseudoElement;
-use selectors::parser::SelectorParseError;
+use selectors::parser::SelectorParseErrorKind;
 #[cfg(feature = "servo")] use servo_config::prefs::PREFS;
 use shared_lock::StylesheetGuards;
-use style_traits::{PARSING_MODE_DEFAULT, ToCss, ParseError};
-use style_traits::{PropertyDeclarationParseError, StyleParseError, ValueParseError};
+use style_traits::{PARSING_MODE_DEFAULT, ToCss, ParseError, StyleParseErrorKind};
 use stylesheets::{CssRuleType, Origin, UrlExtraData};
 #[cfg(feature = "servo")] use values::Either;
 use values::generics::text::LineHeight;
 use values::computed;
 use values::computed::NonNegativeLength;
 use rule_tree::{CascadeLevel, StrongRuleNode};
 use self::computed_value_flags::*;
 use style_adjuster::StyleAdjuster;
@@ -148,17 +147,17 @@ macro_rules! unwrap_or_initial {
 }
 
 /// A module with code for all the shorthand css properties, and a few
 /// serialization helpers.
 #[allow(missing_docs)]
 pub mod shorthands {
     use cssparser::Parser;
     use parser::{Parse, ParserContext};
-    use style_traits::{ParseError, StyleParseError};
+    use style_traits::{ParseError, StyleParseErrorKind};
     use values::specified;
 
     <%include file="/shorthand/serialize.mako.rs" />
     <%include file="/shorthand/background.mako.rs" />
     <%include file="/shorthand/border.mako.rs" />
     <%include file="/shorthand/box.mako.rs" />
     <%include file="/shorthand/column.mako.rs" />
     <%include file="/shorthand/font.mako.rs" />
@@ -548,17 +547,19 @@ impl LonghandId {
     fn parse_value<'i, 't>(&self, context: &ParserContext, input: &mut Parser<'i, 't>)
                            -> Result<PropertyDeclaration, ParseError<'i>> {
         match *self {
             % for property in data.longhands:
                 LonghandId::${property.camel_case} => {
                     % if not property.derived_from:
                         longhands::${property.ident}::parse_declared(context, input)
                     % else:
-                        Err(PropertyDeclarationParseError::UnknownProperty("${property.ident}".into()).into())
+                        Err(input.new_custom_error(
+                            StyleParseErrorKind::UnknownProperty("${property.ident}".into())
+                        ))
                     % endif
                 }
             % endfor
         }
     }
 
     /// Returns whether this property is animatable.
     pub fn is_animatable(self) -> bool {
@@ -873,17 +874,17 @@ impl ShorthandId {
                           -> Result<(), ParseError<'i>> {
         match *self {
             % for shorthand in data.shorthands_except_all():
                 ShorthandId::${shorthand.camel_case} => {
                     shorthands::${shorthand.ident}::parse_into(declarations, context, input)
                 }
             % endfor
             // 'all' accepts no value other than CSS-wide keywords
-            ShorthandId::All => Err(StyleParseError::UnspecifiedError.into())
+            ShorthandId::All => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         }
     }
 }
 
 /// Servo's representation of a declared value for a given `T`, which is the
 /// declared value for that property.
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub enum DeclaredValue<'a, T: 'a> {
@@ -957,17 +958,17 @@ impl UnparsedValue {
             );
             let mut input = ParserInput::new(&css);
             Parser::new(&mut input).parse_entirely(|input| {
                 match self.from_shorthand {
                     None => longhand_id.parse_value(&context, input),
                     Some(ShorthandId::All) => {
                         // No need to parse the 'all' shorthand as anything other than a CSS-wide
                         // keyword, after variable substitution.
-                        Err(SelectorParseError::UnexpectedIdent("all".into()).into())
+                        Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent("all".into())))
                     }
                     % for shorthand in data.shorthands_except_all():
                         Some(ShorthandId::${shorthand.camel_case}) => {
                             shorthands::${shorthand.ident}::parse_value(&context, input)
                             .map(|longhands| {
                                 match longhand_id {
                                     % for property in shorthand.sub_properties:
                                         LonghandId::${property.camel_case} => {
@@ -1625,30 +1626,29 @@ impl PropertyDeclaration {
     /// > but does accept the `animation-play-state` property and interprets it specially.
     ///
     /// This will not actually parse Importance values, and will always set things
     /// to Importance::Normal. Parsing Importance values is the job of PropertyDeclarationParser,
     /// we only set them here so that we don't have to reallocate
     pub fn parse_into<'i, 't>(declarations: &mut SourcePropertyDeclaration,
                               id: PropertyId, name: CowRcStr<'i>,
                               context: &ParserContext, input: &mut Parser<'i, 't>)
-                              -> Result<(), PropertyDeclarationParseError<'i>> {
+                              -> Result<(), ParseError<'i>> {
         assert!(declarations.is_empty());
         let start = input.state();
         match id {
             PropertyId::Custom(property_name) => {
                 // FIXME: fully implement https://github.com/w3c/csswg-drafts/issues/774
                 // before adding skip_whitespace here.
                 // This probably affects some test results.
                 let value = match input.try(|i| CSSWideKeyword::parse(i)) {
                     Ok(keyword) => DeclaredValueOwned::CSSWideKeyword(keyword),
                     Err(()) => match ::custom_properties::SpecifiedValue::parse(input) {
                         Ok(value) => DeclaredValueOwned::Value(value),
-                        Err(e) => return Err(PropertyDeclarationParseError::InvalidValue(name.to_string().into(),
-                        ValueParseError::from_parse_error(e))),
+                        Err(e) => return Err(StyleParseErrorKind::new_invalid(name, e)),
                     }
                 };
                 declarations.push(PropertyDeclaration::Custom(property_name, value));
                 Ok(())
             }
             PropertyId::Longhand(id) => {
                 input.skip_whitespace();  // Unnecessary for correctness, but may help try() rewind less.
                 input.try(|i| CSSWideKeyword::parse(i)).map(|keyword| {
@@ -1657,28 +1657,26 @@ impl PropertyDeclaration {
                     input.look_for_var_functions();
                     input.parse_entirely(|input| id.parse_value(context, input))
                     .or_else(|err| {
                         while let Ok(_) = input.next() {}  // Look for var() after the error.
                         if input.seen_var_functions() {
                             input.reset(&start);
                             let (first_token_type, css) =
                                 ::custom_properties::parse_non_custom_with_var(input).map_err(|e| {
-                                    PropertyDeclarationParseError::InvalidValue(name,
-                                        ValueParseError::from_parse_error(e))
+                                    StyleParseErrorKind::new_invalid(name, e)
                                 })?;
                             Ok(PropertyDeclaration::WithVariables(id, Arc::new(UnparsedValue {
                                 css: css.into_owned(),
                                 first_token_type: first_token_type,
                                 url_data: context.url_data.clone(),
                                 from_shorthand: None,
                             })))
                         } else {
-                            Err(PropertyDeclarationParseError::InvalidValue(name,
-                                ValueParseError::from_parse_error(err)))
+                            Err(StyleParseErrorKind::new_invalid(name, err))
                         }
                     })
                 }).map(|declaration| {
                     declarations.push(declaration)
                 })
             }
             PropertyId::Shorthand(id) => {
                 input.skip_whitespace();  // Unnecessary for correctness, but may help try() rewind less.
@@ -1696,18 +1694,17 @@ impl PropertyDeclaration {
                     // Not using parse_entirely here: each ${shorthand.ident}::parse_into function
                     // needs to do so *before* pushing to `declarations`.
                     id.parse_into(declarations, context, input).or_else(|err| {
                         while let Ok(_) = input.next() {}  // Look for var() after the error.
                         if input.seen_var_functions() {
                             input.reset(&start);
                             let (first_token_type, css) =
                                 ::custom_properties::parse_non_custom_with_var(input).map_err(|e| {
-                                    PropertyDeclarationParseError::InvalidValue(name,
-                                        ValueParseError::from_parse_error(e))
+                                    StyleParseErrorKind::new_invalid(name, e)
                                 })?;
                             let unparsed = Arc::new(UnparsedValue {
                                 css: css.into_owned(),
                                 first_token_type: first_token_type,
                                 url_data: context.url_data.clone(),
                                 from_shorthand: Some(id),
                             });
                             if id == ShorthandId::All {
@@ -1716,18 +1713,17 @@ impl PropertyDeclaration {
                                 for &longhand in id.longhands() {
                                     declarations.push(
                                         PropertyDeclaration::WithVariables(longhand, unparsed.clone())
                                     )
                                 }
                             }
                             Ok(())
                         } else {
-                            Err(PropertyDeclarationParseError::InvalidValue(name,
-                                ValueParseError::from_parse_error(err)))
+                            Err(StyleParseErrorKind::new_invalid(name, err))
                         }
                     })
                 }
             }
         }
     }
 }
 
--- a/servo/components/style/properties/shorthand/background.mako.rs
+++ b/servo/components/style/properties/shorthand/background.mako.rs
@@ -42,17 +42,17 @@
             // way overallocate.  Note that we always push at least one item if
             // parsing succeeds.
             let mut background_${name} = background_${name}::SpecifiedValue(Vec::with_capacity(1));
         % endfor
         input.parse_comma_separated(|input| {
             // background-color can only be in the last element, so if it
             // is parsed anywhere before, the value is invalid.
             if background_color.is_some() {
-                return Err(StyleParseError::UnspecifiedError.into());
+                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
             }
 
             % for name in "image position repeat size attachment origin clip".split():
                 let mut ${name} = None;
             % endfor
             loop {
                 if background_color.is_none() {
                     if let Ok(value) = input.try(|i| Color::parse(context, i)) {
@@ -107,17 +107,17 @@
                         background_${name}.0.push(bg_${name});
                     } else {
                         background_${name}.0.push(background_${name}::single_value
                                                                     ::get_initial_specified_value());
                     }
                 % endfor
                 Ok(())
             } else {
-                Err(StyleParseError::UnspecifiedError.into())
+                Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
             }
         })?;
 
         Ok(expanded! {
              background_color: background_color.unwrap_or(Color::transparent()),
              background_image: background_image,
              background_position_x: background_position_x,
              background_position_y: background_position_y,
@@ -212,17 +212,17 @@
         input.parse_comma_separated(|input| {
             let value = Position::parse_quirky(context, input, AllowQuirks::Yes)?;
             position_x.0.push(value.horizontal);
             position_y.0.push(value.vertical);
             any = true;
             Ok(())
         })?;
         if !any {
-            return Err(StyleParseError::UnspecifiedError.into());
+            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
         }
 
         Ok(expanded! {
             background_position_x: position_x,
             background_position_y: position_y,
         })
     }
 
--- a/servo/components/style/properties/shorthand/border.mako.rs
+++ b/servo/components/style/properties/shorthand/border.mako.rs
@@ -78,17 +78,17 @@ pub fn parse_border<'i, 't>(context: &Pa
         }
         break
     }
     if any {
         Ok((color.unwrap_or_else(|| Color::currentcolor()),
             style.unwrap_or(BorderStyle::none),
             width.unwrap_or(BorderSideWidth::Medium)))
     } else {
-        Err(StyleParseError::UnspecifiedError.into())
+        Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     }
 }
 
 % for side, logical in ALL_SIDES:
     <%
         spec = "https://drafts.csswg.org/css-backgrounds/#border-%s" % side
         if logical:
             spec = "https://drafts.csswg.org/css-logical-props/#propdef-border-%s" % side
@@ -271,17 +271,17 @@ pub fn parse_border<'i, 't>(context: &Pa
                                 border_image_width::parse(context, input)).ok();
 
                             // Parse border image outset if applicable.
                             let o = input.try(|input| {
                                 input.expect_delim('/')?;
                                 border_image_outset::parse(context, input)
                             }).ok();
                             if w.is_none() && o.is_none() {
-                               Err(StyleParseError::UnspecifiedError.into())
+                               Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
                             }
                             else {
                                Ok((w, o))
                             }
                         });
                         if let Ok((w, o)) = maybe_width_outset {
                             width = w;
                             outset = o;
@@ -307,17 +307,17 @@ pub fn parse_border<'i, 't>(context: &Pa
             if any {
                 % for name in "outset repeat slice source width".split():
                     if let Some(b_${name}) = ${name} {
                         border_image_${name} = b_${name};
                     }
                 % endfor
                 Ok(())
             } else {
-                Err(StyleParseError::UnspecifiedError.into())
+                Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
             }
         });
         result?;
 
         Ok(expanded! {
             % for name in "outset repeat slice source width".split():
                 border_image_${name}: border_image_${name},
             % endfor
--- a/servo/components/style/properties/shorthand/box.mako.rs
+++ b/servo/components/style/properties/shorthand/box.mako.rs
@@ -9,18 +9,18 @@
     use properties::longhands::overflow_x::parse as parse_overflow;
     % if product == "gecko":
         use properties::longhands::overflow_x::SpecifiedValue;
     % endif
 
     pub fn parse_value<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
                                -> Result<Longhands, ParseError<'i>> {
         % if product == "gecko":
-            let moz_kw_found = input.try(|i| {
-                try_match_ident_ignore_ascii_case! { i.expect_ident()?,
+            let moz_kw_found = input.try(|input| {
+                try_match_ident_ignore_ascii_case! { input,
                     "-moz-scrollbars-horizontal" => {
                         Ok(expanded! {
                             overflow_x: SpecifiedValue::scroll,
                             overflow_y: SpecifiedValue::hidden,
                         })
                     }
                     "-moz-scrollbars-vertical" => {
                         Ok(expanded! {
@@ -136,34 +136,34 @@ macro_rules! try_parse_one {
                     % for prop in "duration timing_function delay".split():
                     transition_${prop}: ${prop}.unwrap_or_else(transition_${prop}::single_value
                                                                                  ::get_initial_specified_value),
                     % endfor
                     transition_property: property.unwrap_or(
                         Some(transition_property::single_value::get_initial_specified_value())),
                 })
             } else {
-                Err(StyleParseError::UnspecifiedError.into())
+                Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
             }
         }
 
         % for prop in "property duration timing_function delay".split():
         let mut ${prop}s = Vec::new();
         % endfor
 
         let results = input.parse_comma_separated(|i| parse_one_transition(context, i))?;
         let multiple_items = results.len() >= 2;
         for result in results {
             if let Some(value) = result.transition_property {
                 propertys.push(value);
             } else if multiple_items {
                 // If there is more than one item, and any of transitions has 'none',
                 // then it's invalid. Othersize, leave propertys to be empty (which
                 // means "transition-property: none");
-                return Err(StyleParseError::UnspecifiedError.into());
+                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
             }
 
             % for prop in "duration timing_function delay".split():
             ${prop}s.push(result.transition_${prop});
             % endfor
         }
 
         Ok(expanded! {
@@ -264,17 +264,17 @@ macro_rules! try_parse_one {
                 try_parse_one!(context, input, name, animation_name);
 
                 parsed -= 1;
                 break
             }
 
             // If nothing is parsed, this is an invalid entry.
             if parsed == 0 {
-                Err(StyleParseError::UnspecifiedError.into())
+                Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
             } else {
                 Ok(SingleAnimation {
                     % for prop in props:
                     animation_${prop}: ${prop}.unwrap_or_else(animation_${prop}::single_value
                                                               ::get_initial_specified_value),
                     % endfor
                 })
             }
--- a/servo/components/style/properties/shorthand/column.mako.rs
+++ b/servo/components/style/properties/shorthand/column.mako.rs
@@ -39,17 +39,17 @@
                 }
             }
 
             break
         }
 
         let values = autos + column_count.iter().len() + column_width.iter().len();
         if values == 0 || values > 2 {
-            Err(StyleParseError::UnspecifiedError.into())
+            Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         } else {
             Ok(expanded! {
                 column_count: unwrap_or_initial!(column_count),
                 column_width: unwrap_or_initial!(column_width),
             })
         }
     }
 </%helpers:shorthand>
@@ -84,12 +84,12 @@
         }
         if any {
             Ok(expanded! {
                 column_rule_width: unwrap_or_initial!(column_rule_width),
                 column_rule_style: unwrap_or_initial!(column_rule_style),
                 column_rule_color: unwrap_or_initial!(column_rule_color),
             })
         } else {
-            Err(StyleParseError::UnspecifiedError.into())
+            Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         }
     }
 </%helpers:shorthand>
--- a/servo/components/style/properties/shorthand/font.mako.rs
+++ b/servo/components/style/properties/shorthand/font.mako.rs
@@ -93,17 +93,17 @@
             break
         }
         #[inline]
         fn count<T>(opt: &Option<T>) -> u8 {
             if opt.is_some() { 1 } else { 0 }
         }
         if size.is_none() ||
            (count(&style) + count(&weight) + count(&variant_caps) + count(&stretch) + nb_normals) > 4 {
-            return Err(StyleParseError::UnspecifiedError.into())
+            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         }
         let line_height = if input.try(|input| input.expect_delim('/')).is_ok() {
             Some(LineHeight::parse(context, input)?)
         } else {
             None
         };
         let family = FontFamily::parse(input)?;
         Ok(expanded! {
@@ -257,33 +257,33 @@
         % if product == "gecko":
             ligatures = Some(font_variant_ligatures::get_none_specified_value());
         % endif
         } else {
             let mut has_custom_value: bool = false;
             loop {
                 if input.try(|input| input.expect_ident_matching("normal")).is_ok() ||
                    input.try(|input| input.expect_ident_matching("none")).is_ok() {
-                    return Err(StyleParseError::UnspecifiedError.into())
+                    return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
                 }
             % for prop in sub_properties:
                 if ${prop}.is_none() {
                     if let Ok(value) = input.try(|i| font_variant_${prop}::parse(context, i)) {
                         has_custom_value = true;
                         ${prop} = Some(value);
                         continue
                     }
                 }
             % endfor
 
                 break
             }
 
             if !has_custom_value {
-                return Err(StyleParseError::UnspecifiedError.into())
+                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
             }
         }
 
         Ok(expanded! {
         % for prop in sub_properties:
             font_variant_${prop}: unwrap_or_initial!(font_variant_${prop}, ${prop}),
         % endfor
         })
--- a/servo/components/style/properties/shorthand/inherited_text.mako.rs
+++ b/servo/components/style/properties/shorthand/inherited_text.mako.rs
@@ -31,17 +31,17 @@
             break
         }
         if color.is_some() || style.is_some() {
             Ok(expanded! {
                 text_emphasis_color: unwrap_or_initial!(text_emphasis_color, color),
                 text_emphasis_style: unwrap_or_initial!(text_emphasis_style, style),
             })
         } else {
-            Err(StyleParseError::UnspecifiedError.into())
+            Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         }
     }
 </%helpers:shorthand>
 
 // CSS Compatibility
 // https://compat.spec.whatwg.org/
 <%helpers:shorthand name="-webkit-text-stroke"
                     sub_properties="-webkit-text-stroke-width
@@ -73,12 +73,12 @@
         }
 
         if color.is_some() || width.is_some() {
             Ok(expanded! {
                 _webkit_text_stroke_color: unwrap_or_initial!(_webkit_text_stroke_color, color),
                 _webkit_text_stroke_width: unwrap_or_initial!(_webkit_text_stroke_width, width),
             })
         } else {
-            Err(StyleParseError::UnspecifiedError.into())
+            Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         }
     }
 </%helpers:shorthand>
--- a/servo/components/style/properties/shorthand/list.mako.rs
+++ b/servo/components/style/properties/shorthand/list.mako.rs
@@ -16,17 +16,17 @@
         // `none` is ambiguous until we've finished parsing the shorthands, so we count the number
         // of times we see it.
         let mut nones = 0u8;
         let (mut image, mut position, mut list_style_type, mut any) = (None, None, None, false);
         loop {
             if input.try(|input| input.expect_ident_matching("none")).is_ok() {
                 nones = nones + 1;
                 if nones > 2 {
-                    return Err(SelectorParseError::UnexpectedIdent("none".into()).into())
+                    return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent("none".into())))
                 }
                 any = true;
                 continue
             }
 
             if image.is_none() {
                 if let Ok(value) = input.try(|input| list_style_image::parse(context, input)) {
                     image = Some(value);
@@ -101,12 +101,12 @@
             }
             (true, 0, list_style_type, image) => {
                 Ok(expanded! {
                     list_style_position: position,
                     list_style_image: unwrap_or_initial!(list_style_image, image),
                     list_style_type: unwrap_or_initial!(list_style_type),
                 })
             }
-            _ => Err(StyleParseError::UnspecifiedError.into()),
+            _ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
         }
     }
 </%helpers:shorthand>
--- a/servo/components/style/properties/shorthand/mask.mako.rs
+++ b/servo/components/style/properties/shorthand/mask.mako.rs
@@ -103,17 +103,17 @@
                         mask_${name}.0.push(m_${name});
                     } else {
                         mask_${name}.0.push(mask_${name}::single_value
                                                         ::get_initial_specified_value());
                     }
                 % endfor
                 Ok(())
             } else {
-                Err(StyleParseError::UnspecifiedError.into())
+                Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
             }
         })?;
 
         Ok(expanded! {
             % for name in "image mode position_x position_y size repeat origin clip composite".split():
                 mask_${name}: mask_${name},
             % endfor
          })
@@ -198,17 +198,17 @@
         input.parse_comma_separated(|input| {
             let value = Position::parse(context, input)?;
             position_x.0.push(value.horizontal);
             position_y.0.push(value.vertical);
             any = true;
             Ok(())
         })?;
         if any == false {
-            return Err(StyleParseError::UnspecifiedError.into());
+            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
         }
 
         Ok(expanded! {
             mask_position_x: position_x,
             mask_position_y: position_y,
         })
     }
 
--- a/servo/components/style/properties/shorthand/outline.mako.rs
+++ b/servo/components/style/properties/shorthand/outline.mako.rs
@@ -45,17 +45,17 @@
         }
         if any {
             Ok(expanded! {
                 outline_color: unwrap_or_initial!(outline_color, color),
                 outline_style: unwrap_or_initial!(outline_style, style),
                 outline_width: unwrap_or_initial!(outline_width, width),
             })
         } else {
-            Err(StyleParseError::UnspecifiedError.into())
+            Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         }
     }
 </%helpers:shorthand>
 
 // The -moz-outline-radius shorthand is non-standard and not on a standards track.
 <%helpers:shorthand name="-moz-outline-radius" sub_properties="${' '.join(
     '-moz-outline-radius-%s' % corner
     for corner in ['topleft', 'topright', 'bottomright', 'bottomleft']
--- a/servo/components/style/properties/shorthand/position.mako.rs
+++ b/servo/components/style/properties/shorthand/position.mako.rs
@@ -27,17 +27,17 @@
                     wrap = Some(value);
                     continue
                 }
             }
             break
         }
 
         if direction.is_none() && wrap.is_none() {
-            return Err(StyleParseError::UnspecifiedError.into())
+            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         }
         Ok(expanded! {
             flex_direction: unwrap_or_initial!(flex_direction, direction),
             flex_wrap: unwrap_or_initial!(flex_wrap, wrap),
         })
     }
 </%helpers:shorthand>
 
@@ -82,17 +82,17 @@
                     basis = Some(value);
                     continue
                 }
             }
             break
         }
 
         if grow.is_none() && basis.is_none() {
-            return Err(StyleParseError::UnspecifiedError.into())
+            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         }
         Ok(expanded! {
             flex_grow: grow.unwrap_or(NonNegativeNumber::new(1.0)),
             flex_shrink: shrink.unwrap_or(NonNegativeNumber::new(1.0)),
             // Per spec, this should be SpecifiedValue::zero(), but all
             // browsers currently agree on using `0%`. This is a spec
             // change which hasn't been adopted by browsers:
             // https://github.com/w3c/csswg-drafts/commit/2c446befdf0f686217905bdd7c92409f6bd3921b
@@ -304,29 +304,29 @@
             }
 
             if line_names.len() == values.len() {
                 // should be one longer than track sizes
                 line_names.push(vec![].into_boxed_slice());
             }
 
             let template_areas = TemplateAreas::from_vec(strings)
-                .map_err(|()| StyleParseError::UnspecifiedError)?;
+                .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))?;
             let template_rows = TrackList {
                 list_type: TrackListType::Normal,
                 values: values,
                 line_names: line_names.into_boxed_slice(),
                 auto_repeat: None,
             };
 
             let template_cols = if input.try(|i| i.expect_delim('/')).is_ok() {
                 let value = GridTemplateComponent::parse_without_none(context, input)?;
                 if let GenericGridTemplateComponent::TrackList(ref list) = value {
                     if list.list_type != TrackListType::Explicit {
-                        return Err(StyleParseError::UnspecifiedError.into())
+                        return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
                     }
                 }
 
                 value
             } else {
                 GenericGridTemplateComponent::None
             };
 
@@ -335,17 +335,17 @@
         } else {
             let mut template_rows = GridTemplateComponent::parse(context, input)?;
             if let GenericGridTemplateComponent::TrackList(ref mut list) = template_rows {
                 // Fist line names are parsed already and it shouldn't be parsed again.
                 // If line names are not empty, that means given property value is not acceptable
                 if list.line_names[0].is_empty() {
                     list.line_names[0] = first_line_names;      // won't panic
                 } else {
-                    return Err(StyleParseError::UnspecifiedError.into());
+                    return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
                 }
             }
 
             input.expect_delim('/')?;
             Ok((template_rows, GridTemplateComponent::parse(context, input)?, Either::Second(None_)))
         }
     }
 
@@ -496,17 +496,17 @@
                 }
             }
 
             auto_flow.map(|flow| {
                 SpecifiedAutoFlow {
                     autoflow: flow,
                     dense: dense,
                 }
-            }).ok_or(StyleParseError::UnspecifiedError.into())
+            }).ok_or(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         }
 
         if let Ok((rows, cols, areas)) = input.try(|i| super::grid_template::parse_grid_template(context, i)) {
             temp_rows = rows;
             temp_cols = cols;
             temp_areas = areas;
         } else if let Ok(rows) = input.try(|i| GridTemplateComponent::parse(context, i)) {
             temp_rows = rows;
@@ -612,22 +612,22 @@
                     products="gecko">
     use properties::longhands::align_content;
     use properties::longhands::justify_content;
 
     pub fn parse_value<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
                                -> Result<Longhands, ParseError<'i>> {
         let align = align_content::parse(context, input)?;
         if align.has_extra_flags() {
-            return Err(StyleParseError::UnspecifiedError.into());
+            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
         }
         let justify = input.try(|input| justify_content::parse(context, input))
                            .unwrap_or(justify_content::SpecifiedValue::from(align));
         if justify.has_extra_flags() {
-            return Err(StyleParseError::UnspecifiedError.into());
+            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
         }
 
         Ok(expanded! {
             align_content: align,
             justify_content: justify,
         })
     }
 
@@ -648,21 +648,21 @@
                     products="gecko">
     use values::specified::align::AlignJustifySelf;
     use parser::Parse;
 
     pub fn parse_value<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
                                -> Result<Longhands, ParseError<'i>> {
         let align = AlignJustifySelf::parse(context, input)?;
         if align.has_extra_flags() {
-            return Err(StyleParseError::UnspecifiedError.into());
+            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
         }
         let justify = input.try(|input| AlignJustifySelf::parse(context, input)).unwrap_or(align.clone());
         if justify.has_extra_flags() {
-            return Err(StyleParseError::UnspecifiedError.into());
+            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
         }
 
         Ok(expanded! {
             align_self: align,
             justify_self: justify,
         })
     }
 
@@ -690,22 +690,22 @@
             JustifyItems(align.0)
         }
     }
 
     pub fn parse_value<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
                                -> Result<Longhands, ParseError<'i>> {
         let align = AlignItems::parse(context, input)?;
         if align.has_extra_flags() {
-            return Err(StyleParseError::UnspecifiedError.into());
+            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
         }
         let justify = input.try(|input| JustifyItems::parse(context, input))
                            .unwrap_or(JustifyItems::from(align));
         if justify.has_extra_flags() {
-            return Err(StyleParseError::UnspecifiedError.into());
+            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
         }
 
         Ok(expanded! {
             align_items: align,
             justify_items: justify,
         })
     }
 
--- a/servo/components/style/properties/shorthand/text.mako.rs
+++ b/servo/components/style/properties/shorthand/text.mako.rs
@@ -43,17 +43,17 @@
                 parse_component!(style, text_decoration_style);
                 parse_component!(color, text_decoration_color);
             % endif
 
             break;
         }
 
         if !any {
-            return Err(StyleParseError::UnspecifiedError.into());
+            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
         }
 
         Ok(expanded! {
             text_decoration_line: unwrap_or_initial!(text_decoration_line, line),
 
             % if product == "gecko":
                 text_decoration_style: unwrap_or_initial!(text_decoration_style, style),
                 text_decoration_color: unwrap_or_initial!(text_decoration_color, color),
--- a/servo/components/style/servo/media_queries.rs
+++ b/servo/components/style/servo/media_queries.rs
@@ -9,17 +9,17 @@ use context::QuirksMode;
 use cssparser::{Parser, RGBA};
 use euclid::{ScaleFactor, Size2D, TypedSize2D};
 use font_metrics::ServoMetricsProvider;
 use media_queries::MediaType;
 use parser::ParserContext;
 use properties::{ComputedValues, StyleBuilder};
 use properties::longhands::font_size;
 use rule_cache::RuleCacheConditions;
-use selectors::parser::SelectorParseError;
+use selectors::parser::SelectorParseErrorKind;
 use std::cell::RefCell;
 use std::fmt;
 use std::sync::atomic::{AtomicBool, AtomicIsize, Ordering};
 use style_traits::{CSSPixel, DevicePixel, ToCss, ParseError};
 use style_traits::viewport::ViewportConstraints;
 use values::computed::{self, ToComputedValue};
 use values::specified;
 
@@ -193,17 +193,17 @@ impl Expression {
                     ExpressionKind::Width(Range::Min(specified::Length::parse_non_negative(context, input)?))
                 },
                 "max-width" => {
                     ExpressionKind::Width(Range::Max(specified::Length::parse_non_negative(context, input)?))
                 },
                 "width" => {
                     ExpressionKind::Width(Range::Eq(specified::Length::parse_non_negative(context, input)?))
                 },
-                _ => return Err(SelectorParseError::UnexpectedIdent(name.clone()).into())
+                _ => return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
             }))
         })
     }
 
     /// Evaluate this expression and return whether it matches the current
     /// device.
     pub fn matches(&self, device: &Device, quirks_mode: QuirksMode) -> bool {
         let viewport_size = device.au_viewport_size();
--- a/servo/components/style/servo/selector_parser.rs
+++ b/servo/components/style/servo/selector_parser.rs
@@ -3,35 +3,35 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #![deny(missing_docs)]
 
 //! Servo's selector parser.
 
 use {Atom, Prefix, Namespace, LocalName, CaseSensitivityExt};
 use attr::{AttrIdentifier, AttrValue};
-use cssparser::{Parser as CssParser, ToCss, serialize_identifier, CowRcStr};
+use cssparser::{Parser as CssParser, ToCss, serialize_identifier, CowRcStr, SourceLocation};
 use dom::{OpaqueNode, TElement, TNode};
 use element_state::ElementState;
 use fnv::FnvHashMap;
 use invalidation::element::element_wrapper::ElementSnapshot;
 use properties::ComputedValues;
 use properties::PropertyFlags;
 use properties::longhands::display::computed_value as display;
 use selector_parser::{AttrValue as SelectorAttrValue, ElementExt, PseudoElementCascadeType, SelectorParser};
 use selectors::Element;
 use selectors::attr::{AttrSelectorOperation, NamespaceConstraint, CaseSensitivity};
-use selectors::parser::{SelectorMethods, SelectorParseError};
+use selectors::parser::{SelectorMethods, SelectorParseErrorKind};
 use selectors::visitor::SelectorVisitor;
 use std::ascii::AsciiExt;
 use std::fmt;
 use std::fmt::Debug;
 use std::mem;
 use std::ops::{Deref, DerefMut};
-use style_traits::{ParseError, StyleParseError};
+use style_traits::{ParseError, StyleParseErrorKind};
 
 /// A pseudo-element, both public and private.
 ///
 /// NB: If you add to this list, be sure to update `each_simple_pseudo_element` too.
 #[derive(Clone, Debug, Eq, Hash, PartialEq)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[allow(missing_docs)]
 #[repr(usize)]
@@ -390,19 +390,19 @@ impl ::selectors::SelectorImpl for Selec
     fn is_active_or_hover(pseudo_class: &Self::NonTSPseudoClass) -> bool {
         matches!(*pseudo_class, NonTSPseudoClass::Active |
                                 NonTSPseudoClass::Hover)
     }
 }
 
 impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
     type Impl = SelectorImpl;
-    type Error = StyleParseError<'i>;
+    type Error = StyleParseErrorKind<'i>;
 
-    fn parse_non_ts_pseudo_class(&self, name: CowRcStr<'i>)
+    fn parse_non_ts_pseudo_class(&self, location: SourceLocation, name: CowRcStr<'i>)
                                  -> Result<NonTSPseudoClass, ParseError<'i>> {
         use self::NonTSPseudoClass::*;
         let pseudo_class = match_ignore_ascii_case! { &name,
             "active" => Active,
             "any-link" => AnyLink,
             "checked" => Checked,
             "disabled" => Disabled,
             "enabled" => Enabled,
@@ -413,127 +413,129 @@ impl<'a, 'i> ::selectors::Parser<'i> for
             "link" => Link,
             "placeholder-shown" => PlaceholderShown,
             "read-write" => ReadWrite,
             "read-only" => ReadOnly,
             "target" => Target,
             "visited" => Visited,
             "-servo-nonzero-border" => {
                 if !self.in_user_agent_stylesheet() {
-                    return Err(SelectorParseError::UnexpectedIdent(
-                        "-servo-nonzero-border".into()).into());
+                    return Err(location.new_custom_error(
+                        SelectorParseErrorKind::UnexpectedIdent("-servo-nonzero-border".into())
+                    ))
                 }
                 ServoNonZeroBorder
             },
-            _ => return Err(SelectorParseError::UnexpectedIdent(name.clone()).into()),
+            _ => return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))),
         };
 
         Ok(pseudo_class)
     }
 
     fn parse_non_ts_functional_pseudo_class<'t>(&self,
                                                 name: CowRcStr<'i>,
                                                 parser: &mut CssParser<'i, 't>)
                                                 -> Result<NonTSPseudoClass, ParseError<'i>> {
         use self::NonTSPseudoClass::*;
         let pseudo_class = match_ignore_ascii_case!{ &name,
             "lang" => {
                 Lang(parser.expect_ident_or_string()?.as_ref().into())
             }
             "-servo-case-sensitive-type-attr" => {
                 if !self.in_user_agent_stylesheet() {
-                    return Err(SelectorParseError::UnexpectedIdent(name.clone()).into());
+                    return Err(parser.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())));
                 }
                 ServoCaseSensitiveTypeAttr(Atom::from(parser.expect_ident()?.as_ref()))
             }
-            _ => return Err(SelectorParseError::UnexpectedIdent(name.clone()).into())
+            _ => return Err(parser.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
         };
 
         Ok(pseudo_class)
     }
 
-    fn parse_pseudo_element(&self, name: CowRcStr<'i>) -> Result<PseudoElement, ParseError<'i>> {
+    fn parse_pseudo_element(&self, location: SourceLocation, name: CowRcStr<'i>)
+                             -> Result<PseudoElement, ParseError<'i>> {
         use self::PseudoElement::*;
         let pseudo_element = match_ignore_ascii_case! { &name,
             "before" => Before,
             "after" => After,
             "selection" => Selection,
             "-servo-details-summary" => {
                 if !self.in_user_agent_stylesheet() {
-                    return Err(SelectorParseError::UnexpectedIdent(name.clone()).into())
+                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
                 }
                 DetailsSummary
             },
             "-servo-details-content" => {
                 if !self.in_user_agent_stylesheet() {
-                    return Err(SelectorParseError::UnexpectedIdent(name.clone()).into())
+                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
                 }
                 DetailsContent
             },
             "-servo-text" => {
                 if !self.in_user_agent_stylesheet() {
-                    return Err(SelectorParseError::UnexpectedIdent(name.clone()).into())
+                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
                 }
                 ServoText
             },
             "-servo-input-text" => {
                 if !self.in_user_agent_stylesheet() {
-                    return Err(SelectorParseError::UnexpectedIdent(name.clone()).into())
+                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
                 }
                 ServoInputText
             },
             "-servo-table-wrapper" => {
                 if !self.in_user_agent_stylesheet() {
-                    return Err(SelectorParseError::UnexpectedIdent(name.clone()).into())
+                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
                 }
                 ServoTableWrapper
             },
             "-servo-anonymous-table-wrapper" => {
                 if !self.in_user_agent_stylesheet() {
-                    return Err(SelectorParseError::UnexpectedIdent(name.clone()).into())
+                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
                 }
                 ServoAnonymousTableWrapper
             },
             "-servo-anonymous-table" => {
                 if !self.in_user_agent_stylesheet() {
-                    return Err(SelectorParseError::UnexpectedIdent(name.clone()).into())
+                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
                 }
                 ServoAnonymousTable
             },
             "-servo-anonymous-table-row" => {
                 if !self.in_user_agent_stylesheet() {
-                    return Err(SelectorParseError::UnexpectedIdent(name.clone()).into())
+                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
                 }
                 ServoAnonymousTableRow
             },
             "-servo-anonymous-table-cell" => {
                 if !self.in_user_agent_stylesheet() {
-                    return Err(SelectorParseError::UnexpectedIdent(name.clone()).into())
+                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
                 }
                 ServoAnonymousTableCell
             },
             "-servo-anonymous-block" => {
                 if !self.in_user_agent_stylesheet() {
-                    return Err(SelectorParseError::UnexpectedIdent(name.clone()).into())
+                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
                 }
                 ServoAnonymousBlock
             },
             "-servo-inline-block-wrapper" => {
                 if !self.in_user_agent_stylesheet() {
-                    return Err(SelectorParseError::UnexpectedIdent(name.clone()).into())
+                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
                 }
                 ServoInlineBlockWrapper
             },
             "-servo-input-absolute" => {
                 if !self.in_user_agent_stylesheet() {
-                    return Err(SelectorParseError::UnexpectedIdent(name.clone()).into())
+                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
                 }
                 ServoInlineAbsolute
             },
-            _ => return Err(SelectorParseError::UnexpectedIdent(name.clone()).into())
+            _ => return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
 
         };
 
         Ok(pseudo_element)
     }
 
     fn default_namespace(&self) -> Option<Namespace> {
         self.namespaces.default.as_ref().map(|&(ref ns, _)| ns.clone())
--- a/servo/components/style/stylesheets/document_rule.rs
+++ b/servo/components/style/stylesheets/document_rule.rs
@@ -1,25 +1,25 @@
 /* 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/. */
 
 //! [@document rules](https://www.w3.org/TR/2012/WD-css3-conditional-20120911/#at-document)
 //! initially in CSS Conditional Rules Module Level 3, @document has been postponed to the level 4.
 //! We implement the prefixed `@-moz-document`.
 
-use cssparser::{Parser, Token, SourceLocation, BasicParseError};
+use cssparser::{Parser, Token, SourceLocation};
 #[cfg(feature = "gecko")]
 use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
 use media_queries::Device;
 use parser::{Parse, ParserContext};
 use servo_arc::Arc;
 use shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
 use std::fmt;
-use style_traits::{ToCss, ParseError, StyleParseError};
+use style_traits::{ToCss, ParseError, StyleParseErrorKind};
 use stylesheets::CssRules;
 use values::specified::url::SpecifiedUrl;
 
 #[derive(Debug)]
 /// A @-moz-document rule
 pub struct DocumentRule {
     /// The parsed condition
     pub condition: DocumentCondition,
@@ -95,20 +95,21 @@ pub enum UrlMatchingFunction {
     RegExp(String),
 }
 
 macro_rules! parse_quoted_or_unquoted_string {
     ($input:ident, $url_matching_function:expr) => {
         $input.parse_nested_block(|input| {
             let start = input.position();
             input.parse_entirely(|input| {
+                let location = input.current_source_location();
                 match input.next() {
                     Ok(&Token::QuotedString(ref value)) =>
                         Ok($url_matching_function(value.as_ref().to_owned())),
-                    Ok(t) => Err(BasicParseError::UnexpectedToken(t.clone()).into()),
+                    Ok(t) => Err(location.new_unexpected_token_error(t.clone())),
                     Err(e) => Err(e.into()),
                 }
             }).or_else(|_: ParseError| {
                 while let Ok(_) = input.next() {}
                 Ok($url_matching_function(input.slice_from(start).to_string()))
             })
         })
     }
@@ -124,17 +125,17 @@ impl UrlMatchingFunction {
             parse_quoted_or_unquoted_string!(input, UrlMatchingFunction::Domain)
         } else if input.try(|input| input.expect_function_matching("regexp")).is_ok() {
             input.parse_nested_block(|input| {
                 Ok(UrlMatchingFunction::RegExp(input.expect_string()?.as_ref().to_owned()))
             })
         } else if let Ok(url) = input.try(|input| SpecifiedUrl::parse(context, input)) {
             Ok(UrlMatchingFunction::Url(url))
         } else {
-            Err(StyleParseError::UnspecifiedError.into())
+            Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         }
     }
 
     #[cfg(feature = "gecko")]
     /// Evaluate a URL matching function.
     pub fn evaluate(&self, device: &Device) -> bool {
         use gecko_bindings::bindings::Gecko_DocumentRule_UseForPresentation;
         use gecko_bindings::structs::URLMatchingFunction as GeckoUrlMatchingFunction;
--- a/servo/components/style/stylesheets/font_feature_values_rule.rs
+++ b/servo/components/style/stylesheets/font_feature_values_rule.rs
@@ -3,28 +3,27 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! The [`@font-feature-values`][font-feature-values] at-rule.
 //!
 //! [font-feature-values]: https://drafts.csswg.org/css-fonts-3/#at-font-feature-values-rule
 
 use Atom;
 use computed_values::font_family::FamilyName;
-use cssparser::{AtRuleParser, AtRuleType, BasicParseError, DeclarationListParser, DeclarationParser, Parser};
+use cssparser::{AtRuleParser, AtRuleType, BasicParseErrorKind, DeclarationListParser, DeclarationParser, Parser};
 use cssparser::{CowRcStr, RuleListParser, SourceLocation, QualifiedRuleParser, Token, serialize_identifier};
 use error_reporting::{ContextualParseError, ParseErrorReporter};
 #[cfg(feature = "gecko")]
 use gecko_bindings::bindings::Gecko_AppendFeatureValueHashEntry;
 #[cfg(feature = "gecko")]
 use gecko_bindings::structs::{self, gfxFontFeatureValueSet, nsTArray};
 use parser::{ParserContext, ParserErrorContext, Parse};
-use selectors::parser::SelectorParseError;
 use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
 use std::fmt;
-use style_traits::{ParseError, StyleParseError, ToCss};
+use style_traits::{ParseError, StyleParseErrorKind, ToCss};
 use stylesheets::CssRuleType;
 
 /// A @font-feature-values block declaration.
 /// It is `<ident>: <integer>+`.
 /// This struct can take 3 value types.
 /// - `SingleValue` is to keep just one unsigned integer value.
 /// - `PairValues` is to keep one or two unsigned integer values.
 /// - `VectorValues` is to keep a list of unsigned integer values.
@@ -54,19 +53,20 @@ pub trait ToGeckoFontFeatureValues {
 
 /// A @font-feature-values block declaration value that keeps one value.
 #[derive(Clone, Debug, PartialEq)]
 pub struct SingleValue(pub u32);
 
 impl Parse for SingleValue {
     fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                      -> Result<SingleValue, ParseError<'i>> {
+        let location = input.current_source_location();
         match *input.next()? {
             Token::Number { int_value: Some(v), .. } if v >= 0 => Ok(SingleValue(v as u32)),
-            ref t => Err(BasicParseError::UnexpectedToken(t.clone()).into()),
+            ref t => Err(location.new_unexpected_token_error(t.clone())),
         }
     }
 }
 
 impl ToCss for SingleValue {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         self.0.to_css(dest)
     }
@@ -82,26 +82,28 @@ impl ToGeckoFontFeatureValues for Single
 
 /// A @font-feature-values block declaration value that keeps one or two values.
 #[derive(Clone, Debug, PartialEq)]
 pub struct PairValues(pub u32, pub Option<u32>);
 
 impl Parse for PairValues {
     fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                      -> Result<PairValues, ParseError<'i>> {
+        let location = input.current_source_location();
         let first = match *input.next()? {
             Token::Number { int_value: Some(a), .. } if a >= 0 => a as u32,
-            ref t => return Err(BasicParseError::UnexpectedToken(t.clone()).into()),
+            ref t => return Err(location.new_unexpected_token_error(t.clone())),
         };
+        let location = input.current_source_location();
         match input.next() {
             Ok(&Token::Number { int_value: Some(b), .. }) if b >= 0 => {
                 Ok(PairValues(first, Some(b as u32)))
             }
             // It can't be anything other than number.
-            Ok(t) => Err(BasicParseError::UnexpectedToken(t.clone()).into()),
+            Ok(t) => Err(location.new_unexpected_token_error(t.clone())),
             // It can be just one value.
             Err(_) => Ok(PairValues(first, None))
         }
     }
 }
 
 impl ToCss for PairValues {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
@@ -131,28 +133,29 @@ impl ToGeckoFontFeatureValues for PairVa
 #[derive(Clone, Debug, PartialEq)]
 pub struct VectorValues(pub Vec<u32>);
 
 impl Parse for VectorValues {
     fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                      -> Result<VectorValues, ParseError<'i>> {
         let mut vec = vec![];
         loop {
+            let location = input.current_source_location();
             match input.next() {
                 Ok(&Token::Number { int_value: Some(a), .. }) if a >= 0 => {
                     vec.push(a as u32);
                 },
                 // It can't be anything other than number.
-                Ok(t) => return Err(BasicParseError::UnexpectedToken(t.clone()).into()),
+                Ok(t) => return Err(location.new_unexpected_token_error(t.clone())),
                 Err(_) => break,
             }
         }
 
         if vec.len() == 0 {
-            return Err(BasicParseError::EndOfInput.into());
+            return Err(input.new_error(BasicParseErrorKind::EndOfInput));
         }
 
         Ok(VectorValues(vec))
     }
 }
 
 impl ToCss for VectorValues {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
@@ -192,24 +195,24 @@ struct FFVDeclarationsParser<'a, 'b: 'a,
     declarations: &'a mut Vec<FFVDeclaration<T>>,
 }
 
 /// Default methods reject all at rules.
 impl<'a, 'b, 'i, T> AtRuleParser<'i> for FFVDeclarationsParser<'a, 'b, T> {
     type PreludeNoBlock = ();
     type PreludeBlock = ();
     type AtRule = ();
-    type Error = SelectorParseError<'i, StyleParseError<'i>>;
+    type Error = StyleParseErrorKind<'i>;
 }
 
 impl<'a, 'b, 'i, T> DeclarationParser<'i> for FFVDeclarationsParser<'a, 'b, T>
     where T: Parse
 {
     type Declaration = ();
-    type Error = SelectorParseError<'i, StyleParseError<'i>>;
+    type Error = StyleParseErrorKind<'i>;
 
     fn parse_value<'t>(&mut self, name: CowRcStr<'i>, input: &mut Parser<'i, 't>)
                        -> Result<(), ParseError<'i>> {
         let value = input.parse_entirely(|i| T::parse(self.context, i))?;
         let new = FFVDeclaration {
             name: Atom::from(&*name),
             value: value,
         };
@@ -266,19 +269,20 @@ macro_rules! font_feature_values_blocks 
 
                 {
                     let mut iter = RuleListParser::new_for_nested_rule(input, FontFeatureValuesRuleParser {
                         context: context,
                         error_context: error_context,
                         rule: &mut rule,
                     });
                     while let Some(result) = iter.next() {
-                        if let Err(err) = result {
-                            let error = ContextualParseError::UnsupportedRule(err.slice, err.error);
-                            context.log_css_error(error_context, err.location, error);
+                        if let Err((error, slice)) = result {
+                            let location = error.location;
+                            let error = ContextualParseError::UnsupportedRule(slice, error);
+                            context.log_css_error(error_context, location, error);
                         }
                     }
                 }
                 rule
             }
 
             /// Prints font family names.
             pub fn font_family_to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
@@ -382,34 +386,34 @@ macro_rules! font_feature_values_blocks 
             error_context: &'a ParserErrorContext<'a, R>,
             rule: &'a mut FontFeatureValuesRule,
         }
 
         /// Default methods reject all qualified rules.
         impl<'a, 'i, R: ParseErrorReporter> QualifiedRuleParser<'i> for FontFeatureValuesRuleParser<'a, R> {
             type Prelude = ();
             type QualifiedRule = ();
-            type Error = SelectorParseError<'i, StyleParseError<'i>>;
+            type Error = StyleParseErrorKind<'i>;
         }
 
         impl<'a, 'i, R: ParseErrorReporter> AtRuleParser<'i> for FontFeatureValuesRuleParser<'a, R> {
             type PreludeNoBlock = ();
             type PreludeBlock = BlockType;
             type AtRule = ();
-            type Error = SelectorParseError<'i, StyleParseError<'i>>;
+            type Error = StyleParseErrorKind<'i>;
 
             fn parse_prelude<'t>(&mut self,
                                  name: CowRcStr<'i>,
-                                 _input: &mut Parser<'i, 't>)
+                                 input: &mut Parser<'i, 't>)
                                  -> Result<AtRuleType<(), BlockType>, ParseError<'i>> {
                 match_ignore_ascii_case! { &*name,
                     $(
                         $name => Ok(AtRuleType::WithBlock(BlockType::$ident_camel)),
                     )*
-                    _ => Err(BasicParseError::AtRuleBodyInvalid.into()),
+                    _ => Err(input.new_error(BasicParseErrorKind::AtRuleBodyInvalid)),
                 }
             }
 
             fn parse_block<'t>(
                 &mut self,
                 prelude: BlockType,
                 input: &mut Parser<'i, 't>
             ) -> Result<Self::AtRule, ParseError<'i>> {
@@ -419,20 +423,22 @@ macro_rules! font_feature_values_blocks 
                         BlockType::$ident_camel => {
                             let parser = FFVDeclarationsParser {
                                 context: &self.context,
                                 declarations: &mut self.rule.$ident,
                             };
 
                             let mut iter = DeclarationListParser::new(input, parser);
                             while let Some(declaration) = iter.next() {
-                                if let Err(err) = declaration {
+                                if let Err((error, slice)) = declaration {
+                                    let location = error.location;
                                     let error = ContextualParseError::UnsupportedKeyframePropertyDeclaration(
-                                        err.slice, err.error);
-                                    self.context.log_css_error(self.error_context, err.location, error);
+                                        slice, error
+                                    );
+                                    self.context.log_css_error(self.error_context, location, error);
                                 }
                             }
                         },
                     )*
                 }
 
                 Ok(())
             }
--- a/servo/components/style/stylesheets/keyframes_rule.rs
+++ b/servo/components/style/stylesheets/keyframes_rule.rs
@@ -7,22 +7,20 @@
 use cssparser::{AtRuleParser, Parser, QualifiedRuleParser, RuleListParser, ParserInput, CowRcStr};
 use cssparser::{DeclarationListParser, DeclarationParser, parse_one_rule, SourceLocation};
 use error_reporting::{NullReporter, ContextualParseError, ParseErrorReporter};
 use parser::{ParserContext, ParserErrorContext};
 use properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock, PropertyId, PropertyParserContext};
 use properties::{PropertyDeclarationId, LonghandId, SourcePropertyDeclaration};
 use properties::LonghandIdSet;
 use properties::longhands::transition_timing_function::single_value::SpecifiedValue as SpecifiedTimingFunction;
-use selectors::parser::SelectorParseError;
 use servo_arc::Arc;
 use shared_lock::{DeepCloneParams, DeepCloneWithLock, SharedRwLock, SharedRwLockReadGuard, Locked, ToCssWithGuard};
 use std::fmt;
-use style_traits::{PARSING_MODE_DEFAULT, ToCss, ParseError, StyleParseError};
-use style_traits::PropertyDeclarationParseError;
+use style_traits::{PARSING_MODE_DEFAULT, ToCss, ParseError, StyleParseErrorKind};
 use stylesheets::{CssRuleType, StylesheetContents};
 use stylesheets::rule_parser::VendorPrefix;
 use values::{KeyframesName, serialize_percentage};
 
 /// A [`@keyframes`][keyframes] rule.
 ///
 /// [keyframes]: https://drafts.csswg.org/css-animations/#keyframes
 #[derive(Debug)]
@@ -132,17 +130,17 @@ impl KeyframePercentage {
             KeyframePercentage::new(0.)
         } else if input.try(|input| input.expect_ident_matching("to")).is_ok() {
             KeyframePercentage::new(1.)
         } else {
             let percentage = input.expect_percentage()?;
             if percentage >= 0. && percentage <= 1. {
                 KeyframePercentage::new(percentage)
             } else {
-                return Err(StyleParseError::UnspecifiedError.into());
+                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
             }
         };
 
         Ok(percentage)
     }
 }
 
 /// A keyframes selector is a list of percentages or from/to symbols, which are
@@ -495,43 +493,44 @@ pub fn parse_keyframe_list<R>(
         declarations: &mut declarations,
     }).filter_map(Result::ok).collect()
 }
 
 impl<'a, 'i, R> AtRuleParser<'i> for KeyframeListParser<'a, R> {
     type PreludeNoBlock = ();
     type PreludeBlock = ();
     type AtRule = Arc<Locked<Keyframe>>;
-    type Error = SelectorParseError<'i, StyleParseError<'i>>;
+    type Error = StyleParseErrorKind<'i>;
 }
 
 /// A wrapper to wraps the KeyframeSelector with its source location
 struct KeyframeSelectorParserPrelude {
     selector: KeyframeSelector,
     source_location: SourceLocation,
 }
 
 impl<'a, 'i, R: ParseErrorReporter> QualifiedRuleParser<'i> for KeyframeListParser<'a, R> {
     type Prelude = KeyframeSelectorParserPrelude;
     type QualifiedRule = Arc<Locked<Keyframe>>;
-    type Error = SelectorParseError<'i, StyleParseError<'i>>;
+    type Error = StyleParseErrorKind<'i>;
 
     fn parse_prelude<'t>(&mut self, input: &mut Parser<'i, 't>) -> Result<Self::Prelude, ParseError<'i>> {
         let start_position = input.position();
         let start_location = input.current_source_location();
         match KeyframeSelector::parse(input) {
             Ok(sel) => {
                 Ok(KeyframeSelectorParserPrelude {
                     selector: sel,
                     source_location: start_location,
                 })
             },
             Err(e) => {
+                let location = e.location;
                 let error = ContextualParseError::InvalidKeyframeRule(input.slice_from(start_position), e.clone());
-                self.context.log_css_error(self.error_context, start_location, error);
+                self.context.log_css_error(self.error_context, location, error);
                 Err(e)
             }
         }
     }
 
     fn parse_block<'t>(&mut self, prelude: Self::Prelude, input: &mut Parser<'i, 't>)
                        -> Result<Self::QualifiedRule, ParseError<'i>> {
         let context =
@@ -547,20 +546,21 @@ impl<'a, 'i, R: ParseErrorReporter> Qual
         };
         let mut iter = DeclarationListParser::new(input, parser);
         let mut block = PropertyDeclarationBlock::new();
         while let Some(declaration) = iter.next() {
             match declaration {
                 Ok(()) => {
                     block.extend(iter.parser.declarations.drain(), Importance::Normal);
                 }
-                Err(err) => {
+                Err((error, slice)) => {
                     iter.parser.declarations.clear();
-                    let error = ContextualParseError::UnsupportedKeyframePropertyDeclaration(err.slice, err.error);
-                    context.log_css_error(self.error_context, err.location, error);
+                    let location = error.location;
+                    let error = ContextualParseError::UnsupportedKeyframePropertyDeclaration(slice, error);
+                    context.log_css_error(self.error_context, location, error);
                 }
             }
             // `parse_important` is not called here, `!important` is not allowed in keyframe blocks.
         }
         Ok(Arc::new(self.shared_lock.wrap(Keyframe {
             selector: prelude.selector,
             block: Arc::new(self.shared_lock.wrap(block)),
             source_location: prelude.source_location,
@@ -573,30 +573,31 @@ struct KeyframeDeclarationParser<'a, 'b:
     declarations: &'a mut SourcePropertyDeclaration,
 }
 
 /// Default methods reject all at rules.
 impl<'a, 'b, 'i> AtRuleParser<'i> for KeyframeDeclarationParser<'a, 'b> {
     type PreludeNoBlock = ();
     type PreludeBlock = ();
     type AtRule = ();
-    type Error = SelectorParseError<'i, StyleParseError<'i>>;
+    type Error = StyleParseErrorKind<'i>;
 }
 
 impl<'a, 'b, 'i> DeclarationParser<'i> for KeyframeDeclarationParser<'a, 'b> {
     type Declaration = ();
-    type Error = SelectorParseError<'i, StyleParseError<'i>>;
+    type Error = StyleParseErrorKind<'i>;
 
     fn parse_value<'t>(&mut self, name: CowRcStr<'i>, input: &mut Parser<'i, 't>)
                        -> Result<(), ParseError<'i>> {
         let property_context = PropertyParserContext::new(self.context);
 
-        let id = PropertyId::parse(&name, Some(&property_context))
-            .map_err(|()| PropertyDeclarationParseError::UnknownProperty(name.clone()))?;
+        let id = PropertyId::parse(&name, Some(&property_context)).map_err(|()| {
+            input.new_custom_error(StyleParseErrorKind::UnknownProperty(name.clone()))
+        })?;
         match PropertyDeclaration::parse_into(self.declarations, id, name, self.context, input) {
             Ok(()) => {
                 // In case there is still unparsed text in the declaration, we should roll back.
                 input.expect_exhausted().map_err(|e| e.into())
             }
-            Err(_e) => Err(StyleParseError::UnspecifiedError.into())
+            Err(e) => Err(e.into())
         }
     }
 }
--- a/servo/components/style/stylesheets/rule_parser.rs
+++ b/servo/components/style/stylesheets/rule_parser.rs
@@ -3,29 +3,28 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Parsing of the stylesheet contents.
 
 use {Namespace, Prefix};
 use computed_values::font_family::FamilyName;
 use counter_style::{parse_counter_style_body, parse_counter_style_name};
 use cssparser::{AtRuleParser, AtRuleType, Parser, QualifiedRuleParser, RuleListParser};
-use cssparser::{CowRcStr, SourceLocation, BasicParseError};
+use cssparser::{CowRcStr, SourceLocation, BasicParseError, BasicParseErrorKind};
 use error_reporting::{ContextualParseError, ParseErrorReporter};
 use font_face::parse_font_face_block;
 use media_queries::{parse_media_query_list, MediaList};
 use parser::{Parse, ParserContext, ParserErrorContext};
 use properties::parse_property_declaration_list;
 use selector_parser::{SelectorImpl, SelectorParser};
 use selectors::SelectorList;
-use selectors::parser::SelectorParseError;
 use servo_arc::Arc;
 use shared_lock::{Locked, SharedRwLock};
 use str::starts_with_ignore_ascii_case;
-use style_traits::{StyleParseError, ParseError};
+use style_traits::{StyleParseErrorKind, ParseError};
 use stylesheets::{CssRule, CssRules, CssRuleType, Origin, StylesheetLoader};
 use stylesheets::{DocumentRule, FontFeatureValuesRule, KeyframesRule, MediaRule};
 use stylesheets::{NamespaceRule, PageRule, StyleRule, SupportsRule, ViewportRule};
 use stylesheets::document_rule::DocumentCondition;
 use stylesheets::font_feature_values_rule::parse_family_name_list;
 use stylesheets::keyframes_rule::parse_keyframe_list;
 use stylesheets::stylesheet::Namespaces;
 use stylesheets::supports_rule::SupportsCondition;
@@ -155,66 +154,67 @@ fn register_namespace(ns: &Namespace) ->
 fn register_namespace(_: &Namespace) {
     // servo doesn't use namespace ids
 }
 
 impl<'a, 'i, R: ParseErrorReporter> AtRuleParser<'i> for TopLevelRuleParser<'a, R> {
     type PreludeNoBlock = AtRuleNonBlockPrelude;
     type PreludeBlock = AtRuleBlockPrelude;
     type AtRule = CssRule;
-    type Error = SelectorParseError<'i, StyleParseError<'i>>;
+    type Error = StyleParseErrorKind<'i>;
 
     fn parse_prelude<'t>(
         &mut self,
         name: CowRcStr<'i>,
         input: &mut Parser<'i, 't>
     ) -> Result<AtRuleType<AtRuleNonBlockPrelude, AtRuleBlockPrelude>, ParseError<'i>> {
         let location = input.current_source_location();
         match_ignore_ascii_case! { &*name,
             "import" => {
                 if self.state > State::Imports {
                     // "@import must be before any rule but @charset"
                     self.had_hierarchy_error = true;
-                    return Err(StyleParseError::UnexpectedImportRule.into())
+                    return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedImportRule))
                 }
 
                 let url_string = input.expect_url_or_string()?.as_ref().to_owned();
                 let specified_url = SpecifiedUrl::parse_from_string(url_string, &self.context)?;
 
                 let media = parse_media_query_list(&self.context, input,
                                                    self.error_context.error_reporter);
                 let media = Arc::new(self.shared_lock.wrap(media));
 
                 let prelude = AtRuleNonBlockPrelude::Import(specified_url, media, location);
                 return Ok(AtRuleType::WithoutBlock(prelude));
             },
             "namespace" => {
                 if self.state > State::Namespaces {
                     // "@namespace must be before any rule but @charset and @import"
                     self.had_hierarchy_error = true;
-                    return Err(StyleParseError::UnexpectedNamespaceRule.into())
+                    return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedNamespaceRule))
                 }
 
                 let prefix = input.try(|i| i.expect_ident_cloned())
                                   .map(|s| Prefix::from(s.as_ref())).ok();
                 let maybe_namespace = match input.expect_url_or_string() {
                     Ok(url_or_string) => url_or_string,
-                    Err(BasicParseError::UnexpectedToken(t)) =>
-                        return Err(StyleParseError::UnexpectedTokenWithinNamespace(t).into()),
+                    Err(BasicParseError { kind: BasicParseErrorKind::UnexpectedToken(t), location }) => {
+                        return Err(location.new_custom_error(StyleParseErrorKind::UnexpectedTokenWithinNamespace(t)))
+                    }
                     Err(e) => return Err(e.into()),
                 };
                 let url = Namespace::from(maybe_namespace.as_ref());
                 let prelude = AtRuleNonBlockPrelude::Namespace(prefix, url, location);
                 return Ok(AtRuleType::WithoutBlock(prelude));
             },
             // @charset is removed by rust-cssparser if it’s the first rule in the stylesheet
             // anything left is invalid.
             "charset" => {
                 self.had_hierarchy_error = true;
-                return Err(StyleParseError::UnexpectedCharsetRule.into())
+                return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedCharsetRule))
             }
             _ => {}
         }
 
         AtRuleParser::parse_prelude(&mut self.nested(), name, input)
     }
 
     #[inline]
@@ -274,17 +274,17 @@ impl<'a, 'i, R: ParseErrorReporter> AtRu
 pub struct QualifiedRuleParserPrelude {
     selectors: SelectorList<SelectorImpl>,
     source_location: SourceLocation,
 }
 
 impl<'a, 'i, R: ParseErrorReporter> QualifiedRuleParser<'i> for TopLevelRuleParser<'a, R> {
     type Prelude = QualifiedRuleParserPrelude;
     type QualifiedRule = CssRule;
-    type Error = SelectorParseError<'i, StyleParseError<'i>>;
+    type Error = StyleParseErrorKind<'i>;
 
     #[inline]
     fn parse_prelude<'t>(
         &mut self,
         input: &mut Parser<'i, 't>,
     ) -> Result<QualifiedRuleParserPrelude, ParseError<'i>> {
         QualifiedRuleParser::parse_prelude(&mut self.nested(), input)
     }
@@ -326,31 +326,32 @@ impl<'a, 'b, R: ParseErrorReporter> Nest
             namespaces: self.namespaces,
         };
 
         let mut iter = RuleListParser::new_for_nested_rule(input, nested_parser);
         let mut rules = Vec::new();
         while let Some(result) = iter.next() {
             match result {
                 Ok(rule) => rules.push(rule),
-                Err(err) => {
-                    let error = ContextualParseError::UnsupportedRule(err.slice, err.error);
-                    self.context.log_css_error(self.error_context, err.location, error);
+                Err((error, slice)) => {
+                    let location = error.location;
+                    let error = ContextualParseError::UnsupportedRule(slice, error);
+                    self.context.log_css_error(self.error_context, location, error);
                 }
             }
         }
         CssRules::new(rules, self.shared_lock)
     }
 }
 
 impl<'a, 'b, 'i, R: ParseErrorReporter> AtRuleParser<'i> for NestedRuleParser<'a, 'b, R> {
     type PreludeNoBlock = AtRuleNonBlockPrelude;
     type PreludeBlock = AtRuleBlockPrelude;
     type AtRule = CssRule;
-    type Error = SelectorParseError<'i, StyleParseError<'i>>;
+    type Error = StyleParseErrorKind<'i>;
 
     fn parse_prelude<'t>(
         &mut self,
         name: CowRcStr<'i>,
         input: &mut Parser<'i, 't>
     ) -> Result<AtRuleType<AtRuleNonBlockPrelude, AtRuleBlockPrelude>, ParseError<'i>> {
         let location = input.current_source_location();
 
@@ -366,75 +367,75 @@ impl<'a, 'b, 'i, R: ParseErrorReporter> 
                 Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Supports(cond, location)))
             },
             "font-face" => {
                 Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::FontFace(location)))
             },
             "font-feature-values" => {
                 if !cfg!(feature = "gecko") {
                     // Support for this rule is not fully implemented in Servo yet.
-                    return Err(StyleParseError::UnsupportedAtRule(name.clone()).into())
+                    return Err(input.new_custom_error(StyleParseErrorKind::UnsupportedAtRule(name.clone())))
                 }
                 let family_names = parse_family_name_list(self.context, input)?;
                 Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::FontFeatureValues(family_names, location)))
             },
             "counter-style" => {
                 if !cfg!(feature = "gecko") {
                     // Support for this rule is not fully implemented in Servo yet.
-                    return Err(StyleParseError::UnsupportedAtRule(name.clone()).into())
+                    return Err(input.new_custom_error(StyleParseErrorKind::UnsupportedAtRule(name.clone())))
                 }
                 let name = parse_counter_style_name(input)?;
                 // ASCII-case-insensitive matches for "decimal" and "disc".
                 // The name is already lower-cased by `parse_counter_style_name`
                 // so we can use == here.
                 if name.0 == atom!("decimal") || name.0 == atom!("disc") {
-                    return Err(StyleParseError::UnspecifiedError.into())
+                    return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
                 }
                 Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::CounterStyle(name)))
             },
             "viewport" => {
                 if viewport_rule::enabled() {
                     Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Viewport))
                 } else {
-                    Err(StyleParseError::UnsupportedAtRule(name.clone()).into())
+                    Err(input.new_custom_error(StyleParseErrorKind::UnsupportedAtRule(name.clone())))
                 }
             },
             "keyframes" | "-webkit-keyframes" | "-moz-keyframes" => {
                 let prefix = if starts_with_ignore_ascii_case(&*name, "-webkit-") {
                     Some(VendorPrefix::WebKit)
                 } else if starts_with_ignore_ascii_case(&*name, "-moz-") {
                     Some(VendorPrefix::Moz)
                 } else {
                     None
                 };
                 if cfg!(feature = "servo") &&
                    prefix.as_ref().map_or(false, |p| matches!(*p, VendorPrefix::Moz)) {
                     // Servo should not support @-moz-keyframes.
-                    return Err(StyleParseError::UnsupportedAtRule(name.clone()).into())
+                    return Err(input.new_custom_error(StyleParseErrorKind::UnsupportedAtRule(name.clone())))
                 }
                 let name = KeyframesName::parse(self.context, input)?;
 
                 Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Keyframes(name, prefix, location)))
             },
             "page" => {
                 if cfg!(feature = "gecko") {
                     Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Page(location)))
                 } else {
-                    Err(StyleParseError::UnsupportedAtRule(name.clone()).into())
+                    Err(input.new_custom_error(StyleParseErrorKind::UnsupportedAtRule(name.clone())))
                 }
             },
             "-moz-document" => {
                 if cfg!(feature = "gecko") {
                     let cond = DocumentCondition::parse(self.context, input)?;
                     Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Document(cond, location)))
                 } else {
-                    Err(StyleParseError::UnsupportedAtRule(name.clone()).into())
+                    Err(input.new_custom_error(StyleParseErrorKind::UnsupportedAtRule(name.clone())))
                 }
             },
-            _ => Err(StyleParseError::UnsupportedAtRule(name.clone()).into())
+            _ => Err(input.new_custom_error(StyleParseErrorKind::UnsupportedAtRule(name.clone())))
         }
     }
 
     fn parse_block<'t>(
         &mut self,
         prelude: AtRuleBlockPrelude,
         input: &mut Parser<'i, 't>
     ) -> Result<CssRule, ParseError<'i>> {
@@ -543,17 +544,17 @@ impl<'a, 'b, 'i, R: ParseErrorReporter> 
             }
         }
     }
 }
 
 impl<'a, 'b, 'i, R: ParseErrorReporter> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'b, R> {
     type Prelude = QualifiedRuleParserPrelude;
     type QualifiedRule = CssRule;
-    type Error = SelectorParseError<'i, StyleParseError<'i>>;
+    type Error = StyleParseErrorKind<'i>;
 
     fn parse_prelude<'t>(
         &mut self,
         input: &mut Parser<'i, 't>
     ) -> Result<QualifiedRuleParserPrelude, ParseError<'i>> {
         let selector_parser = SelectorParser {
             stylesheet_origin: self.stylesheet_origin,
             namespaces: self.namespaces,
--- a/servo/components/style/stylesheets/stylesheet.rs
+++ b/servo/components/style/stylesheets/stylesheet.rs
@@ -389,20 +389,21 @@ impl Stylesheet {
                     Ok(rule) => {
                         // Use a fallible push here, and if it fails, just
                         // fall out of the loop.  This will cause the page to
                         // be shown incorrectly, but it's better than OOMing.
                         if rules.try_push(rule).is_err() {
                             break;
                         }
                     },
-                    Err(err) => {
-                        let error = ContextualParseError::InvalidRule(err.slice, err.error);
+                    Err((error, slice)) => {
+                        let location = error.location;
+                        let error = ContextualParseError::InvalidRule(slice, error);
                         iter.parser.context.log_css_error(&iter.parser.error_context,
-                                                          err.location, error);
+                                                          location, error);
                     }
                 }
             }
         }
 
         let source_map_url = input.current_source_map_url().map(String::from);
         let source_url = input.current_source_url().map(String::from);
         (rules, source_map_url, source_url)
--- a/servo/components/style/stylesheets/supports_rule.rs
+++ b/servo/components/style/stylesheets/supports_rule.rs
@@ -1,25 +1,25 @@
 /* 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/. */
 
 //! [@supports rules](https://drafts.csswg.org/css-conditional-3/#at-supports)
 
-use cssparser::{BasicParseError, ParseError as CssParseError, ParserInput};
 use cssparser::{Delimiter, parse_important, Parser, SourceLocation, Token};
+use cssparser::{ParseError as CssParseError, ParserInput};
 #[cfg(feature = "gecko")]
 use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
 use parser::ParserContext;
 use properties::{PropertyId, PropertyDeclaration, PropertyParserContext, SourcePropertyDeclaration};
-use selectors::parser::SelectorParseError;
+use selectors::parser::SelectorParseErrorKind;
 use servo_arc::Arc;
 use shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
 use std::fmt;
-use style_traits::{ToCss, ParseError, StyleParseError};
+use style_traits::{ToCss, ParseError};
 use stylesheets::{CssRuleType, CssRules};
 
 /// An [`@supports`][supports] rule.
 ///
 /// [supports]: https://drafts.csswg.org/css-conditional-3/#at-supports
 #[derive(Debug)]
 pub struct SupportsRule {
     /// The parsed condition
@@ -99,29 +99,30 @@ impl SupportsCondition {
     pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<SupportsCondition, ParseError<'i>> {
         if let Ok(_) = input.try(|i| i.expect_ident_matching("not")) {
             let inner = SupportsCondition::parse_in_parens(input)?;
             return Ok(SupportsCondition::Not(Box::new(inner)));
         }
 
         let in_parens = SupportsCondition::parse_in_parens(input)?;
 
+        let location = input.current_source_location();
         let (keyword, wrapper) = match input.next() {
             Err(_) => {
                 // End of input
                 return Ok(in_parens)
             }
             Ok(&Token::Ident(ref ident)) => {
                 match_ignore_ascii_case! { &ident,
                     "and" => ("and", SupportsCondition::And as fn(_) -> _),
                     "or" => ("or", SupportsCondition::Or as fn(_) -> _),
-                    _ => return Err(SelectorParseError::UnexpectedIdent(ident.clone()).into())
+                    _ => return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())))
                 }
             }
-            Ok(t) => return Err(CssParseError::Basic(BasicParseError::UnexpectedToken(t.clone())))
+            Ok(t) => return Err(location.new_unexpected_token_error(t.clone()))
         };
 
         let mut conditions = Vec::with_capacity(2);
         conditions.push(in_parens);
         loop {
             conditions.push(SupportsCondition::parse_in_parens(input)?);
             if input.try(|input| input.expect_ident_matching(keyword)).is_err() {
                 // Did not find the expected keyword.
@@ -133,28 +134,29 @@ impl SupportsCondition {
     }
 
     /// https://drafts.csswg.org/css-conditional-3/#supports_condition_in_parens
     fn parse_in_parens<'i, 't>(input: &mut Parser<'i, 't>) -> Result<SupportsCondition, ParseError<'i>> {
         // Whitespace is normally taken care of in `Parser::next`,
         // but we want to not include it in `pos` for the SupportsCondition::FutureSyntax cases.
         while input.try(Parser::expect_whitespace).is_ok() {}
         let pos = input.position();
+        let location = input.current_source_location();
         // FIXME: remove clone() when lifetimes are non-lexical
         match input.next()?.clone() {
             Token::ParenthesisBlock => {
                 let nested = input.try(|input| {
                     input.parse_nested_block(|i| parse_condition_or_declaration(i))
                 });
                 if nested.is_ok() {
                     return nested;
                 }
             }
             Token::Function(_) => {}
-            t => return Err(CssParseError::Basic(BasicParseError::UnexpectedToken(t))),
+            t => return Err(location.new_unexpected_token_error(t)),
         }
         input.parse_nested_block(|i| consume_any_value(i))?;
         Ok(SupportsCondition::FutureSyntax(input.slice_from(pos).to_owned()))
     }
 
     /// Evaluate a supports condition
     pub fn eval(&self, cx: &ParserContext) -> bool {
         match *self {
@@ -253,26 +255,26 @@ impl Declaration {
     /// Determine if a declaration parses
     ///
     /// https://drafts.csswg.org/css-conditional-3/#support-definition
     pub fn eval(&self, context: &ParserContext) -> bool {
         debug_assert_eq!(context.rule_type(), CssRuleType::Style);
 
         let mut input = ParserInput::new(&self.0);
         let mut input = Parser::new(&mut input);
-        input.parse_entirely(|input| {
+        input.parse_entirely(|input| -> Result<(), CssParseError<()>> {
             let prop = input.expect_ident().unwrap().as_ref().to_owned();
             input.expect_colon().unwrap();
 
             let property_context = PropertyParserContext::new(&context);
             let id = PropertyId::parse(&prop, Some(&property_context))
-                        .map_err(|_| StyleParseError::UnspecifiedError)?;
+                        .map_err(|()| input.new_custom_error(()))?;
 
             let mut declarations = SourcePropertyDeclaration::new();
             input.parse_until_before(Delimiter::Bang, |input| {
                 PropertyDeclaration::parse_into(&mut declarations, id, prop.into(), &context, input)
-                    .map_err(|e| StyleParseError::PropertyDeclaration(e).into())
+                    .map_err(|_| input.new_custom_error(()))
             })?;
             let _ = input.try(parse_important);
             Ok(())
         }).is_ok()
     }
 }
--- a/servo/components/style/stylesheets/viewport_rule.rs
+++ b/servo/components/style/stylesheets/viewport_rule.rs
@@ -13,25 +13,25 @@ use cssparser::{AtRuleParser, Declaratio
 use cssparser::{CowRcStr, ToCss as ParserToCss};
 use error_reporting::{ContextualParseError, ParseErrorReporter};
 use euclid::TypedSize2D;
 use font_metrics::get_metrics_provider_for_product;
 use media_queries::Device;
 use parser::{ParserContext, ParserErrorContext};
 use properties::StyleBuilder;
 use rule_cache::RuleCacheConditions;
-use selectors::parser::SelectorParseError;
+use selectors::parser::SelectorParseErrorKind;
 use shared_lock::{SharedRwLockReadGuard, StylesheetGuards, ToCssWithGuard};
 use std::ascii::AsciiExt;
 use std::borrow::Cow;
 use std::cell::RefCell;
 use std::fmt;
 use std::iter::Enumerate;
 use std::str::Chars;
-use style_traits::{PinchZoomFactor, ToCss, ParseError, StyleParseError};
+use style_traits::{PinchZoomFactor, ToCss, ParseError, StyleParseErrorKind};
 use style_traits::viewport::{Orientation, UserZoom, ViewportConstraints, Zoom};
 use stylesheets::{StylesheetInDocument, Origin};
 use values::computed::{Context, ToComputedValue};
 use values::specified::{NoCalcLength, LengthOrPercentageOrAuto, ViewportPercentageLength};
 
 /// Whether parsing and processing of `@viewport` rules is enabled.
 #[cfg(feature = "servo")]
 pub fn enabled() -> bool {
@@ -271,22 +271,22 @@ fn parse_shorthand<'i, 't>(context: &Par
         Ok(max) => Ok((min, max))
     }
 }
 
 impl<'a, 'b, 'i> AtRuleParser<'i> for ViewportRuleParser<'a, 'b> {
     type PreludeNoBlock = ();
     type PreludeBlock = ();
     type AtRule = Vec<ViewportDescriptorDeclaration>;
-    type Error = SelectorParseError<'i, StyleParseError<'i>>;
+    type Error = StyleParseErrorKind<'i>;
 }
 
 impl<'a, 'b, 'i> DeclarationParser<'i> for ViewportRuleParser<'a, 'b> {
     type Declaration = Vec<ViewportDescriptorDeclaration>;
-    type Error = SelectorParseError<'i, StyleParseError<'i>>;
+    type Error = StyleParseErrorKind<'i>;
 
     fn parse_value<'t>(&mut self, name: CowRcStr<'i>, input: &mut Parser<'i, 't>)
                        -> Result<Vec<ViewportDescriptorDeclaration>, ParseError<'i>> {
         macro_rules! declaration {
             ($declaration:ident($parse:expr)) => {
                 declaration!($declaration(value: try!($parse(input)),
                                           important: input.try(parse_important).is_ok()))
             };
@@ -318,17 +318,17 @@ impl<'a, 'b, 'i> DeclarationParser<'i> f
             "min-height" => ok!(MinHeight(|i| ViewportLength::parse(self.context, i))),
             "max-height" => ok!(MaxHeight(|i| ViewportLength::parse(self.context, i))),
             "height" => ok!(shorthand -> [MinHeight, MaxHeight]),
             "zoom" => ok!(Zoom(Zoom::parse)),
             "min-zoom" => ok!(MinZoom(Zoom::parse)),
             "max-zoom" => ok!(MaxZoom(Zoom::parse)),
             "user-zoom" => ok!(UserZoom(UserZoom::parse)),
             "orientation" => ok!(Orientation(Orientation::parse)),
-            _ => Err(SelectorParseError::UnexpectedIdent(name.clone()).into()),
+            _ => Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))),
         }
     }
 }
 
 /// A `@viewport` rule.
 #[derive(Clone, Debug, PartialEq)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub struct ViewportRule {
@@ -363,19 +363,20 @@ impl ViewportRule {
         let mut parser = DeclarationListParser::new(input, parser);
         while let Some(result) = parser.next() {
             match result {
                 Ok(declarations) => {
                     for declarations in declarations {
                         cascade.add(Cow::Owned(declarations))
                     }
                 }
-                Err(err) => {
-                    let error = ContextualParseError::UnsupportedViewportDescriptorDeclaration(err.slice, err.error);
-                    context.log_css_error(error_context, err.location, error);
+                Err((error, slice)) => {
+                    let location = error.location;
+                    let error = ContextualParseError::UnsupportedViewportDescriptorDeclaration(slice, error);
+                    context.log_css_error(error_context, location, error);
                 }
             }
         }
         Ok(ViewportRule { declarations: cascade.finish() })
     }
 }
 
 impl ViewportRule {
--- a/servo/components/style/values/generics/grid.rs
+++ b/servo/components/style/values/generics/grid.rs
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Generic types for the handling of
 //! [grids](https://drafts.csswg.org/css-grid/).
 
 use cssparser::Parser;
 use parser::{Parse, ParserContext};
 use std::{fmt, mem, usize};
-use style_traits::{ToCss, ParseError, StyleParseError};
+use style_traits::{ToCss, ParseError, StyleParseErrorKind};
 use values::{CSSFloat, CustomIdent, serialize_dimension};
 use values::computed::{Context, ToComputedValue};
 use values::specified;
 use values::specified::grid::parse_line_names;
 
 /// A `<grid-line>` type.
 ///
 /// https://drafts.csswg.org/css-grid/#typedef-grid-row-start-grid-line
@@ -83,54 +83,55 @@ impl Parse for GridLine<specified::Integ
 
         // <custom-ident> | [ <integer> && <custom-ident>? ] | [ span && [ <integer> || <custom-ident> ] ]
         // This <grid-line> horror is simply,
         // [ span? && [ <custom-ident> || <integer> ] ]
         // And, for some magical reason, "span" should be the first or last value and not in-between.
         let mut val_before_span = false;
 
         for _ in 0..3 {     // Maximum possible entities for <grid-line>
+            let location = input.current_source_location();
             if input.try(|i| i.expect_ident_matching("span")).is_ok() {
                 if grid_line.is_span {
-                    return Err(StyleParseError::UnspecifiedError.into())
+                    return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
                 }
 
                 if grid_line.line_num.is_some() || grid_line.ident.is_some() {
                     val_before_span = true;
                 }
 
                 grid_line.is_span = true;
             } else if let Ok(i) = input.try(|i| specified::Integer::parse(context, i)) {
                 // FIXME(emilio): Probably shouldn't reject if it's calc()...
                 if i.value() == 0 || val_before_span || grid_line.line_num.is_some() {
-                    return Err(StyleParseError::UnspecifiedError.into())
+                    return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
                 }
 
                 grid_line.line_num = Some(i);
             } else if let Ok(name) = input.try(|i| i.expect_ident_cloned()) {
                 if val_before_span || grid_line.ident.is_some() {
-                    return Err(StyleParseError::UnspecifiedError.into());
+                    return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
                 }
-                grid_line.ident = Some(CustomIdent::from_ident(&name, &[])?);
+                grid_line.ident = Some(CustomIdent::from_ident(location, &name, &[])?);
             } else {
                 break
             }
         }
 
         if grid_line.is_auto() {
-            return Err(StyleParseError::UnspecifiedError.into())
+            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         }
 
         if grid_line.is_span {
             if let Some(i) = grid_line.line_num {
                 if i.value() <= 0 {       // disallow negative integers for grid spans
-                    return Err(StyleParseError::UnspecifiedError.into())
+                    return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
                 }
             } else if grid_line.ident.is_none() {       // integer could be omitted
-                return Err(StyleParseError::UnspecifiedError.into())
+                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
             }
         }
 
         Ok(grid_line)
     }
 }
 
 define_css_keyword_enum!{ TrackKeyword:
@@ -364,17 +365,17 @@ impl Parse for RepeatCount<specified::In
         // Maximum number of repeat is 10000. The greater numbers should be clamped.
         const MAX_LINE: i32 = 10000;
         if let Ok(mut i) = input.try(|i| specified::Integer::parse_positive(context, i)) {
             if i.value() > MAX_LINE {
                 i = specified::Integer::new(MAX_LINE);
             }
             Ok(RepeatCount::Number(i))
         } else {
-            try_match_ident_ignore_ascii_case! { input.expect_ident()?,
+            try_match_ident_ignore_ascii_case! { input,
                 "auto-fill" => Ok(RepeatCount::AutoFill),
                 "auto-fit" => Ok(RepeatCount::AutoFit),
             }
         }
     }
 }
 
 /// The structure containing `<line-names>` and `<track-size>` values.
@@ -607,24 +608,24 @@ impl Parse for LineNameList {
             if let Ok((mut names_list, count)) = repeat_parse_result {
                 match count {
                     RepeatCount::Number(num) =>
                         line_names.extend(names_list.iter().cloned().cycle()
                                   .take(num.value() as usize * names_list.len())),
                     RepeatCount::AutoFill if fill_idx.is_none() => {
                         // `repeat(autof-fill, ..)` should have just one line name.
                         if names_list.len() != 1 {
-                            return Err(StyleParseError::UnspecifiedError.into());
+                            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
                         }
                         let names = names_list.pop().unwrap();
 
                         line_names.push(names);
                         fill_idx = Some(line_names.len() as u32 - 1);
                     },
-                    _ => return Err(StyleParseError::UnspecifiedError.into()),
+                    _ => return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
                 }
             } else if let Ok(names) = input.try(parse_line_names) {
                 line_names.push(names);
             } else {
                 break
             }
         }
 
--- a/servo/components/style/values/generics/mod.rs
+++ b/servo/components/style/values/generics/mod.rs
@@ -4,17 +4,17 @@
 
 //! Generic types that share their serialization implementations
 //! for both specified and computed values.
 
 use counter_style::{Symbols, parse_counter_style_name};
 use cssparser::Parser;
 use parser::{Parse, ParserContext};
 use std::fmt;
-use style_traits::{Comma, OneOrMoreSeparated, ParseError, StyleParseError, ToCss};
+use style_traits::{Comma, OneOrMoreSeparated, ParseError, StyleParseErrorKind, ToCss};
 use super::CustomIdent;
 
 pub mod background;
 pub mod basic_shape;
 pub mod border;
 #[path = "box.rs"]
 pub mod box_;
 pub mod effects;
@@ -109,26 +109,26 @@ impl Parse for CounterStyleOrNone {
             return input.parse_nested_block(|input| {
                 let symbols_type = input.try(|i| SymbolsType::parse(i))
                     .unwrap_or(SymbolsType::Symbolic);
                 let symbols = Symbols::parse(context, input)?;
                 // There must be at least two symbols for alphabetic or
                 // numeric system.
                 if (symbols_type == SymbolsType::Alphabetic ||
                     symbols_type == SymbolsType::Numeric) && symbols.0.len() < 2 {
-                    return Err(StyleParseError::UnspecifiedError.into());
+                    return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
                 }
                 // Identifier is not allowed in symbols() function.
                 if symbols.0.iter().any(|sym| !sym.is_allowed_in_symbols()) {
-                    return Err(StyleParseError::UnspecifiedError.into());
+                    return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
                 }
                 Ok(CounterStyleOrNone::Symbols(symbols_type, symbols))
             });
         }
-        Err(StyleParseError::UnspecifiedError.into())
+        Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     }
 }
 
 /// A settings tag, defined by a four-character tag and a setting value
 ///
 /// For font-feature-settings, this is a tag and an integer,
 /// for font-variation-settings this is a tag and a float
 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
@@ -165,23 +165,24 @@ impl<T: Parse> Parse for FontSettingTag<
     /// <string> [ on | off | <integer> ]
     /// <string> <number>
     fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
         use byteorder::{ReadBytesExt, BigEndian};
         use std::io::Cursor;
 
         let u_tag;
         {
+            let location = input.current_source_location();
             let tag = input.expect_string()?;
 
             // allowed strings of length 4 containing chars: <U+20, U+7E>
             if tag.len() != 4 ||
                tag.chars().any(|c| c < ' ' || c > '~')
             {
-                return Err(StyleParseError::UnspecifiedError.into())
+                return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
             }
 
             let mut raw = Cursor::new(tag.as_bytes());
             u_tag = raw.read_u32::<BigEndian>().unwrap();
         }
 
         Ok(FontSettingTag { tag: u_tag, value: T::parse(context, input)? })
     }
@@ -243,17 +244,17 @@ impl ToCss for FontSettingTagInt {
 
 impl Parse for FontSettingTagInt {
     fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
         if let Ok(value) = input.try(|input| input.expect_integer()) {
             // handle integer, throw if it is negative
             if value >= 0 {
                 Ok(FontSettingTagInt(value as u32))
             } else {
-                Err(StyleParseError::UnspecifiedError.into())
+                Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
             }
         } else if let Ok(_) = input.try(|input| input.expect_ident_matching("on")) {
             // on is an alias for '1'
             Ok(FontSettingTagInt(1))
         } else if let Ok(_) = input.try(|input| input.expect_ident_matching("off")) {
             // off is an alias for '0'
             Ok(FontSettingTagInt(0))
         } else {
--- a/servo/components/style/values/generics/svg.rs
+++ b/servo/components/style/values/generics/svg.rs
@@ -2,17 +2,17 @@
  * 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/. */
 
 //! Generic types for CSS values in SVG
 
 use cssparser::Parser;
 use parser::{Parse, ParserContext};
 use std::fmt;
-use style_traits::{ParseError, StyleParseError, ToCss};
+use style_traits::{ParseError, StyleParseErrorKind, ToCss};
 use values::{Either, None_};
 use values::computed::NumberOrPercentage;
 use values::computed::length::LengthOrPercentage;
 use values::distance::{ComputeSquaredDistance, SquaredDistance};
 
 /// An SVG paint value
 ///
 /// https://www.w3.org/TR/SVG2/painting.html#SpecifyingPaint
@@ -49,17 +49,17 @@ pub enum SVGPaintKind<ColorType, UrlPain
     ContextFill,
     /// `context-stroke`
     ContextStroke,
 }
 
 impl<ColorType, UrlPaintServer> SVGPaintKind<ColorType, UrlPaintServer> {
     /// Parse a keyword value only
     fn parse_ident<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
-        try_match_ident_ignore_ascii_case! { input.expect_ident()?,
+        try_match_ident_ignore_ascii_case! { input,
             "none" => Ok(SVGPaintKind::None),
             "context-fill" => Ok(SVGPaintKind::ContextFill),
             "context-stroke" => Ok(SVGPaintKind::ContextStroke),
         }
     }
 }
 
 /// Parse SVGPaint's fallback.
@@ -99,17 +99,17 @@ impl<ColorType: Parse, UrlPaintServer: P
                 })
             }
         } else if let Ok(color) = input.try(|i| ColorType::parse(context, i)) {
             Ok(SVGPaint {
                 kind: SVGPaintKind::Color(color),
                 fallback: None,
             })
         } else {
-            Err(StyleParseError::UnspecifiedError.into())
+            Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         }
     }
 }
 
 /// A value of <length> | <percentage> | <number> for svg which allow unitless length.
 /// https://www.w3.org/TR/SVG11/painting.html#StrokeProperties
 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@@ -184,17 +184,17 @@ impl <LengthOrPercentageType: Parse, Num
                      -> Result<Self, ParseError<'i>> {
         if let Ok(num) = input.try(|i| NumberType::parse(context, i)) {
             return Ok(SvgLengthOrPercentageOrNumber::Number(num));
         }
 
         if let Ok(lop) = input.try(|i| LengthOrPercentageType::parse(context, i)) {
             return Ok(SvgLengthOrPercentageOrNumber::LengthOrPercentage(lop));
         }
-        Err(StyleParseError::UnspecifiedError.into())
+        Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     }
 }
 
 /// An SVG length value supports `context-value` in addition to length.
 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[derive(Clone, ComputeSquaredDistance, Copy, Debug, PartialEq)]
 #[derive(ToAnimatedValue, ToAnimatedZero)]
--- a/servo/components/style/values/mod.rs
+++ b/servo/components/style/values/mod.rs
@@ -4,23 +4,23 @@
 
 //! Common [values][values] used in CSS.
 //!
 //! [values]: https://drafts.csswg.org/css-values/
 
 #![deny(missing_docs)]
 
 use Atom;
-pub use cssparser::{RGBA, Token, Parser, serialize_identifier, BasicParseError, CowRcStr};
+pub use cssparser::{RGBA, Token, Parser, serialize_identifier, CowRcStr, SourceLocation};
 use parser::{Parse, ParserContext};
-use selectors::parser::SelectorParseError;
+use selectors::parser::SelectorParseErrorKind;
 use std::ascii::AsciiExt;
 use std::fmt::{self, Debug};
 use std::hash;
-use style_traits::{ToCss, ParseError, StyleParseError};
+use style_traits::{ToCss, ParseError, StyleParseErrorKind};
 
 pub mod animated;
 pub mod computed;
 pub mod distance;
 pub mod generics;
 pub mod specified;
 
 /// A CSS float value.
@@ -52,19 +52,19 @@ pub fn serialize_dimension<W>(value: CSS
 /// Convenience void type to disable some properties and values through types.
 #[cfg_attr(feature = "servo", derive(Deserialize, HeapSizeOf, Serialize))]
 #[derive(Clone, Copy, Debug, PartialEq, ToComputedValue, ToCss)]
 pub enum Impossible {}
 
 impl Parse for Impossible {
     fn parse<'i, 't>(
         _context: &ParserContext,
-        _input: &mut Parser<'i, 't>)
+        input: &mut Parser<'i, 't>)
     -> Result<Self, ParseError<'i>> {
-        Err(StyleParseError::UnspecifiedError.into())
+        Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     }
 }
 
 /// A struct representing one of two kinds of values.
 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[derive(Animate, Clone, ComputeSquaredDistance, Copy)]
 #[derive(PartialEq, ToAnimatedValue, ToAnimatedZero, ToComputedValue, ToCss)]
@@ -98,26 +98,27 @@ impl<A: Parse, B: Parse> Parse for Eithe
 /// https://drafts.csswg.org/css-values-4/#custom-idents
 #[derive(Clone, Debug, Eq, Hash, PartialEq, ToComputedValue)]
 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub struct CustomIdent(pub Atom);
 
 impl CustomIdent {
     /// Parse an already-tokenizer identifier
-    pub fn from_ident<'i>(ident: &CowRcStr<'i>, excluding: &[&str]) -> Result<Self, ParseError<'i>> {
+    pub fn from_ident<'i>(location: SourceLocation, ident: &CowRcStr<'i>, excluding: &[&str])
+                          -> Result<Self, ParseError<'i>> {
         let valid = match_ignore_ascii_case! { ident,
             "initial" | "inherit" | "unset" | "default" => false,
             _ => true
         };
         if !valid {
-            return Err(SelectorParseError::UnexpectedIdent(ident.clone()).into());
+            return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())));
         }
         if excluding.iter().any(|s| ident.eq_ignore_ascii_case(s)) {
-            Err(StyleParseError::UnspecifiedError.into())
+            Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         } else {
             Ok(CustomIdent(Atom::from(ident.as_ref())))
         }
     }
 }
 
 impl ToCss for CustomIdent {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
@@ -134,17 +135,18 @@ pub enum KeyframesName {
     Ident(CustomIdent),
     /// <string>
     QuotedString(Atom),
 }
 
 impl KeyframesName {
     /// https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-name
     pub fn from_ident(value: &str) -> Self {
-        let custom_ident = CustomIdent::from_ident(&value.into(), &["none"]).ok();
+        let location = SourceLocation { line: 0, column: 0 };
+        let custom_ident = CustomIdent::from_ident(location, &value.into(), &["none"]).ok();
         match custom_ident {
             Some(ident) => KeyframesName::Ident(ident),
             None => KeyframesName::QuotedString(value.into()),
         }
     }
 
     /// Create a new KeyframesName from Atom.
     #[cfg(feature = "gecko")]
@@ -177,20 +179,21 @@ impl PartialEq for KeyframesName {
 impl hash::Hash for KeyframesName {
     fn hash<H>(&self, state: &mut H) where H: hash::Hasher {
         self.as_atom().hash(state)
     }
 }
 
 impl Parse for KeyframesName {
     fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
+        let location = input.current_source_location();
         match input.next() {
-            Ok(&Token::Ident(ref s)) => Ok(KeyframesName::Ident(CustomIdent::from_ident(s, &["none"])?)),
+            Ok(&Token::Ident(ref s)) => Ok(KeyframesName::Ident(CustomIdent::from_ident(location, s, &["none"])?)),
             Ok(&Token::QuotedString(ref s)) => Ok(KeyframesName::QuotedString(Atom::from(s.as_ref()))),
-            Ok(t) => Err(BasicParseError::UnexpectedToken(t.clone()).into()),
+            Ok(t) => Err(location.new_unexpected_token_error(t.clone())),
             Err(e) => Err(e.into()),
         }
     }
 }
 
 impl ToCss for KeyframesName {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         match *self {
--- a/servo/components/style/values/specified/align.rs
+++ b/servo/components/style/values/specified/align.rs
@@ -4,20 +4,20 @@
 
 //! Values for CSS Box Alignment properties
 //!
 //! https://drafts.csswg.org/css-align/
 
 use cssparser::Parser;
 use gecko_bindings::structs;
 use parser::{Parse, ParserContext};
-use selectors::parser::SelectorParseError;
+use selectors::parser::SelectorParseErrorKind;
 use std::ascii::AsciiExt;
 use std::fmt;
-use style_traits::{ToCss, ParseError, StyleParseError};
+use style_traits::{ToCss, ParseError, StyleParseErrorKind};
 
 bitflags! {
     /// Constants shared by multiple CSS Box Alignment properties
     ///
     /// These constants match Gecko's `NS_STYLE_ALIGN_*` constants.
     #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
     #[derive(ToComputedValue)]
     pub flags AlignFlags: u8 {
@@ -196,17 +196,17 @@ impl Parse for AlignJustifyContent {
 
         // <*-position> followed by optional <content-distribution>
         if let Ok(fallback) = input.try(|input| parse_overflow_content_position(input)) {
             if let Ok(value) = input.try(|input| parse_content_distribution(input)) {
                 return Ok(AlignJustifyContent::with_fallback(value, fallback))
             }
             return Ok(AlignJustifyContent::new(fallback))
         }
-        Err(StyleParseError::UnspecifiedError.into())
+        Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     }
 }
 
 /// Value of the `align-self` or `justify-self` property.
 ///
 /// https://drafts.csswg.org/css-align/#self-alignment
 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
 #[derive(Clone, Copy, Debug, Eq, PartialEq, ToComputedValue, ToCss)]
@@ -234,17 +234,17 @@ impl Parse for AlignJustifySelf {
         // auto | normal | stretch | <baseline-position>
         if let Ok(value) = input.try(parse_auto_normal_stretch_baseline) {
             return Ok(AlignJustifySelf(value))
         }
         // [ <overflow-position>? && <self-position> ]
         if let Ok(value) = input.try(parse_overflow_self_position) {
             return Ok(AlignJustifySelf(value))
         }
-        Err(StyleParseError::UnspecifiedError.into())
+        Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     }
 }
 
 /// Value of the `align-items` property
 ///
 /// https://drafts.csswg.org/css-align/#self-alignment
 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
 #[derive(Clone, Copy, Debug, Eq, PartialEq, ToComputedValue, ToCss)]
@@ -272,17 +272,17 @@ impl Parse for AlignItems {
         // normal | stretch | <baseline-position>
         if let Ok(value) = input.try(parse_normal_stretch_baseline) {
             return Ok(AlignItems(value))
         }
         // [ <overflow-position>? && <self-position> ]
         if let Ok(value) = input.try(parse_overflow_self_position) {
             return Ok(AlignItems(value))
         }
-        Err(StyleParseError::UnspecifiedError.into())
+        Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     }
 }
 
 /// Value of the `justify-items` property
 ///
 /// https://drafts.csswg.org/css-align/#justify-items-property
 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
 #[derive(Clone, Copy, Debug, Eq, PartialEq, ToCss)]
@@ -321,17 +321,17 @@ impl Parse for JustifyItems {
         // [ legacy && [ left | right | center ] ]
         if let Ok(value) = input.try(parse_legacy) {
             return Ok(JustifyItems(value))
         }
         // [ <overflow-position>? && <self-position> ]
         if let Ok(value) = parse_overflow_self_position(input) {
             return Ok(JustifyItems(value))
         }
-        Err(StyleParseError::UnspecifiedError.into())
+        Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     }
 }
 
 #[cfg(feature = "gecko")]
 impl From<u16> for AlignJustifyContent {
     fn from(bits: u16) -> AlignJustifyContent {
         AlignJustifyContent(bits)
     }
@@ -346,30 +346,30 @@ impl From<AlignJustifyContent> for u16 {
 
 // auto | normal | stretch | <baseline-position>
 fn parse_auto_normal_stretch_baseline<'i, 't>(input: &mut Parser<'i, 't>)
                                               -> Result<AlignFlags, ParseError<'i>> {
     if let Ok(baseline) = input.try(parse_baseline) {
         return Ok(baseline);
     }
 
-    try_match_ident_ignore_ascii_case! { input.expect_ident()?,
+    try_match_ident_ignore_ascii_case! { input,
         "auto" => Ok(ALIGN_AUTO),
         "normal" => Ok(ALIGN_NORMAL),
         "stretch" => Ok(ALIGN_STRETCH),
     }
 }
 
 // normal | stretch | <baseline-position>
 fn parse_normal_stretch_baseline<'i, 't>(input: &mut Parser<'i, 't>) -> Result<AlignFlags, ParseError<'i>> {
     if let Ok(baseline) = input.try(parse_baseline) {
         return Ok(baseline);
     }
 
-    try_match_ident_ignore_ascii_case! { input.expect_ident()?,
+    try_match_ident_ignore_ascii_case! { input,
         "normal" => Ok(ALIGN_NORMAL),
         "stretch" => Ok(ALIGN_STRETCH),
     }
 }
 
 // normal | <baseline-position>
 fn parse_normal_or_baseline<'i, 't>(input: &mut Parser<'i, 't>) -> Result<AlignFlags, ParseError<'i>> {
     if let Ok(baseline) = input.try(parse_baseline) {
@@ -378,32 +378,32 @@ fn parse_normal_or_baseline<'i, 't>(inpu
 
     input.expect_ident_matching("normal")?;
     Ok(ALIGN_NORMAL)
 }
 
 // <baseline-position>
 fn parse_baseline<'i, 't>(input: &mut Parser<'i, 't>) -> Result<AlignFlags, ParseError<'i>> {
     // FIXME: remove clone() when lifetimes are non-lexical
-    try_match_ident_ignore_ascii_case! { input.expect_ident()?.clone(),
+    try_match_ident_ignore_ascii_case! { input,
         "baseline" => Ok(ALIGN_BASELINE),
         "first" => {
             input.expect_ident_matching("baseline")?;
             Ok(ALIGN_BASELINE)
         }
         "last" => {
             input.expect_ident_matching("baseline")?;
             Ok(ALIGN_LAST_BASELINE)
         }
     }
 }
 
 // <content-distribution>
 fn parse_content_distribution<'i, 't>(input: &mut Parser<'i, 't>) -> Result<AlignFlags, ParseError<'i>> {
-    try_match_ident_ignore_ascii_case! { input.expect_ident()?,
+    try_match_ident_ignore_ascii_case! { input,
         "stretch" => Ok(ALIGN_STRETCH),
         "space-between" => Ok(ALIGN_SPACE_BETWEEN),
         "space-around" => Ok(ALIGN_SPACE_AROUND),
         "space-evenly" => Ok(ALIGN_SPACE_EVENLY),
     }
 }
 
 // [ <overflow-position>? && <content-position> ]
@@ -416,35 +416,35 @@ fn parse_overflow_content_position<'i, '
         return Ok(content)
     }
     // <overflow-position> followed by required <content-position>
     if let Ok(overflow) = parse_overflow_position(input) {
         if let Ok(content) = parse_content_position(input) {
             return Ok(overflow | content)
         }
     }
-    return Err(StyleParseError::UnspecifiedError.into())
+    return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
 }
 
 // <content-position>
 fn parse_content_position<'i, 't>(input: &mut Parser<'i, 't>) -> Result<AlignFlags, ParseError<'i>> {
-    try_match_ident_ignore_ascii_case! { input.expect_ident()?,
+    try_match_ident_ignore_ascii_case! { input,
         "start" => Ok(ALIGN_START),
         "end" => Ok(ALIGN_END),
         "flex-start" => Ok(ALIGN_FLEX_START),
         "flex-end" => Ok(ALIGN_FLEX_END),
         "center" => Ok(ALIGN_CENTER),
         "left" => Ok(ALIGN_LEFT),
         "right" => Ok(ALIGN_RIGHT),
     }
 }
 
 // <overflow-position>
 fn parse_overflow_position<'i, 't>(input: &mut Parser<'i, 't>) -> Result<AlignFlags, ParseError<'i>> {
-    try_match_ident_ignore_ascii_case! { input.expect_ident()?,
+    try_match_ident_ignore_ascii_case! { input,
         "safe" => Ok(ALIGN_SAFE),
         "unsafe" => Ok(ALIGN_UNSAFE),
     }
 }
 
 // [ <overflow-position>? && <self-position> ]
 fn parse_overflow_self_position<'i, 't>(input: &mut Parser<'i, 't>) -> Result<AlignFlags, ParseError<'i>> {
     // <self-position> followed by optional <overflow-position>
@@ -455,48 +455,50 @@ fn parse_overflow_self_position<'i, 't>(
         return Ok(self_position)
     }
     // <overflow-position> followed by required <self-position>
     if let Ok(overflow) = parse_overflow_position(input) {
         if let Ok(self_position) = parse_self_position(input) {
             return Ok(overflow | self_position)
         }
     }
-    return Err(StyleParseError::UnspecifiedError.into())
+    return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
 }
 
 // <self-position>
 fn parse_self_position<'i, 't>(input: &mut Parser<'i, 't>) -> Result<AlignFlags, ParseError<'i>> {
-    try_match_ident_ignore_ascii_case! { input.expect_ident()?,
+    try_match_ident_ignore_ascii_case! { input,
         "start" => Ok(ALIGN_START),
         "end" => Ok(ALIGN_END),
         "flex-start" => Ok(ALIGN_FLEX_START),
         "flex-end" => Ok(ALIGN_FLEX_END),
         "center" => Ok(ALIGN_CENTER),
         "left" => Ok(ALIGN_LEFT),
         "right" => Ok(ALIGN_RIGHT),
         "self-start" => Ok(ALIGN_SELF_START),
         "self-end" => Ok(ALIGN_SELF_END),
     }
 }
 
 // [ legacy && [ left | right | center ] ]
 fn parse_legacy<'i, 't>(input: &mut Parser<'i, 't>) -> Result<AlignFlags, ParseError<'i>> {
+    let a_location = input.current_source_location();
     let a = input.expect_ident()?.clone();
+    let b_location = input.current_source_location();
     let b = input.expect_ident()?;
     if a.eq_ignore_ascii_case("legacy") {
         (match_ignore_ascii_case! { &b,
             "left" => Ok(ALIGN_LEGACY | ALIGN_LEFT),
             "right" => Ok(ALIGN_LEGACY | ALIGN_RIGHT),
             "center" => Ok(ALIGN_LEGACY | ALIGN_CENTER),
             _ => Err(())
-        }).map_err(|()| SelectorParseError::UnexpectedIdent(b.clone()).into())
+        }).map_err(|()| b_location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(b.clone())))
     } else if b.eq_ignore_ascii_case("legacy") {
         (match_ignore_ascii_case! { &a,
             "left" => Ok(ALIGN_LEGACY | ALIGN_LEFT),
             "right" => Ok(ALIGN_LEGACY | ALIGN_RIGHT),
             "center" => Ok(ALIGN_LEGACY | ALIGN_CENTER),
             _ => Err(())
-        }).map_err(|()| SelectorParseError::UnexpectedIdent(a).into())
+        }).map_err(|()| a_location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(a)))
     } else {
-        Err(StyleParseError::UnspecifiedError.into())
+        Err(a_location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     }
 }
--- a/servo/components/style/values/specified/angle.rs
+++ b/servo/components/style/values/specified/angle.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/. */
 
 //! Specified angles.
 
-use cssparser::{Parser, Token, BasicParseError};
+use cssparser::{Parser, Token};
 use parser::{ParserContext, Parse};
 use std::ascii::AsciiExt;
 use std::fmt;
 use style_traits::{ToCss, ParseError};
 use values::CSSFloat;
 use values::computed::{Context, ToComputedValue};
 use values::computed::angle::Angle as ComputedAngle;
 use values::specified::calc::CalcNode;
@@ -107,17 +107,17 @@ impl Parse for Angle {
         match token {
             Token::Dimension { value, ref unit, .. } => {
                 Angle::parse_dimension(value, unit, /* from_calc = */ false)
             }
             Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
                 return input.parse_nested_block(|i| CalcNode::parse_angle(context, i))
             }
             _ => Err(())
-        }.map_err(|()| BasicParseError::UnexpectedToken(token.clone()).into())
+        }.map_err(|()| input.new_unexpected_token_error(token.clone()))
     }
 }
 
 impl Angle {
     /// Parse an `<angle>` value given a value and an unit.
     pub fn parse_dimension(
         value: CSSFloat,
         unit: &str,
@@ -150,11 +150,11 @@ impl Angle {
             Token::Dimension { value, ref unit, .. } => {
                 Angle::parse_dimension(value, unit, /* from_calc = */ false)
             }
             Token::Number { value, .. } if value == 0. => Ok(Angle::zero()),
             Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
                 return input.parse_nested_block(|i| CalcNode::parse_angle(context, i))
             }
             _ => Err(())
-        }.map_err(|()| BasicParseError::UnexpectedToken(token.clone()).into())
+        }.map_err(|()| input.new_unexpected_token_error(token.clone()))
     }
 }
--- a/servo/components/style/values/specified/background.rs
+++ b/servo/components/style/values/specified/background.rs
@@ -1,38 +1,39 @@
 /* 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/. */
 
 //! Specified types for CSS values related to backgrounds.
 
 use cssparser::Parser;
 use parser::{Parse, ParserContext};
-use selectors::parser::SelectorParseError;
+use selectors::parser::SelectorParseErrorKind;
 use style_traits::ParseError;
 use values::generics::background::BackgroundSize as GenericBackgroundSize;
 use values::specified::length::LengthOrPercentageOrAuto;
 
 /// A specified value for the `background-size` property.
 pub type BackgroundSize = GenericBackgroundSize<LengthOrPercentageOrAuto>;
 
 impl Parse for BackgroundSize {
     fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
         if let Ok(width) = input.try(|i| LengthOrPercentageOrAuto::parse_non_negative(context, i)) {
             let height = input
                 .try(|i| LengthOrPercentageOrAuto::parse_non_negative(context, i))
                 .unwrap_or(LengthOrPercentageOrAuto::Auto);
             return Ok(GenericBackgroundSize::Explicit { width, height });
         }
+        let location = input.current_source_location();
         let ident = input.expect_ident()?;
         (match_ignore_ascii_case! { &ident,
             "cover" => Ok(GenericBackgroundSize::Cover),
             "contain" => Ok(GenericBackgroundSize::Contain),
             _ => Err(()),
-        }).map_err(|()| SelectorParseError::UnexpectedIdent(ident.clone()).into())
+        }).map_err(|()| location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())))
     }
 }
 
 impl BackgroundSize {
     /// Returns `auto auto`.
     pub fn auto() -> Self {
         GenericBackgroundSize::Explicit {
             width: LengthOrPercentageOrAuto::Auto,
--- a/servo/components/style/values/specified/basic_shape.rs
+++ b/servo/components/style/values/specified/basic_shape.rs
@@ -6,17 +6,17 @@
 //! [`basic-shape`][basic-shape]s
 //!
 //! [basic-shape]: https://drafts.csswg.org/css-shapes/#typedef-basic-shape
 
 use cssparser::Parser;
 use parser::{Parse, ParserContext};
 use std::borrow::Cow;
 use std::fmt;
-use style_traits::{ToCss, ParseError, StyleParseError};
+use style_traits::{ToCss, ParseError, StyleParseErrorKind};
 use values::computed::Percentage;
 use values::generics::basic_shape::{Circle as GenericCircle};
 use values::generics::basic_shape::{ClippingShape as GenericClippingShape, Ellipse as GenericEllipse};
 use values::generics::basic_shape::{FillRule, BasicShape as GenericBasicShape};
 use values::generics::basic_shape::{FloatAreaShape as GenericFloatAreaShape, InsetRect as GenericInsetRect};
 use values::generics::basic_shape::{GeometryBox, ShapeBox, ShapeSource};
 use values::generics::basic_shape::{Polygon as GenericPolygon, ShapeRadius as GenericShapeRadius};
 use values::generics::rect::Rect;
@@ -76,45 +76,46 @@ impl<ReferenceBox: Parse> Parse for Shap
               parse_component(context, input, &mut ref_box) {
             //
         }
 
         if let Some(shp) = shape {
             return Ok(ShapeSource::Shape(shp, ref_box))
         }
 
-        ref_box.map(|v| ShapeSource::Box(v)).ok_or(StyleParseError::UnspecifiedError.into())
+        ref_box.map(|v| ShapeSource::Box(v)).ok_or(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     }
 }
 
 impl Parse for GeometryBox {
     fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
         if let Ok(shape_box) = input.try(|i| ShapeBox::parse(i)) {
             return Ok(GeometryBox::ShapeBox(shape_box))
         }
 
-        try_match_ident_ignore_ascii_case! { input.expect_ident()?,
+        try_match_ident_ignore_ascii_case! { input,
             "fill-box" => Ok(GeometryBox::FillBox),
             "stroke-box" => Ok(GeometryBox::StrokeBox),
             "view-box" => Ok(GeometryBox::ViewBox),
         }
     }
 }
 
 impl Parse for BasicShape {
     fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
+        let location = input.current_source_location();
         let function = input.expect_function()?.clone();
         input.parse_nested_block(move |i| {
             (match_ignore_ascii_case! { &function,
                 "inset" => return InsetRect::parse_function_arguments(context, i).map(GenericBasicShape::Inset),
                 "circle" => return Circle::parse_function_arguments(context, i).map(GenericBasicShape::Circle),
                 "ellipse" => return Ellipse::parse_function_arguments(context, i).map(GenericBasicShape::Ellipse),
                 "polygon" => return Polygon::parse_function_arguments(context, i).map(GenericBasicShape::Polygon),
                 _ => Err(())
-            }).map_err(|()| StyleParseError::UnexpectedFunction(function.clone()).into())
+            }).map_err(|()| location.new_custom_error(StyleParseErrorKind::UnexpectedFunction(function.clone())))
         })
     }
 }
 
 impl Parse for InsetRect {
     fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
         input.expect_function_matching("inset")?;
         input.parse_nested_block(|i| Self::parse_function_arguments(context, i))
@@ -224,17 +225,17 @@ impl ToCss for Ellipse {
 
 impl Parse for ShapeRadius {
     fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
                      -> Result<Self, ParseError<'i>> {
         if let Ok(lop) = input.try(|i| LengthOrPercentage::parse_non_negative(context, i)) {
             return Ok(GenericShapeRadius::Length(lop))
         }
 
-        try_match_ident_ignore_ascii_case! { input.expect_ident()?,
+        try_match_ident_ignore_ascii_case! { input,
             "closest-side" => Ok(GenericShapeRadius::ClosestSide),
             "farthest-side" => Ok(GenericShapeRadius::FarthestSide),
         }
     }
 }
 
 /// https://drafts.csswg.org/css-shapes/#basic-shape-serialization
 ///
--- a/servo/components/style/values/specified/border.rs
+++ b/servo/components/style/values/specified/border.rs
@@ -57,17 +57,17 @@ impl BorderSideWidth {
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
         allow_quirks: AllowQuirks)
         -> Result<Self, ParseError<'i>>
     {
         if let Ok(length) = input.try(|i| Length::parse_non_negative_quirky(context, i, allow_quirks)) {
             return Ok(BorderSideWidth::Length(length));
         }
-        try_match_ident_ignore_ascii_case! { input.expect_ident()?,
+        try_match_ident_ignore_ascii_case! { input,
             "thin" => Ok(BorderSideWidth::Thin),
             "medium" => Ok(BorderSideWidth::Medium),
             "thick" => Ok(BorderSideWidth::Thick),
         }
     }
 }
 
 impl Parse for BorderSideWidth {
--- a/servo/components/style/values/specified/box.rs
+++ b/servo/components/style/values/specified/box.rs
@@ -18,17 +18,17 @@ impl Parse for VerticalAlign {
     fn parse<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
         if let Ok(lop) = input.try(|i| LengthOrPercentage::parse_quirky(context, i, AllowQuirks::Yes)) {
             return Ok(GenericVerticalAlign::Length(lop));
         }
 
-        try_match_ident_ignore_ascii_case! { input.expect_ident()?,
+        try_match_ident_ignore_ascii_case! { input,
             "baseline" => Ok(GenericVerticalAlign::Baseline),
             "sub" => Ok(GenericVerticalAlign::Sub),
             "super" => Ok(GenericVerticalAlign::Super),
             "top" => Ok(GenericVerticalAlign::Top),
             "text-top" => Ok(GenericVerticalAlign::TextTop),
             "middle" => Ok(GenericVerticalAlign::Middle),
             "bottom" => Ok(GenericVerticalAlign::Bottom),
             "text-bottom" => Ok(GenericVerticalAlign::TextBottom),
--- a/servo/components/style/values/specified/calc.rs
+++ b/servo/components/style/values/specified/calc.rs
@@ -1,21 +1,21 @@
 /* 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/. */
 
 //! [Calc expressions][calc].
 //!
 //! [calc]: https://drafts.csswg.org/css-values/#calc-notation
 
-use cssparser::{Parser, Token, BasicParseError};
+use cssparser::{Parser, Token};
 use parser::ParserContext;
 use std::ascii::AsciiExt;
 use std::fmt;
-use style_traits::{ToCss, ParseError, StyleParseError};
+use style_traits::{ToCss, ParseError, StyleParseErrorKind};
 use style_traits::values::specified::AllowedNumericType;
 use values::{CSSInteger, CSSFloat};
 use values::computed;
 use values::specified::{Angle, Time};
 use values::specified::length::{AbsoluteLength, FontRelativeLength, NoCalcLength};
 use values::specified::length::ViewportPercentageLength;
 
 /// A node inside a `Calc` expression's AST.
@@ -165,42 +165,43 @@ impl CalcNode {
     ///
     /// May return a "complex" `CalcNode`, in the presence of a parenthesized
     /// expression, for example.
     fn parse_one<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
         expected_unit: CalcUnit
     ) -> Result<Self, ParseError<'i>> {
+        let location = input.current_source_location();
         // FIXME: remove early returns when lifetimes are non-lexical
         match (input.next()?, expected_unit) {
             (&Token::Number { value, .. }, _) => return Ok(CalcNode::Number(value)),
             (&Token::Dimension { value, ref unit, .. }, CalcUnit::Length) |
             (&Token::Dimension { value, ref unit, .. }, CalcUnit::LengthOrPercentage) => {
                 return NoCalcLength::parse_dimension(context, value, unit)
                     .map(CalcNode::Length)
-                    .map_err(|()| StyleParseError::UnspecifiedError.into())
+                    .map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
             }
             (&Token::Dimension { value, ref unit, .. }, CalcUnit::Angle) => {
                 return Angle::parse_dimension(value, unit, /* from_calc = */ true)
                     .map(CalcNode::Angle)
-                    .map_err(|()| StyleParseError::UnspecifiedError.into())
+                    .map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
             }
             (&Token::Dimension { value, ref unit, .. }, CalcUnit::Time) => {
                 return Time::parse_dimension(value, unit, /* from_calc = */ true)
                     .map(CalcNode::Time)
-                    .map_err(|()| StyleParseError::UnspecifiedError.into())
+                    .map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
             }
             (&Token::Percentage { unit_value, .. }, CalcUnit::LengthOrPercentage) |
             (&Token::Percentage { unit_value, .. }, CalcUnit::Percentage) => {
                 return Ok(CalcNode::Percentage(unit_value))
             }
             (&Token::ParenthesisBlock, _) => {}
             (&Token::Function(ref name), _) if name.eq_ignore_ascii_case("calc") => {}
-            (t, _) => return Err(BasicParseError::UnexpectedToken(t.clone()).into())
+            (t, _) => return Err(location.new_unexpected_token_error(t.clone()))
         }
         input.parse_nested_block(|i| {
             CalcNode::parse(context, i, expected_unit)
         })
     }
 
     /// Parse a top-level `calc` expression, with all nested sub-expressions.
     ///
@@ -231,17 +232,17 @@ impl CalcNode {
                         }
                         Token::Delim('-') => {
                             let rhs =
                                 Self::parse_product(context, input, expected_unit)?;
                             let new_root =
                                 CalcNode::Sub(Box::new(root), Box::new(rhs));
                             root = new_root;
                         }
-                        t => return Err(BasicParseError::UnexpectedToken(t).into()),
+                        t => return Err(input.new_unexpected_token_error(t)),
                     }
                 }
                 _ => {
                     input.reset(&start);
                     break
                 }
             }
         }
@@ -554,73 +555,73 @@ impl CalcNode {
     /// Convenience parsing function for integers.
     pub fn parse_integer<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>
     ) -> Result<CSSInteger, ParseError<'i>> {
         Self::parse(context, input, CalcUnit::Integer)?
             .to_number()
             .map(|n| n as CSSInteger)
-            .map_err(|()| StyleParseError::UnspecifiedError.into())
+            .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     }
 
     /// Convenience parsing function for `<length> | <percentage>`.
     pub fn parse_length_or_percentage<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
         clamping_mode: AllowedNumericType
     ) -> Result<CalcLengthOrPercentage, ParseError<'i>> {
         Self::parse(context, input, CalcUnit::LengthOrPercentage)?
             .to_length_or_percentage(clamping_mode)
-            .map_err(|()| StyleParseError::UnspecifiedError.into())
+            .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     }
 
     /// Convenience parsing function for percentages.
     pub fn parse_percentage<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>
     ) -> Result<CSSFloat, ParseError<'i>> {
         Self::parse(context, input, CalcUnit::Percentage)?
             .to_percentage()
-            .map_err(|()| StyleParseError::UnspecifiedError.into())
+            .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     }
 
     /// Convenience parsing function for `<length>`.
     pub fn parse_length<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
         clamping_mode: AllowedNumericType
     ) -> Result<CalcLengthOrPercentage, ParseError<'i>> {
         Self::parse(context, input, CalcUnit::Length)?
             .to_length_or_percentage(clamping_mode)
-            .map_err(|()| StyleParseError::UnspecifiedError.into())
+            .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     }
 
     /// Convenience parsing function for `<number>`.
     pub fn parse_number<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>
     ) -> Result<CSSFloat, ParseError<'i>> {
         Self::parse(context, input, CalcUnit::Number)?
             .to_number()
-            .map_err(|()| StyleParseError::UnspecifiedError.into())
+            .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     }
 
     /// Convenience parsing function for `<angle>`.
     pub fn parse_angle<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>
     ) -> Result<Angle, ParseError<'i>> {
         Self::parse(context, input, CalcUnit::Angle)?
             .to_angle()
-            .map_err(|()| StyleParseError::UnspecifiedError.into())
+            .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     }
 
     /// Convenience parsing function for `<time>`.
     pub fn parse_time<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>
     ) -> Result<Time, ParseError<'i>> {
         Self::parse(context, input, CalcUnit::Time)?
             .to_time()
-            .map_err(|()| StyleParseError::UnspecifiedError.into())
+            .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     }
 }
--- a/servo/components/style/values/specified/color.rs
+++ b/servo/components/style/values/specified/color.rs
@@ -1,24 +1,24 @@
 /* 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/. */
 
 //! Specified color values.
 
-use cssparser::{Color as CSSParserColor, Parser, RGBA, Token, BasicParseError};
+use cssparser::{Color as CSSParserColor, Parser, RGBA, Token, BasicParseError, BasicParseErrorKind};
 #[cfg(feature = "gecko")]
 use gecko_bindings::structs::nscolor;
 use itoa;
 use parser::{ParserContext, Parse};
 #[cfg(feature = "gecko")]
 use properties::longhands::system_colors::SystemColor;
 use std::fmt;
 use std::io::Write;
-use style_traits::{ToCss, ParseError, StyleParseError, ValueParseError};
+use style_traits::{ToCss, ParseError, StyleParseErrorKind, ValueParseErrorKind};
 use super::AllowQuirks;
 use values::computed::{Color as ComputedColor, Context, ToComputedValue};
 
 /// Specified color value
 #[derive(Clone, Debug, PartialEq)]
 #[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub enum Color {
@@ -89,18 +89,21 @@ impl Parse for Color {
                 #[cfg(feature = "gecko")] {
                     if let Ok(system) = input.try(SystemColor::parse) {
                         return Ok(Color::System(system));
                     } else if let Ok(c) = gecko::SpecialColorKeyword::parse(input) {
                         return Ok(Color::Special(c));
                     }
                 }
                 match e {
-                    BasicParseError::UnexpectedToken(t) =>
-                        Err(StyleParseError::ValueError(ValueParseError::InvalidColor(t)).into()),
+                    BasicParseError { kind: BasicParseErrorKind::UnexpectedToken(t), location } => {
+                        Err(location.new_custom_error(
+                            StyleParseErrorKind::ValueError(ValueParseErrorKind::InvalidColor(t))
+                        ))
+                    }
                     e => Err(e.into())
                 }
             }
         }
     }
 }
 
 impl ToCss for Color {
@@ -174,65 +177,68 @@ impl Color {
                 .map_err(|_| e)
         })
     }
 
     /// Parse a <quirky-color> value.
     ///
     /// https://quirks.spec.whatwg.org/#the-hashless-hex-color-quirk
     fn parse_quirky_color<'i, 't>(input: &mut Parser<'i, 't>) -> Result<RGBA, ParseError<'i>> {
+        let location = input.current_source_location();
         let (value, unit) = match *input.next()? {
             Token::Number { int_value: Some(integer), .. } => {
                 (integer, None)
             },
             Token::Dimension { int_value: Some(integer), ref unit, .. } => {
                 (integer, Some(unit))
             },
             Token::Ident(ref ident) => {
                 if ident.len() != 3 && ident.len() != 6 {
-                    return Err(StyleParseError::UnspecifiedError.into());
+                    return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
                 }
                 return parse_hash_color(ident.as_bytes())
-                    .map_err(|()| StyleParseError::UnspecifiedError.into());
+                    .map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
             }
             ref t => {
-                return Err(BasicParseError::UnexpectedToken(t.clone()).into());
+                return Err(location.new_unexpected_token_error(t.clone()));
             },
         };
         if value < 0 {
-            return Err(StyleParseError::UnspecifiedError.into());
+            return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
         }
         let length = if value <= 9 {
             1
         } else if value <= 99 {
             2
         } else if value <= 999 {
             3
         } else if value <= 9999 {
             4
         } else if value <= 99999 {
             5
         } else if value <= 999999 {
             6
         } else {
-            return Err(StyleParseError::UnspecifiedError.into())
+            return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         };
         let total = length + unit.as_ref().map_or(0, |d| d.len());
         if total > 6 {
-            return Err(StyleParseError::UnspecifiedError.into());
+            return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
         }
         let mut serialization = [b'0'; 6];
         let space_padding = 6 - total;
         let mut written = space_padding;
         written += itoa::write(&mut serialization[written..], value).unwrap();
         if let Some(unit) = unit {
             written += (&mut serialization[written..]).write(unit.as_bytes()).unwrap();
         }
         debug_assert!(written == 6);
-        parse_hash_color(&serialization).map_err(|()| StyleParseError::UnspecifiedError.into())
+        parse_hash_color(&serialization).map_err(|()| {
+            location.new_custom_error(StyleParseErrorKind::UnspecifiedError)
+        })
     }
 
     /// Returns false if the color is completely transparent, and
     /// true otherwise.
     pub fn is_non_transparent(&self) -> bool {
         match *self {
             Color::Numeric { ref parsed, .. } => parsed.alpha != 0,
             _ => true,
--- a/servo/components/style/values/specified/effects.rs
+++ b/servo/components/style/values/specified/effects.rs
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Specified types for CSS values related to effects.
 
-use cssparser::{Parser, Token, BasicParseError};
+use cssparser::{self, Parser, Token, BasicParseErrorKind};
 use parser::{Parse, ParserContext};
-use style_traits::{ParseError, StyleParseError, ValueParseError};
+use style_traits::{ParseError, StyleParseErrorKind, ValueParseErrorKind};
 #[cfg(not(feature = "gecko"))]
 use values::Impossible;
 use values::computed::{Context, NonNegativeNumber as ComputedNonNegativeNumber, ToComputedValue};
 use values::computed::effects::BoxShadow as ComputedBoxShadow;
 use values::computed::effects::SimpleShadow as ComputedSimpleShadow;
 use values::generics::NonNegative;
 use values::generics::effects::BoxShadow as GenericBoxShadow;
 use values::generics::effects::Filter as GenericFilter;
@@ -133,17 +133,17 @@ impl Parse for BoxShadow {
                 if let Ok(value) = input.try(|i| RGBAColor::parse(context, i)) {
                     color = Some(value);
                     continue;
                 }
             }
             break;
         }
 
-        let lengths = lengths.ok_or(StyleParseError::UnspecifiedError)?;
+        let lengths = lengths.ok_or(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))?;
         Ok(BoxShadow {
             base: SimpleShadow {
                 color: color,
                 horizontal: lengths.0,
                 vertical: lengths.1,
                 blur: lengths.2,
             },
             spread: lengths.3,
@@ -181,20 +181,25 @@ impl Parse for Filter {
         input: &mut Parser<'i, 't>
     ) -> Result<Self, ParseError<'i>> {
         #[cfg(feature = "gecko")]
         {
             if let Ok(url) = input.try(|i| SpecifiedUrl::parse(context, i)) {
                 return Ok(GenericFilter::Url(url));
             }
         }
+        let location = input.current_source_location();
         let function = match input.expect_function() {
             Ok(f) => f.clone(),
-            Err(BasicParseError::UnexpectedToken(t)) =>
-                return Err(ValueParseError::InvalidFilter(t.clone()).into()),
+            Err(cssparser::BasicParseError {
+                kind: BasicParseErrorKind::UnexpectedToken(t),
+                location,
+            }) => {
+                return Err(location.new_custom_error(ValueParseErrorKind::InvalidFilter(t)))
+            }
             Err(e) => return Err(e.into()),
         };
         input.parse_nested_block(|i| {
             match_ignore_ascii_case! { &*function,
                 "blur" => Ok(GenericFilter::Blur((Length::parse_non_negative(context, i)?).into())),
                 "brightness" => Ok(GenericFilter::Brightness(Factor::parse(context, i)?)),
                 "contrast" => Ok(GenericFilter::Contrast(Factor::parse(context, i)?)),
                 "grayscale" => {
@@ -215,17 +220,19 @@ impl Parse for Filter {
                 },
                 "saturate" => Ok(GenericFilter::Saturate(Factor::parse(context, i)?)),
                 "sepia" => {
                     // Values of amount over 100% are allowed but UAs must clamp the values to 1.
                     // https://drafts.fxtf.org/filter-effects/#funcdef-filter-sepia
                     Ok(GenericFilter::Sepia(Factor::parse_with_clamping_to_one(context, i)?))
                 },
                 "drop-shadow" => Ok(GenericFilter::DropShadow(Parse::parse(context, i)?)),
-                _ => Err(ValueParseError::InvalidFilter(Token::Function(function.clone())).into()),
+                _ => Err(location.new_custom_error(
+                    ValueParseErrorKind::InvalidFilter(Token::Function(function.clone()))
+                )),
             }
         })
     }
 }
 
 impl Parse for SimpleShadow {
     #[inline]
     fn parse<'i, 't>(
--- a/servo/components/style/values/specified/flex.rs
+++ b/servo/components/style/values/specified/flex.rs
@@ -16,14 +16,14 @@ pub type FlexBasis = GenericFlexBasis<Le
 impl Parse for FlexBasis {
     fn parse<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>)
     -> Result<Self, ParseError<'i>> {
         if let Ok(length) = input.try(|i| LengthOrPercentage::parse_non_negative(context, i)) {
             return Ok(GenericFlexBasis::Length(length));
         }
-        try_match_ident_ignore_ascii_case! { input.expect_ident()?,
+        try_match_ident_ignore_ascii_case! { input,
             "auto" => Ok(GenericFlexBasis::Auto),
             "content" => Ok(GenericFlexBasis::Content),
         }
     }
 }
--- a/servo/components/style/values/specified/font.rs
+++ b/servo/components/style/values/specified/font.rs
@@ -78,17 +78,17 @@ pub enum KeywordSize {
     // This is not a real font keyword and will not parse
     // HTML font-size 7 corresponds to this value
     XXXLarge,
 }
 
 impl KeywordSize {
     /// Parse a keyword size
     pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
-        try_match_ident_ignore_ascii_case! { input.expect_ident()?,
+        try_match_ident_ignore_ascii_case! { input,
             "xx-small" => Ok(KeywordSize::XXSmall),
             "x-small" => Ok(KeywordSize::XSmall),
             "small" => Ok(KeywordSize::Small),
             "medium" => Ok(KeywordSize::Medium),
             "large" => Ok(KeywordSize::Large),
             "x-large" => Ok(KeywordSize::XLarge),
             "xx-large" => Ok(KeywordSize::XXLarge),
         }
--- a/servo/components/style/values/specified/grid.rs
+++ b/servo/components/style/values/specified/grid.rs
@@ -1,32 +1,33 @@
 /* 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/. */
 
 //! CSS handling for the computed value of
 //! [grids](https://drafts.csswg.org/css-grid/)
 
-use cssparser::{Parser, Token, BasicParseError};
+use cssparser::{Parser, Token, ParseError as CssParseError};
 use parser::{Parse, ParserContext};
 use std::ascii::AsciiExt;
 use std::mem;
-use style_traits::{ParseError, StyleParseError};
+use style_traits::{ParseError, StyleParseErrorKind};
 use values::{CSSFloat, CustomIdent};
 use values::computed::{self, Context, ToComputedValue};
 use values::generics::grid::{GridTemplateComponent, RepeatCount, TrackBreadth, TrackKeyword, TrackRepeat};
 use values::generics::grid::{LineNameList, TrackSize, TrackList, TrackListType, TrackListValue};
 use values::specified::{LengthOrPercentage, Integer};
 
 /// Parse a single flexible length.
 pub fn parse_flex<'i, 't>(input: &mut Parser<'i, 't>) -> Result<CSSFloat, ParseError<'i>> {
+    let location = input.current_source_location();
     match *input.next()? {
         Token::Dimension { value, ref unit, .. } if unit.eq_ignore_ascii_case("fr") && value.is_sign_positive()
             => Ok(value),
-        ref t => Err(BasicParseError::UnexpectedToken(t.clone()).into()),
+        ref t => Err(location.new_unexpected_token_error(t.clone())),
     }
 }
 
 impl Parse for TrackBreadth<LengthOrPercentage> {
     fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
         if let Ok(lop) = input.try(|i| LengthOrPercentage::parse_non_negative(context, i)) {
             return Ok(TrackBreadth::Breadth(lop))
         }
@@ -69,18 +70,20 @@ impl Parse for TrackSize<LengthOrPercent
 
 /// Parse the grid line names into a vector of owned strings.
 ///
 /// https://drafts.csswg.org/css-grid/#typedef-line-names
 pub fn parse_line_names<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Box<[CustomIdent]>, ParseError<'i>> {
     input.expect_square_bracket_block()?;
     input.parse_nested_block(|input| {
         let mut values = vec![];
-        while let Ok(ident) = input.try(|i| i.expect_ident_cloned()) {
-            let ident = CustomIdent::from_ident(&ident, &["span"])?;
+        while let Ok((loc, ident)) = input.try(|i| -> Result<_, CssParseError<()>> {
+             Ok((i.current_source_location(), i.expect_ident_cloned()?))
+        }) {
+            let ident = CustomIdent::from_ident(loc, &ident, &["span"])?;
             values.push(ident);
         }
 
         Ok(values.into_boxed_slice())
     })
 }
 
 /// The type of `repeat` function (only used in parsing).
@@ -119,17 +122,17 @@ impl TrackRepeat<LengthOrPercentage, Int
                 let mut current_names;
 
                 loop {
                     current_names = input.try(parse_line_names).unwrap_or(vec![].into_boxed_slice());
                     if let Ok(track_size) = input.try(|i| TrackSize::parse(context, i)) {
                         if !track_size.is_fixed() {
                             if is_auto {
                                 // should be <fixed-size> for <auto-repeat>
-                                return Err(StyleParseError::UnspecifiedError.into())
+                                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
                             }
 
                             if repeat_type == RepeatType::Fixed {
                                 repeat_type = RepeatType::Normal       // <track-size> for sure
                             }
                         }
 
                         values.push(track_size);
@@ -142,17 +145,17 @@ impl TrackRepeat<LengthOrPercentage, Int
                             // but we are adding this for gecko parity. We should remove this when
                             // gecko implements new spec.
                             names.push(input.try(parse_line_names).unwrap_or(vec![].into_boxed_slice()));
                             break
                         }
                     } else {
                         if values.is_empty() {
                             // expecting at least one <track-size>
-                            return Err(StyleParseError::UnspecifiedError.into())
+                            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
                         }
 
                         names.push(current_names);      // final `<line-names>`
                         break       // no more <track-size>, breaking
                     }
                 }
 
                 let repeat = TrackRepeat {
@@ -186,39 +189,39 @@ impl Parse for TrackList<LengthOrPercent
         let mut atleast_one_not_fixed = false;
         loop {
             current_names.extend_from_slice(&mut input.try(parse_line_names).unwrap_or(vec![].into_boxed_slice()));
             if let Ok(track_size) = input.try(|i| TrackSize::parse(context, i)) {
                 if !track_size.is_fixed() {
                     atleast_one_not_fixed = true;
                     if auto_repeat.is_some() {
                         // <auto-track-list> only accepts <fixed-size> and <fixed-repeat>
-                        return Err(StyleParseError::UnspecifiedError.into())
+                        return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
                     }
                 }
 
                 let vec = mem::replace(&mut current_names, vec![]);
                 names.push(vec.into_boxed_slice());
                 values.push(TrackListValue::TrackSize(track_size));
             } else if let Ok((repeat, type_)) = input.try(|i| TrackRepeat::parse_with_repeat_type(context, i)) {
                 if list_type == TrackListType::Explicit {
                     list_type = TrackListType::Normal;      // <explicit-track-list> doesn't contain repeat()
                 }
 
                 match type_ {
                     RepeatType::Normal => {
                         atleast_one_not_fixed = true;
                         if auto_repeat.is_some() { // only <fixed-repeat>
-                            return Err(StyleParseError::UnspecifiedError.into())
+                            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
                         }
                     },
                     RepeatType::Auto => {
                         if auto_repeat.is_some() || atleast_one_not_fixed {
                             // We've either seen <auto-repeat> earlier, or there's at least one non-fixed value
-                            return Err(StyleParseError::UnspecifiedError.into())
+                            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
                         }
 
                         list_type = TrackListType::Auto(values.len() as u16 + auto_offset);
                         auto_repeat = Some(repeat);
                         let vec = mem::replace(&mut current_names, vec![]);
                         names.push(vec.into_boxed_slice());
                         continue;
                     },
@@ -228,17 +231,17 @@ impl Parse for TrackList<LengthOrPercent
                 let vec = mem::replace(&mut current_names, vec![]);
                 names.push(vec.into_boxed_slice());
                 if let RepeatCount::Number(num) = repeat.count {
                     auto_offset += (num.value() - 1) as u16;
                 }
                 values.push(TrackListValue::TrackRepeat(repeat));
             } else {
                 if values.is_empty() && auto_repeat.is_none() {
-                    return Err(StyleParseError::UnspecifiedError.into())
+                    return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
                 }
 
                 names.push(current_names.into_boxed_slice());
                 break
             }
         }
 
         Ok(TrackList {
--- a/servo/components/style/values/specified/image.rs
+++ b/servo/components/style/values/specified/image.rs
@@ -3,26 +3,26 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! CSS handling for the specified value of
 //! [`image`][image]s
 //!
 //! [image]: https://drafts.csswg.org/css-images/#image-values
 
 use Atom;
-use cssparser::{Parser, Token, BasicParseError};
+use cssparser::{Parser, Token};
 use custom_properties::SpecifiedValue;
 use parser::{Parse, ParserContext};
-use selectors::parser::SelectorParseError;
+use selectors::parser::SelectorParseErrorKind;
 #[cfg(feature = "servo")]
 use servo_url::ServoUrl;
 use std::cmp::Ordering;
 use std::f32::consts::PI;
 use std::fmt;
-use style_traits::{ToCss, ParseError, StyleParseError};
+use style_traits::{ToCss, ParseError, StyleParseErrorKind};
 use values::{Either, None_};
 #[cfg(feature = "gecko")]
 use values::computed::{Context, Position as ComputedPosition, ToComputedValue};
 use values::generics::image::{Circle, CompatMode, Ellipse, ColorStop as GenericColorStop};
 use values::generics::image::{EndingShape as GenericEndingShape, Gradient as GenericGradient};
 use values::generics::image::{GradientItem as GenericGradientItem, GradientKind as GenericGradientKind};
 use values::generics::image::{Image as GenericImage, LineDirection as GenericsLineDirection};
 use values::generics::image::{MozImageRect as GenericMozImageRect, ShapeExtent};
@@ -164,20 +164,21 @@ impl Image {
     #[cfg(feature = "servo")]
     pub fn for_cascade(url: ServoUrl) -> Self {
         GenericImage::Url(SpecifiedUrl::for_cascade(url))
     }
 
     /// Parses a `-moz-element(# <element-id>)`.
     fn parse_element<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Atom, ParseError<'i>> {
         input.try(|i| i.expect_function_matching("-moz-element"))?;
+        let location = input.current_source_location();
         input.parse_nested_block(|i| {
             match *i.next()? {
                 Token::IDHash(ref id) => Ok(Atom::from(id.as_ref())),
-                ref t => Err(BasicParseError::UnexpectedToken(t.clone()).into()),
+                ref t => Err(location.new_unexpected_token_error(t.clone())),
             }
         })
     }
 }
 
 impl Parse for Gradient {
     fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
         enum Shape {
@@ -231,30 +232,30 @@ impl Parse for Gradient {
             "-webkit-gradient" => {
                 return input.parse_nested_block(|i| Self::parse_webkit_gradient_argument(context, i));
             },
             _ => None,
         };
 
         let (shape, repeating, mut compat_mode) = match result {
             Some(result) => result,
-            None => return Err(StyleParseError::UnexpectedFunction(func.clone()).into()),
+            None => return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedFunction(func.clone()))),
         };
 
         let (kind, items) = input.parse_nested_block(|i| {
             let shape = match shape {
                 Shape::Linear => GradientKind::parse_linear(context, i, &mut compat_mode)?,
                 Shape::Radial => GradientKind::parse_radial(context, i, &mut compat_mode)?,
             };
             let items = GradientItem::parse_comma_separated(context, i)?;
             Ok((shape, items))
         })?;
 
         if items.len() < 2 {
-            return Err(StyleParseError::UnspecifiedError.into());
+            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
         }
 
         Ok(Gradient {
             items: items,
             repeating: repeating,
             kind: kind,
             compat_mode: compat_mode,
         })
@@ -430,17 +431,17 @@ impl Gradient {
                 }
 
                 #[cfg(not(feature = "gecko"))]
                 {
                     let kind = GenericGradientKind::Radial(shape, position, None);
                     (kind, reverse_stops)
                 }
             },
-            _ => return Err(SelectorParseError::UnexpectedIdent(ident.clone()).into()),
+            _ => return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone()))),
         };
 
         let mut items = input.try(|i| {
             i.expect_comma()?;
             i.parse_comma_separated(|i| {
                 let function = i.expect_function()?.clone();
                 let (color, mut p) = i.parse_nested_block(|i| {
                     let p = match_ignore_ascii_case! { &function,
@@ -449,21 +450,21 @@ impl Gradient {
                                 NumberOrPercentage::Number(number) => Percentage::new(number.value),
                                 NumberOrPercentage::Percentage(p) => p,
                             };
                             i.expect_comma()?;
                             p
                         },
                         "from" => Percentage::zero(),
                         "to" => Percentage::hundred(),
-                        _ => return Err(StyleParseError::UnexpectedFunction(function.clone()).into()),
+                        _ => return Err(i.new_custom_error(StyleParseErrorKind::UnexpectedFunction(function.clone()))),
                     };
                     let color = Color::parse(context, i)?;
                     if color == Color::CurrentColor {
-                        return Err(StyleParseError::UnspecifiedError.into());
+                        return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError));
                     }
                     Ok((color.into(), p))
                 })?;
                 if reverse_stops {
                     p.reverse();
                 }
                 Ok(GenericGradientItem::ColorStop(GenericColorStop {
                     color: color,
@@ -723,32 +724,32 @@ impl LineDirection {
                 CompatMode::Modern => to_ident?,
                 // Fall back to Modern compatibility mode in case there is a `to` keyword.
                 // According to Gecko, `-moz-linear-gradient(to ...)` should serialize like
                 // `linear-gradient(to ...)`.
                 CompatMode::Moz if to_ident.is_ok() => *compat_mode = CompatMode::Modern,
                 // There is no `to` keyword in webkit prefixed syntax. If it's consumed,
                 // parsing should throw an error.
                 CompatMode::WebKit if to_ident.is_ok() => {
-                    return Err(SelectorParseError::UnexpectedIdent("to".into()).into())
+                    return Err(i.new_custom_error(SelectorParseErrorKind::UnexpectedIdent("to".into())))
                 },
                 _ => {},
             }
 
             #[cfg(feature = "gecko")]
             {
                 // `-moz-` prefixed linear gradient can be both Angle and Position.
                 if *compat_mode == CompatMode::Moz {
                     let position = i.try(|i| LegacyPosition::parse(context, i)).ok();
                     if _angle.is_none() {
                         _angle = i.try(|i| Angle::parse(context, i)).ok();
                     };
 
                     if _angle.is_none() && position.is_none() {
-                        return Err(StyleParseError::UnspecifiedError.into());
+                        return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError));
                     }
                     return Ok(LineDirection::MozPosition(position, _angle));
                 }
             }
 
             if let Ok(x) = i.try(X::parse) {
                 if let Ok(y) = i.try(Y::parse) {
                     return Ok(LineDirection::Corner(x, y));
@@ -862,17 +863,17 @@ impl EndingShape {
 }
 
 impl ShapeExtent {
     fn parse_with_compat_mode<'i, 't>(input: &mut Parser<'i, 't>,
                                       compat_mode: CompatMode)
                                       -> Result<Self, ParseError<'i>> {
         match Self::parse(input)? {
             ShapeExtent::Contain | ShapeExtent::Cover if compat_mode == CompatMode::Modern => {
-                Err(StyleParseError::UnspecifiedError.into())
+                Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
             },
             ShapeExtent::Contain => Ok(ShapeExtent::ClosestSide),
             ShapeExtent::Cover => Ok(ShapeExtent::FarthestCorner),
             keyword => Ok(keyword),
         }
     }
 }
 
@@ -886,17 +887,17 @@ impl GradientItem {
                     seen_stop = false;
                     return Ok(GenericGradientItem::InterpolationHint(hint));
                 }
             }
             seen_stop = true;
             ColorStop::parse(context, input).map(GenericGradientItem::ColorStop)
         })?;
         if !seen_stop || items.len() < 2 {
-            return Err(StyleParseError::UnspecifiedError.into());
+            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
         }
         Ok(items)
     }
 }
 
 impl Parse for ColorStop {
     fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
                      -> Result<Self, ParseError<'i>> {
--- a/servo/components/style/values/specified/length.rs
+++ b/servo/components/style/values/specified/length.rs
@@ -2,24 +2,24 @@
  * 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/. */
 
 //! [Length values][length].
 //!
 //! [length]: https://drafts.csswg.org/css-values/#lengths
 
 use app_units::Au;
-use cssparser::{Parser, Token, BasicParseError};
+use cssparser::{Parser, Token};
 use euclid::Size2D;
 use font_metrics::FontMetricsQueryResult;
 use parser::{Parse, ParserContext};
 use std::{cmp, fmt, mem};
 use std::ascii::AsciiExt;
 use std::ops::{Add, Mul};
-use style_traits::{ToCss, ParseError, StyleParseError};
+use style_traits::{ToCss, ParseError, StyleParseErrorKind};
 use style_traits::values::specified::AllowedNumericType;
 use stylesheets::CssRuleType;
 use super::{AllowQuirks, Number, ToComputedValue, Percentage};
 use values::{Auto, CSSFloat, Either, None_, Normal};
 use values::{ExtremumLength, serialize_dimension};
 use values::computed::{self, CSSPixelLength, Context};
 use values::generics::NonNegative;
 use values::specified::NonNegativeNumber;
@@ -657,32 +657,33 @@ impl Length {
     #[inline]
     fn parse_internal<'i, 't>(context: &ParserContext,
                               input: &mut Parser<'i, 't>,
                               num_context: AllowedNumericType,
                               allow_quirks: AllowQuirks)
                               -> Result<Length, ParseError<'i>> {
         // FIXME: remove early returns when lifetimes are non-lexical
         {
+            let location = input.current_source_location();
             let token = input.next()?;
             match *token {
                 Token::Dimension { value, ref unit, .. } if num_context.is_ok(context.parsing_mode, value) => {
                     return Length::parse_dimension(context, value, unit)
-                        .map_err(|()| BasicParseError::UnexpectedToken(token.clone()).into())
+                        .map_err(|()| location.new_unexpected_token_error(token.clone()))
                 }
                 Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
                     if value != 0. &&
                        !context.parsing_mode.allows_unitless_lengths() &&
                        !allow_quirks.allowed(context.quirks_mode) {
-                        return Err(StyleParseError::UnspecifiedError.into())
+                        return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
                     }
                     return Ok(Length::NoCalc(NoCalcLength::Absolute(AbsoluteLength::Px(value))))
                 },
                 Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {}
-                ref token => return Err(BasicParseError::UnexpectedToken(token.clone()).into())
+                ref token => return Err(location.new_unexpected_token_error(token.clone()))
             }
         }
         input.parse_nested_block(|input| {
             CalcNode::parse_length(context, input, num_context).map(|calc| Length::Calc(Box::new(calc)))
         })
     }
 
     /// Parse a non-negative length
@@ -853,37 +854,38 @@ impl LengthOrPercentage {
     fn parse_internal<'i, 't>(context: &ParserContext,
                               input: &mut Parser<'i, 't>,
                               num_context: AllowedNumericType,
                               allow_quirks: AllowQuirks)
                               -> Result<LengthOrPercentage, ParseError<'i>>
     {
         // FIXME: remove early returns when lifetimes are non-lexical
         {
+            let location = input.current_source_location();
             let token = input.next()?;
             match *token {
                 Token::Dimension { value, ref unit, .. } if num_context.is_ok(context.parsing_mode, value) => {
                     return NoCalcLength::parse_dimension(context, value, unit)
                         .map(LengthOrPercentage::Length)
-                        .map_err(|()| BasicParseError::UnexpectedToken(token.clone()).into())
+                        .map_err(|()| location.new_unexpected_token_error(token.clone()))
                 }
                 Token::Percentage { unit_value, .. } if num_context.is_ok(context.parsing_mode, unit_value) => {
                     return Ok(LengthOrPercentage::Percentage(computed::Percentage(unit_value)))
                 }
                 Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
                     if value != 0. &&
                        !context.parsing_mode.allows_unitless_lengths() &&
                        !allow_quirks.allowed(context.quirks_mode) {
-                        return Err(BasicParseError::UnexpectedToken(token.clone()).into())
+                        return Err(location.new_unexpected_token_error(token.clone()))
                     } else {
                         return Ok(LengthOrPercentage::Length(NoCalcLength::from_px(value)))
                     }
                 }
                 Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {}
-                _ => return Err(BasicParseError::UnexpectedToken(token.clone()).into())
+                _ => return Err(location.new_unexpected_token_error(token.clone()))
             }
         }
 
         let calc = input.parse_nested_block(|i| {
             CalcNode::parse_length_or_percentage(context, i, num_context)
         })?;
         Ok(LengthOrPercentage::Calc(Box::new(calc)))
     }
@@ -930,17 +932,17 @@ impl LengthOrPercentage {
         }
 
         // TODO(emilio): Probably should use Number::parse_non_negative to
         // handle calc()?
         let num = input.expect_number()?;
         if num >= 0. {
             Ok(LengthOrPercentage::Length(NoCalcLength::Absolute(AbsoluteLength::Px(num))))
         } else {
-            Err(StyleParseError::UnspecifiedError.into())
+            Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         }
     }
 
     /// Extract value from ref without a clone, replacing it with a 0 Au
     ///
     /// Use when you need to move out of a length array without cloning
     #[inline]
     pub fn take(&mut self) -> Self {
@@ -995,41 +997,42 @@ impl From<computed::Percentage> for Leng
 impl LengthOrPercentageOrAuto {
     fn parse_internal<'i, 't>(context: &ParserContext,
                               input: &mut Parser<'i, 't>,
                               num_context: AllowedNumericType,
                               allow_quirks: AllowQuirks)
                               -> Result<Self, ParseError<'i>> {
         // FIXME: remove early returns when lifetimes are non-lexical
         {
+            let location = input.current_source_location();
             let token = input.next()?;
             match *token {
                 Token::Dimension { value, ref unit, .. } if num_context.is_ok(context.parsing_mode, value) => {
                     return NoCalcLength::parse_dimension(context, value, unit)
                         .map(LengthOrPercentageOrAuto::Length)
-                        .map_err(|()| BasicParseError::UnexpectedToken(token.clone()).into())
+                        .map_err(|()| location.new_unexpected_token_error(token.clone()))
                 }
                 Token::Percentage { unit_value, .. } if num_context.is_ok(context.parsing_mode, unit_value) => {
                     return Ok(LengthOrPercentageOrAuto::Percentage(computed::Percentage(unit_value)))
                 }
                 Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
                     if value != 0. &&
                        !context.parsing_mode.allows_unitless_lengths() &&
                        !allow_quirks.allowed(context.quirks_mode) {
-                        return Err(StyleParseError::UnspecifiedError.into())
+                        return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
                     }
                     return Ok(LengthOrPercentageOrAuto::Length(
                         NoCalcLength::Absolute(AbsoluteLength::Px(value))
                     ))
                 }
                 Token::Ident(ref value) if value.eq_ignore_ascii_case("auto") => {
                     return Ok(LengthOrPercentageOrAuto::Auto)
                 }
                 Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {}
-                _ => return Err(BasicParseError::UnexpectedToken(token.clone()).into())
+                _ => return Err(location.new_unexpected_token_error(token.clone()))
             }
         }
 
         let calc = input.parse_nested_block(|i| {
             CalcNode::parse_length_or_percentage(context, i, num_context)
         })?;
         Ok(LengthOrPercentageOrAuto::Calc(Box::new(calc)))
     }
@@ -1100,40 +1103,41 @@ impl LengthOrPercentageOrNone {
     fn parse_internal<'i, 't>(context: &ParserContext,
                               input: &mut Parser<'i, 't>,
                               num_context: AllowedNumericType,
                               allow_quirks: AllowQuirks)
                               -> Result<LengthOrPercentageOrNone, ParseError<'i>>
     {
         // FIXME: remove early returns when lifetimes are non-lexical
         {
+            let location = input.current_source_location();
             let token = input.next()?;
             match *token {
                 Token::Dimension { value, ref unit, .. } if num_context.is_ok(context.parsing_mode, value) => {
                     return NoCalcLength::parse_dimension(context, value, unit)
                         .map(LengthOrPercentageOrNone::Length)
-                        .map_err(|()| BasicParseError::UnexpectedToken(token.clone()).into())
+                        .map_err(|()| location.new_unexpected_token_error(token.clone()))
                 }
                 Token::Percentage { unit_value, .. } if num_context.is_ok(context.parsing_mode, unit_value) => {
                     return Ok(LengthOrPercentageOrNone::Percentage(computed::Percentage(unit_value)))
                 }
                 Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
                     if value != 0. && !context.parsing_mode.allows_unitless_lengths() &&
                        !allow_quirks.allowed(context.quirks_mode) {
-                        return Err(StyleParseError::UnspecifiedError.into())
+                        return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
                     }
                     return Ok(LengthOrPercentageOrNone::Length(
                         NoCalcLength::Absolute(AbsoluteLength::Px(value))
                     ))
                 }
                 Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {}
                 Token::Ident(ref value) if value.eq_ignore_ascii_case("none") => {
                     return Ok(LengthOrPercentageOrNone::None)
                 }
-                _ => return Err(BasicParseError::UnexpectedToken(token.clone()).into())
+                _ => return Err(location.new_unexpected_token_error(token.clone()))
             }
         }
 
         let calc = input.parse_nested_block(|i| {
             CalcNode::parse_length_or_percentage(context, i, num_context)
         })?;
         Ok(LengthOrPercentageOrNone::Calc(Box::new(calc)))
     }
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -3,23 +3,23 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Specified values.
 //!
 //! TODO(emilio): Enhance docs.
 
 use Namespace;
 use context::QuirksMode;
-use cssparser::{Parser, Token, serialize_identifier, BasicParseError};
+use cssparser::{Parser, Token, serialize_identifier};
 use parser::{ParserContext, Parse};
 use self::url::SpecifiedUrl;
 use std::ascii::AsciiExt;
 use std::f32;
 use std::fmt;
-use style_traits::{ToCss, ParseError, StyleParseError};
+use style_traits::{ToCss, ParseError, StyleParseErrorKind};
 use style_traits::values::specified::AllowedNumericType;
 use super::{Auto, CSSFloat, CSSInteger, Either, None_};
 use super::computed::{Context, ToComputedValue};
 use super::generics::{GreaterThanOrEqualToOne, NonNegative};
 use super::generics::grid::{GridLine as GenericGridLine, TrackBreadth as GenericTrackBreadth};
 use super::generics::grid::{TrackSize as GenericTrackSize, TrackList as GenericTrackList};
 use values::specified::calc::CalcNode;
 
@@ -98,21 +98,22 @@ impl Parse for SpecifiedUrl {
 }
 
 impl Eq for SpecifiedUrl {}
 }
 
 /// Parse an `<integer>` value, handling `calc()` correctly.
 pub fn parse_integer<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
                              -> Result<Integer, ParseError<'i>> {
+    let location = input.current_source_location();
     // FIXME: remove early returns when lifetimes are non-lexical
     match *input.next()? {
         Token::Number { int_value: Some(v), .. } => return Ok(Integer::new(v)),
         Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {}
-        ref t => return Err(BasicParseError::UnexpectedToken(t.clone()).into())
+        ref t => return Err(location.new_unexpected_token_error(t.clone()))
     }
 
     let result = input.parse_nested_block(|i| {
         CalcNode::parse_integer(context, i)
     })?;
 
     Ok(Integer::from_calc(result))
 }
@@ -124,26 +125,27 @@ pub fn parse_number<'i, 't>(context: &Pa
     parse_number_with_clamping_mode(context, input, AllowedNumericType::All)
 }
 
 /// Parse a `<number>` value, with a given clamping mode.
 pub fn parse_number_with_clamping_mode<'i, 't>(context: &ParserContext,
                                                input: &mut Parser<'i, 't>,
                                                clamping_mode: AllowedNumericType)
                                                -> Result<Number, ParseError<'i>> {
+    let location = input.current_source_location();
     // FIXME: remove early returns when lifetimes are non-lexical
     match *input.next()? {
         Token::Number { value, .. } if clamping_mode.is_ok(context.parsing_mode, value) => {
             return Ok(Number {
                 value: value.min(f32::MAX).max(f32::MIN),
                 calc_clamping_mode: None,
             })
         }
         Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {}
-        ref t => return Err(BasicParseError::UnexpectedToken(t.clone()).into())
+        ref t => return Err(location.new_unexpected_token_error(t.clone()))
     }
 
     let result = input.parse_nested_block(|i| {
         CalcNode::parse_number(context, i)
     })?;
 
     Ok(Number {
         value: result.min(f32::MAX).max(f32::MIN),
@@ -410,17 +412,17 @@ impl Integer {
     ) -> Result<Integer, ParseError<'i>> {
         match parse_integer(context, input) {
             // FIXME(emilio): The spec asks us to avoid rejecting it at parse
             // time except until computed value time.
             //
             // It's not totally clear it's worth it though, and no other browser
             // does this.
             Ok(value) if value.value() >= min => Ok(value),
-            Ok(_value) => Err(StyleParseError::UnspecifiedError.into()),
+            Ok(_value) => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
             Err(e) => Err(e),
         }
     }
 
     /// Parse a non-negative integer.
     pub fn parse_non_negative<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
@@ -468,17 +470,19 @@ impl ToCss for Integer {
 pub type IntegerOrAuto = Either<Integer, Auto>;
 
 impl IntegerOrAuto {
     #[allow(missing_docs)]
     pub fn parse_positive<'i, 't>(context: &ParserContext,
                                   input: &mut Parser<'i, 't>)
                                   -> Result<IntegerOrAuto, ParseError<'i>> {
         match IntegerOrAuto::parse(context, input) {
-            Ok(Either::First(integer)) if integer.value() <= 0 => Err(StyleParseError::UnspecifiedError.into()),
+            Ok(Either::First(integer)) if integer.value() <= 0 => {
+                Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
+            }
             result => result,
         }
     }
 }
 
 /// A wrapper of Integer, with value >= 1.
 pub type PositiveInteger = GreaterThanOrEqualToOne<Integer>;
 
@@ -736,50 +740,51 @@ impl Attr {
     pub fn parse_function<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
                                   -> Result<Attr, ParseError<'i>> {
         // Syntax is `[namespace? `|`]? ident`
         // no spaces allowed
         let first = input.try(|i| i.expect_ident_cloned()).ok();
         if let Ok(token) = input.try(|i| i.next_including_whitespace().map(|t| t.clone())) {
             match token {
                 Token::Delim('|') => {
+                    let location = input.current_source_location();
                     // must be followed by an ident
                     let second_token = match *input.next_including_whitespace()? {
                         Token::Ident(ref second) => second,
-                        ref t => return Err(BasicParseError::UnexpectedToken(t.clone()).into()),
+                        ref t => return Err(location.new_unexpected_token_error(t.clone())),
                     };
 
                     let ns_with_id = if let Some(ns) = first {
                         let ns = Namespace::from(ns.as_ref());
                         let id: Result<_, ParseError> =
                             get_id_for_namespace(&ns, context)
-                            .map_err(|()| StyleParseError::UnspecifiedError.into());
+                            .map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
                         Some((ns, id?))
                     } else {
                         None
                     };
                     return Ok(Attr {
                         namespace: ns_with_id,
                         attribute: second_token.as_ref().to_owned(),
                     })
                 }
                 // In the case of attr(foobar    ) we don't want to error out
                 // because of the trailing whitespace
                 Token::WhiteSpace(_) => (),
-                ref t => return Err(BasicParseError::UnexpectedToken(t.clone()).into()),
+                ref t => return Err(input.new_unexpected_token_error(t.clone())),
             }
         }
 
         if let Some(first) = first {
             Ok(Attr {
                 namespace: None,
                 attribute: first.as_ref().to_owned(),
             })
         } else {
-            Err(StyleParseError::UnspecifiedError.into())
+            Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         }
     }
 }
 
 impl ToCss for Attr {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         dest.write_str("attr(")?;
         if let Some(ref ns) = self.namespace {
--- a/servo/components/style/values/specified/percentage.rs
+++ b/servo/components/style/values/specified/percentage.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/. */
 
 //! Specified percentages.
 
-use cssparser::{BasicParseError, Parser, Token};
+use cssparser::{Parser, Token};
 use parser::{Parse, ParserContext};
 use std::ascii::AsciiExt;
 use std::fmt;
 use style_traits::{ParseError, ToCss};
 use style_traits::values::specified::AllowedNumericType;
 use values::{CSSFloat, serialize_percentage};
 use values::computed::{Context, ToComputedValue};
 use values::computed::percentage::Percentage as ComputedPercentage;
@@ -92,23 +92,24 @@ impl Percentage {
     }
 
     /// Parses a specific kind of percentage.
     pub fn parse_with_clamping_mode<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
         num_context: AllowedNumericType,
     ) -> Result<Self, ParseError<'i>> {
+        let location = input.current_source_location();
         // FIXME: remove early returns when lifetimes are non-lexical
         match *input.next()? {
             Token::Percentage { unit_value, .. } if num_context.is_ok(context.parsing_mode, unit_value) => {
                 return Ok(Percentage::new(unit_value));
             }
             Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {},
-            ref t => return Err(BasicParseError::UnexpectedToken(t.clone()).into()),
+            ref t => return Err(location.new_unexpected_token_error(t.clone())),
         }
 
         let result = input.parse_nested_block(|i| {
             CalcNode::parse_percentage(context, i)
         })?;
 
         // TODO(emilio): -moz-image-rect is the only thing that uses
         // the clamping mode... I guess we could disallow it...
--- a/servo/components/style/values/specified/svg.rs
+++ b/servo/components/style/values/specified/svg.rs
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Specified types for SVG properties.
 
 use cssparser::Parser;
 use parser::{Parse, ParserContext};
-use style_traits::{CommaWithSpace, ParseError, Separator, StyleParseError};
+use style_traits::{CommaWithSpace, ParseError, Separator, StyleParseErrorKind};
 use values::generics::svg as generic;
 use values::specified::{LengthOrPercentage, NonNegativeLengthOrPercentage, NonNegativeNumber};
 use values::specified::{Number, Opacity, SpecifiedUrl};
 use values::specified::color::RGBAColor;
 
 /// Specified SVG Paint value
 pub type SVGPaint = generic::SVGPaint<RGBAColor, SpecifiedUrl>;
 
@@ -34,17 +34,17 @@ fn is_context_value_enabled() -> bool {
 
 fn parse_context_value<'i, 't, T>(input: &mut Parser<'i, 't>, value: T)
                                   -> Result<T, ParseError<'i>> {
     if is_context_value_enabled() {
         if input.expect_ident_matching("context-value").is_ok() {
             return Ok(value);
         }
     }
-    Err(StyleParseError::UnspecifiedError.into())
+    Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
 }
 
 /// A value of <length> | <percentage> | <number> for stroke-dashoffset.
 /// https://www.w3.org/TR/SVG11/painting.html#StrokeProperties
 pub type SvgLengthOrPercentageOrNumber =
     generic::SvgLengthOrPercentageOrNumber<LengthOrPercentage, Number>;
 
 /// <length> | <percentage> | <number> | context-value
@@ -110,17 +110,17 @@ impl Parse for SVGStrokeDashArray {
 pub type SVGOpacity = generic::SVGOpacity<Opacity>;
 
 impl Parse for SVGOpacity {
     fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
                      -> Result<Self, ParseError<'i>> {
         if let Ok(opacity) = input.try(|i| Opacity::parse(context, i)) {
             Ok(generic::SVGOpacity::Opacity(opacity))
         } else if is_context_value_enabled() {
-            try_match_ident_ignore_ascii_case! { input.expect_ident()?,
+            try_match_ident_ignore_ascii_case! { input,
                 "context-fill-opacity" => Ok(generic::SVGOpacity::ContextFillOpacity),
                 "context-stroke-opacity" => Ok(generic::SVGOpacity::ContextStrokeOpacity),
             }
         } else {
-            Err(StyleParseError::UnspecifiedError.into())
+            Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         }
     }
 }
--- a/servo/components/style/values/specified/text.rs
+++ b/servo/components/style/values/specified/text.rs
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Specified types for text properties.
 
 use cssparser::Parser;
 use parser::{Parse, ParserContext};
-use selectors::parser::SelectorParseError;
+use selectors::parser::SelectorParseErrorKind;
 use std::ascii::AsciiExt;
 use style_traits::ParseError;
 use values::computed::{Context, ToComputedValue};
 use values::computed::text::LineHeight as ComputedLineHeight;
 use values::generics::text::InitialLetter as GenericInitialLetter;
 use values::generics::text::LineHeight as GenericLineHeight;
 use values::generics::text::Spacing;
 use values::specified::{AllowQuirks, Integer, NonNegativeNumber, Number};
@@ -60,26 +60,27 @@ impl Parse for WordSpacing {
 impl Parse for LineHeight {
     fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
         if let Ok(number) = input.try(|i| NonNegativeNumber::parse(context, i)) {
             return Ok(GenericLineHeight::Number(number))
         }
         if let Ok(nlop) = input.try(|i| NonNegativeLengthOrPercentage::parse(context, i)) {
             return Ok(GenericLineHeight::Length(nlop))
         }
+        let location = input.current_source_location();
         let ident = input.expect_ident()?;
         match ident {
             ref ident if ident.eq_ignore_ascii_case("normal") => {
                 Ok(GenericLineHeight::Normal)
             },
             #[cfg(feature = "gecko")]
             ref ident if ident.eq_ignore_ascii_case("-moz-block-height") => {
                 Ok(GenericLineHeight::MozBlockHeight)
             },
-            ident => Err(SelectorParseError::UnexpectedIdent(ident.clone()).into()),
+            ident => Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone()))),
         }
     }
 }
 
 impl ToComputedValue for LineHeight {
     type ComputedValue = ComputedLineHeight;
 
     #[inline]
--- a/servo/components/style/values/specified/time.rs
+++ b/servo/components/style/values/specified/time.rs
@@ -1,19 +1,19 @@
 /* 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/. */
 
 //! Specified time values.
 
-use cssparser::{Parser, Token, BasicParseError};
+use cssparser::{Parser, Token};
 use parser::{ParserContext, Parse};
 use std::ascii::AsciiExt;
 use std::fmt;
-use style_traits::{ToCss, ParseError, StyleParseError};
+use style_traits::{ToCss, ParseError, StyleParseErrorKind};
 use style_traits::values::specified::AllowedNumericType;
 use values::CSSFloat;
 use values::computed::{Context, ToComputedValue};
 use values::computed::time::Time as ComputedTime;
 use values::specified::calc::CalcNode;
 
 /// A time value according to CSS-VALUES § 6.2.
 #[derive(Clone, Copy, Debug, PartialEq)]
@@ -78,34 +78,35 @@ impl Time {
 
     fn parse_with_clamping_mode<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
         clamping_mode: AllowedNumericType
     ) -> Result<Self, ParseError<'i>> {
         use style_traits::PARSING_MODE_DEFAULT;
 
+        let location = input.current_source_location();
         // FIXME: remove early returns when lifetimes are non-lexical
         match input.next() {
             // Note that we generally pass ParserContext to is_ok() to check
             // that the ParserMode of the ParserContext allows all numeric
             // values for SMIL regardless of clamping_mode, but in this Time
             // value case, the value does not animate for SMIL at all, so we use
             // PARSING_MODE_DEFAULT directly.
             Ok(&Token::Dimension { value, ref unit, .. }) if clamping_mode.is_ok(PARSING_MODE_DEFAULT, value) => {
                 return Time::parse_dimension(value, unit, /* from_calc = */ false)
-                    .map_err(|()| StyleParseError::UnspecifiedError.into())
+                    .map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
             }
             Ok(&Token::Function(ref name)) if name.eq_ignore_ascii_case("calc") => {}
-            Ok(t) => return Err(BasicParseError::UnexpectedToken(t.clone()).into()),
+            Ok(t) => return Err(location.new_unexpected_token_error(t.clone())),
             Err(e) => return Err(e.into())
         }
         match input.parse_nested_block(|i| CalcNode::parse_time(context, i)) {
             Ok(time) if clamping_mode.is_ok(PARSING_MODE_DEFAULT, time.seconds) => Ok(time),
-            _ => Err(StyleParseError::UnspecifiedError.into()),
+            _ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
         }
     }
 
     /// Parses a non-negative time value.
     pub fn parse_non_negative<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>
     ) -> Result<Self, ParseError<'i>> {
--- a/servo/components/style/values/specified/transform.rs
+++ b/servo/components/style/values/specified/transform.rs
@@ -1,18 +1,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Specified types for CSS values that are related to transformations.
 
 use cssparser::Parser;
 use parser::{Parse, ParserContext};
-use selectors::parser::SelectorParseError;
-use style_traits::{ParseError, StyleParseError};
+use selectors::parser::SelectorParseErrorKind;
+use style_traits::{ParseError, StyleParseErrorKind};
 use values::computed::{Context, LengthOrPercentage as ComputedLengthOrPercentage};
 use values::computed::{Percentage as ComputedPercentage, ToComputedValue};
 use values::computed::transform::TimingFunction as ComputedTimingFunction;
 use values::generics::transform::{StepPosition, TimingFunction as GenericTimingFunction};
 use values::generics::transform::{TimingKeyword, TransformOrigin as GenericTransformOrigin};
 use values::specified::{Integer, Number};
 use values::specified::length::{Length, LengthOrPercentage};
 use values::specified::position::{Side, X, Y};
@@ -147,34 +147,35 @@ impl Parse for TimingFunction {
     fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
         if let Ok(keyword) = input.try(TimingKeyword::parse) {
             return Ok(GenericTimingFunction::Keyword(keyword));
         }
         if let Ok(ident) = input.try(|i| i.expect_ident_cloned()) {
             let position = match_ignore_ascii_case! { &ident,
                 "step-start" => StepPosition::Start,
                 "step-end" => StepPosition::End,
-                _ => return Err(SelectorParseError::UnexpectedIdent(ident.clone()).into()),
+                _ => return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone()))),
             };
             return Ok(GenericTimingFunction::Steps(Integer::new(1), position));
         }
+        let location = input.current_source_location();
         let function = input.expect_function()?.clone();
         input.parse_nested_block(move |i| {
             (match_ignore_ascii_case! { &function,
                 "cubic-bezier" => {
                     let x1 = Number::parse(context, i)?;
                     i.expect_comma()?;
                     let y1 = Number::parse(context, i)?;
                     i.expect_comma()?;
                     let x2 = Number::parse(context, i)?;
                     i.expect_comma()?;
                     let y2 = Number::parse(context, i)?;
 
                     if x1.get() < 0.0 || x1.get() > 1.0 || x2.get() < 0.0 || x2.get() > 1.0 {
-                        return Err(StyleParseError::UnspecifiedError.into());
+                        return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError));
                     }
 
                     Ok(GenericTimingFunction::CubicBezier { x1, y1, x2, y2 })
                 },
                 "steps" => {
                     let steps = Integer::parse_positive(context, i)?;
                     let position = i.try(|i| {
                         i.expect_comma()?;
@@ -186,17 +187,17 @@ impl Parse for TimingFunction {
                     if allow_frames_timing() {
                         let frames = Integer::parse_with_minimum(context, i, 2)?;
                         Ok(GenericTimingFunction::Frames(frames))
                     } else {
                         Err(())
                     }
                 },
                 _ => Err(()),
-            }).map_err(|()| StyleParseError::UnexpectedFunction(function.clone()).into())
+            }).map_err(|()| location.new_custom_error(StyleParseErrorKind::UnexpectedFunction(function.clone())))
         })
     }
 }
 
 impl ToComputedValue for TimingFunction {
     type ComputedValue = ComputedTimingFunction;
 
     #[inline]
--- a/servo/components/style_traits/Cargo.toml
+++ b/servo/components/style_traits/Cargo.toml
@@ -11,17 +11,17 @@ path = "lib.rs"
 
 [features]
 servo = ["heapsize", "heapsize_derive", "serde", "servo_atoms", "cssparser/heapsize", "cssparser/serde", "webrender_api"]
 gecko = []
 
 [dependencies]
 app_units = "0.5"
 bitflags = "0.7"
-cssparser = "0.21.1"
+cssparser = "0.22.0"
 euclid = "0.15"
 heapsize = {version = "0.4", optional = true}
 heapsize_derive = {version = "0.1", optional = true}
 malloc_size_of = { path = "../malloc_size_of" }
 malloc_size_of_derive = { path = "../malloc_size_of_derive" }
 selectors = { path = "../selectors" }
 serde = {version = "1.0", optional = true}
 webrender_api = {git = "https://github.com/servo/webrender", optional = true}
--- a/servo/components/style_traits/lib.rs
+++ b/servo/components/style_traits/lib.rs
@@ -25,17 +25,17 @@ extern crate selectors;
 #[cfg(feature = "servo")] #[macro_use] extern crate serde;
 #[cfg(feature = "servo")] extern crate webrender_api;
 extern crate servo_arc;
 #[cfg(feature = "servo")] extern crate servo_atoms;
 
 #[cfg(feature = "servo")] pub use webrender_api::DevicePixel;
 
 use cssparser::{CowRcStr, Token};
-use selectors::parser::SelectorParseError;
+use selectors::parser::SelectorParseErrorKind;
 #[cfg(feature = "servo")] use servo_atoms::Atom;
 
 /// One hardware pixel.
 ///
 /// This unit corresponds to the smallest addressable element of the display hardware.
 #[cfg(not(feature = "servo"))]
 #[derive(Clone, Copy, Debug)]
 pub enum DevicePixel {}
@@ -80,33 +80,34 @@ pub mod cursor;
 #[macro_use]
 pub mod values;
 #[macro_use]
 pub mod viewport;
 
 pub use values::{Comma, CommaWithSpace, OneOrMoreSeparated, Separator, Space, ToCss};
 
 /// The error type for all CSS parsing routines.
-pub type ParseError<'i> = cssparser::ParseError<'i, SelectorParseError<'i, StyleParseError<'i>>>;
+pub type ParseError<'i> = cssparser::ParseError<'i, StyleParseErrorKind<'i>>;
+
+/// Error in property value parsing
+pub type ValueParseError<'i> = cssparser::ParseError<'i, ValueParseErrorKind<'i>>;
 
 #[derive(Clone, Debug, PartialEq)]
 /// Errors that can be encountered while parsing CSS values.
-pub enum StyleParseError<'i> {
+pub enum StyleParseErrorKind<'i> {
     /// A bad URL token in a DVB.
     BadUrlInDeclarationValueBlock(CowRcStr<'i>),
     /// A bad string token in a DVB.
     BadStringInDeclarationValueBlock(CowRcStr<'i>),
     /// Unexpected closing parenthesis in a DVB.
     UnbalancedCloseParenthesisInDeclarationValueBlock,
     /// Unexpected closing bracket in a DVB.
     UnbalancedCloseSquareBracketInDeclarationValueBlock,
     /// Unexpected closing curly bracket in a DVB.
     UnbalancedCloseCurlyBracketInDeclarationValueBlock,
-    /// A property declaration parsing error.
-    PropertyDeclaration(PropertyDeclarationParseError<'i>),
     /// A property declaration value had input remaining after successfully parsing.
     PropertyDeclarationValueNotExhausted,
     /// An unexpected dimension token was encountered.
     UnexpectedDimension(CowRcStr<'i>),
     /// Expected identifier not found.
     ExpectedIdentifier(Token<'i>),
     /// Missing or invalid media feature name.
     MediaQueryExpectedFeatureName(CowRcStr<'i>),
@@ -124,75 +125,82 @@ pub enum StyleParseError<'i> {
     UnexpectedCharsetRule,
     /// Unsupported @ rule
     UnsupportedAtRule(CowRcStr<'i>),
     /// A placeholder for many sources of errors that require more specific variants.
     UnspecifiedError,
     /// An unexpected token was found within a namespace rule.
     UnexpectedTokenWithinNamespace(Token<'i>),
     /// An error was encountered while parsing a property value.
-    ValueError(ValueParseError<'i>),
-}
-
-/// Specific errors that can be encountered while parsing property values.
-#[derive(Clone, Debug, PartialEq)]
-pub enum ValueParseError<'i> {
-    /// An invalid token was encountered while parsing a color value.
-    InvalidColor(Token<'i>),
-    /// An invalid filter value was encountered.
-    InvalidFilter(Token<'i>),
-}
+    ValueError(ValueParseErrorKind<'i>),
+    /// An error was encountered while parsing a selector
+    SelectorError(SelectorParseErrorKind<'i>),
 
-impl<'a> From<ValueParseError<'a>> for ParseError<'a> {
-    fn from(this: ValueParseError<'a>) -> Self {
-        StyleParseError::ValueError(this).into()
-    }
-}
-
-impl<'i> ValueParseError<'i> {
-    /// Attempt to extract a ValueParseError value from a ParseError.
-    pub fn from_parse_error(this: ParseError<'i>) -> Option<ValueParseError<'i>> {
-        match this {
-            cssparser::ParseError::Custom(
-                SelectorParseError::Custom(
-                    StyleParseError::ValueError(e))) => Some(e),
-            _ => None,
-        }
-    }
-}
-
-/// The result of parsing a property declaration.
-#[derive(Clone, Debug, PartialEq)]
-pub enum PropertyDeclarationParseError<'i> {
     /// The property declaration was for an unknown property.
     UnknownProperty(CowRcStr<'i>),
     /// An unknown vendor-specific identifier was encountered.
     UnknownVendorProperty,
     /// The property declaration was for a disabled experimental property.
     ExperimentalProperty,
+    /// The property declaration contained an invalid color value.
+    InvalidColor(CowRcStr<'i>, Token<'i>),
+    /// The property declaration contained an invalid filter value.
+    InvalidFilter(CowRcStr<'i>, Token<'i>),
     /// The property declaration contained an invalid value.
-    InvalidValue(CowRcStr<'i>, Option<ValueParseError<'i>>),
+    OtherInvalidValue(CowRcStr<'i>),
     /// The declaration contained an animation property, and we were parsing
     /// this as a keyframe block (so that property should be ignored).
     ///
     /// See: https://drafts.csswg.org/css-animations/#keyframes
     AnimationPropertyInKeyframeBlock,
     /// The property is not allowed within a page rule.
     NotAllowedInPageRule,
 }
 
-impl<'a> From<StyleParseError<'a>> for ParseError<'a> {
-    fn from(this: StyleParseError<'a>) -> Self {
-        cssparser::ParseError::Custom(SelectorParseError::Custom(this))
+impl<'i> From<ValueParseErrorKind<'i>> for StyleParseErrorKind<'i> {
+    fn from(this: ValueParseErrorKind<'i>) -> Self {
+        StyleParseErrorKind::ValueError(this)
+    }
+}
+
+impl<'i> From<SelectorParseErrorKind<'i>> for StyleParseErrorKind<'i> {
+    fn from(this: SelectorParseErrorKind<'i>) -> Self {
+        StyleParseErrorKind::SelectorError(this)
     }
 }
 
-impl<'a> From<PropertyDeclarationParseError<'a>> for ParseError<'a> {
-    fn from(this: PropertyDeclarationParseError<'a>) -> Self {
-        cssparser::ParseError::Custom(SelectorParseError::Custom(StyleParseError::PropertyDeclaration(this)))
+/// Specific errors that can be encountered while parsing property values.
+#[derive(Clone, Debug, PartialEq)]
+pub enum ValueParseErrorKind<'i> {
+    /// An invalid token was encountered while parsing a color value.
+    InvalidColor(Token<'i>),
+    /// An invalid filter value was encountered.
+    InvalidFilter(Token<'i>),
+}
+
+impl<'i> StyleParseErrorKind<'i> {
+    /// Create an InvalidValue parse error
+    pub fn new_invalid(name: CowRcStr<'i>, value_error: ParseError<'i>) -> ParseError<'i> {
+        let variant = match value_error.kind {
+            cssparser::ParseErrorKind::Custom(StyleParseErrorKind::ValueError(e)) => {
+                match e {
+                    ValueParseErrorKind::InvalidColor(token) => {
+                        StyleParseErrorKind::InvalidColor(name, token)
+                    }
+                    ValueParseErrorKind::InvalidFilter(token) => {
+                        StyleParseErrorKind::InvalidFilter(name, token)
+                    }
+                }
+            }
+            _ => StyleParseErrorKind::OtherInvalidValue(name),
+        };
+        cssparser::ParseError {
+            kind: cssparser::ParseErrorKind::Custom(variant),
+            location: value_error.location,
+        }
     }
 }
 
 bitflags! {
     /// The mode to use when parsing values.
     pub flags ParsingMode: u8 {
         /// In CSS, lengths must have units, except for zero values, where the unit can be omitted.
         /// https://www.w3.org/TR/css3-values/#lengths
--- a/servo/components/style_traits/values.rs
+++ b/servo/components/style_traits/values.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/. */
 
 //! Helper types and traits for the handling of CSS values.
 
 use app_units::Au;
-use cssparser::{BasicParseError, ParseError, Parser, Token, UnicodeRange, serialize_string};
+use cssparser::{ParseError, Parser, Token, UnicodeRange, serialize_string};
 use cssparser::ToCss as CssparserToCss;
 use servo_arc::Arc;
 use std::fmt::{self, Write};
 
 /// Serialises a value according to its CSS representation.
 ///
 /// This trait is implemented for `str` and its friends, serialising the string
 /// contents as a CSS quoted string.
@@ -294,22 +294,23 @@ impl Separator for CommaWithSpace {
     ) -> Result<Vec<T>, ParseError<'i, E>>
     where
         F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>
     {
         input.skip_whitespace();  // Unnecessary for correctness, but may help try() rewind less.
         let mut results = vec![parse_one(input)?];
         loop {
             input.skip_whitespace();  // Unnecessary for correctness, but may help try() rewind less.
+            let comma_location = input.current_source_location();
             let comma = input.try(|i| i.expect_comma()).is_ok();
             input.skip_whitespace();  // Unnecessary for correctness, but may help try() rewind less.
             if let Ok(item) = input.try(&mut parse_one) {
                 results.push(item);
             } else if comma {
-                return Err(BasicParseError::UnexpectedToken(Token::Comma).into());
+                return Err(comma_location.new_unexpected_token_error(Token::Comma));
             } else {
                 break;
             }
         }
         Ok(results)
     }
 }
 
@@ -444,21 +445,26 @@ macro_rules! __define_css_keyword_enum__
         pub enum $name {
             $( $variant ),+
         }
 
         impl $name {
             /// Parse this property from a CSS input stream.
             pub fn parse<'i, 't>(input: &mut ::cssparser::Parser<'i, 't>)
                                  -> Result<$name, $crate::ParseError<'i>> {
-                let ident = input.expect_ident()?;
-                Self::from_ident(&ident)
-                    .map_err(|()| ::cssparser::ParseError::Basic(
-                        ::cssparser::BasicParseError::UnexpectedToken(
-                            ::cssparser::Token::Ident(ident.clone()))))
+                use cssparser::Token;
+                let location = input.current_source_location();
+                match *input.next()? {
+                    Token::Ident(ref ident) => {
+                        Self::from_ident(ident).map_err(|()| {
+                            location.new_unexpected_token_error(Token::Ident(ident.clone()))
+                        })
+                    }
+                    ref token => Err(location.new_unexpected_token_error(token.clone()))
+                }
             }
 
             /// Parse this property from an already-tokenized identifier.
             pub fn from_ident(ident: &str) -> Result<$name, ()> {
                 match_ignore_ascii_case! { ident,
                                            $( $css => Ok($name::$variant), )+
                                            $( $alias => Ok($name::$alias_variant), )*
                                            _ => Err(())
--- a/servo/components/style_traits/viewport.rs
+++ b/servo/components/style_traits/viewport.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/. */
 
 //! Helper types for the `@viewport` rule.
 
 use {CSSPixel, PinchZoomFactor, ParseError};
-use cssparser::{Parser, ToCss, ParseError as CssParseError, BasicParseError};
+use cssparser::{Parser, ToCss};
 use euclid::TypedSize2D;
 use std::ascii::AsciiExt;
 use std::fmt;
 
 define_css_keyword_enum!(UserZoom:
                          "zoom" => Zoom,
                          "fixed" => Fixed);
 
@@ -104,31 +104,32 @@ impl Zoom {
     /// Parse a zoom value per:
     ///
     /// https://drafts.csswg.org/css-device-adapt/#descdef-viewport-zoom
     pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Zoom, ParseError<'i>> {
         use PARSING_MODE_DEFAULT;
         use cssparser::Token;
         use values::specified::AllowedNumericType::NonNegative;
 
+        let location = input.current_source_location();
         match *input.next()? {
             // TODO: This parse() method should take ParserContext as an
             // argument, and pass ParsingMode owned by the ParserContext to
             // is_ok() instead of using PARSING_MODE_DEFAULT directly.
             // In order to do so, we might want to move these stuff into style::stylesheets::viewport_rule.
             Token::Percentage { unit_value, .. } if NonNegative.is_ok(PARSING_MODE_DEFAULT, unit_value) => {
                 Ok(Zoom::Percentage(unit_value))
             }
             Token::Number { value, .. } if NonNegative.is_ok(PARSING_MODE_DEFAULT, value) => {
                 Ok(Zoom::Number(value))
             }
             Token::Ident(ref value) if value.eq_ignore_ascii_case("auto") => {
                 Ok(Zoom::Auto)
             }
-            ref t => Err(CssParseError::Basic(BasicParseError::UnexpectedToken(t.clone())))
+            ref t => Err(location.new_unexpected_token_error(t.clone()))
         }
     }
 
     /// Get this zoom value as a float value. Returns `None` if the value is the
     /// `auto` keyword.
     #[inline]
     pub fn to_f32(&self) -> Option<f32> {
         match *self {
--- a/servo/ports/geckolib/Cargo.toml
+++ b/servo/ports/geckolib/Cargo.toml
@@ -10,17 +10,17 @@ path = "lib.rs"
 crate-type = ["staticlib", "rlib"]
 
 [features]
 bindgen = ["style/use_bindgen"]
 gecko_debug = ["style/gecko_debug"]
 
 [dependencies]
 atomic_refcell = "0.1"
-cssparser = "0.21.1"
+cssparser = "0.22.0"
 env_logger = {version = "0.4", default-features = false} # disable `regex` to reduce code size
 libc = "0.2"
 log = {version = "0.3.5", features = ["release_max_level_info"]}
 malloc_size_of = {path = "../../components/malloc_size_of"}
 nsstring_vendor = {path = "../../components/style/gecko_bindings/nsstring_vendor"}
 parking_lot = "0.4"
 # Turn on gecko_like_types because of so that crates which use this
 # crate and also dev-depend on stylo_tests get reasonable behavior
--- a/servo/ports/geckolib/error_reporter.rs
+++ b/servo/ports/geckolib/error_reporter.rs
@@ -2,28 +2,30 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Wrapper around Gecko's CSS error reporting mechanism.
 
 #![allow(unsafe_code)]
 
 use cssparser::{CowRcStr, serialize_identifier, ToCss};
-use cssparser::{SourceLocation, ParseError as CssParseError, Token, BasicParseError};
-use selectors::parser::SelectorParseError;
+use cssparser::{SourceLocation, ParseError, ParseErrorKind, Token, BasicParseErrorKind};
+use selectors::parser::SelectorParseErrorKind;
 use std::ptr;
 use style::error_reporting::{ParseErrorReporter, ContextualParseError};
 use style::gecko_bindings::bindings::{Gecko_CreateCSSErrorReporter, Gecko_DestroyCSSErrorReporter};
 use style::gecko_bindings::bindings::Gecko_ReportUnexpectedCSSError;
 use style::gecko_bindings::structs::{Loader, ServoStyleSheet, nsIURI};
 use style::gecko_bindings::structs::ErrorReporter as GeckoErrorReporter;
 use style::gecko_bindings::structs::URLExtraData as RawUrlExtraData;
 use style::gecko_bindings::sugar::refptr::RefPtr;
 use style::stylesheets::UrlExtraData;
-use style_traits::{ParseError, StyleParseError, PropertyDeclarationParseError, ValueParseError};
+use style_traits::StyleParseErrorKind;
+
+pub type ErrorKind<'i> = ParseErrorKind<'i, StyleParseErrorKind<'i>>;
 
 /// Wrapper around an instance of Gecko's CSS error reporter.
 pub struct ErrorReporter(*mut GeckoErrorReporter);
 
 impl ErrorReporter {
     /// Create a new instance of the Gecko error reporter.
     pub fn new(sheet: *mut ServoStyleSheet,
                loader: *mut Loader,
@@ -65,252 +67,283 @@ impl<'a> ErrorString<'a> {
 
 enum Action {
     Nothing,
     Skip,
     Drop,
 }
 
 trait ErrorHelpers<'a> {
-    fn error_data(self) -> (CowRcStr<'a>, ParseError<'a>);
+    fn error_data(self) -> (CowRcStr<'a>, ErrorKind<'a>);
     fn error_params(self) -> ErrorParams<'a>;
     fn to_gecko_message(&self) -> (Option<&'static [u8]>, &'static [u8], Action);
 }
 
-fn extract_error_param<'a>(err: ParseError<'a>) -> Option<ErrorString<'a>> {
+fn extract_error_param<'a>(err: ErrorKind<'a>) -> Option<ErrorString<'a>> {
     Some(match err {
-        CssParseError::Basic(BasicParseError::UnexpectedToken(t)) => {
+        ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(t)) => {
             ErrorString::UnexpectedToken(t)
         }
 
-        CssParseError::Basic(BasicParseError::AtRuleInvalid(i)) |
-        CssParseError::Custom(SelectorParseError::Custom(
-            StyleParseError::UnsupportedAtRule(i)
-        )) => {
+        ParseErrorKind::Basic(BasicParseErrorKind::AtRuleInvalid(i)) |
+        ParseErrorKind::Custom(StyleParseErrorKind::UnsupportedAtRule(i)) => {
             let mut s = String::from("@");
             serialize_identifier(&i, &mut s).unwrap();
             ErrorString::Snippet(s.into())
         }
 
-        CssParseError::Custom(SelectorParseError::Custom(
-            StyleParseError::PropertyDeclaration(
-                PropertyDeclarationParseError::InvalidValue(property, None)
-            )
-        )) => {
+        ParseErrorKind::Custom(StyleParseErrorKind::OtherInvalidValue(property)) => {
             ErrorString::Snippet(property)
         }
 
-        CssParseError::Custom(SelectorParseError::UnexpectedIdent(ident)) => {
+        ParseErrorKind::Custom(
+            StyleParseErrorKind::SelectorError(
+                SelectorParseErrorKind::UnexpectedIdent(ident)
+            )
+        ) => {
             ErrorString::Ident(ident)
         }
 
-        CssParseError::Custom(SelectorParseError::Custom(
-            StyleParseError::PropertyDeclaration(
-                PropertyDeclarationParseError::UnknownProperty(property)
-            )
-        )) => {
+        ParseErrorKind::Custom(StyleParseErrorKind::UnknownProperty(property)) => {
             ErrorString::Ident(property)
         }
 
-        CssParseError::Custom(SelectorParseError::Custom(
-            StyleParseError::UnexpectedTokenWithinNamespace(token)
-        )) => {
+        ParseErrorKind::Custom(
+            StyleParseErrorKind::UnexpectedTokenWithinNamespace(token)
+        ) => {
             ErrorString::UnexpectedToken(token)
         }
 
         _ => return None,
     })
 }
 
-fn extract_value_error_param<'a>(err: ValueParseError<'a>) -> ErrorString<'a> {
-    match err {
-        ValueParseError::InvalidColor(t) |
-        ValueParseError::InvalidFilter(t) => ErrorString::UnexpectedToken(t),
-    }
-}
-
 struct ErrorParams<'a> {
     prefix_param: Option<ErrorString<'a>>,
     main_param: Option<ErrorString<'a>>,
 }
 
 /// If an error parameter is present in the given error, return it. Additionally return
 /// a second parameter if it exists, for use in the prefix for the eventual error message.
-fn extract_error_params<'a>(err: ParseError<'a>) -> Option<ErrorParams<'a>> {
+fn extract_error_params<'a>(err: ErrorKind<'a>) -> Option<ErrorParams<'a>> {
     let (main, prefix) = match err {
-        CssParseError::Custom(SelectorParseError::Custom(
-            StyleParseError::PropertyDeclaration(
-                PropertyDeclarationParseError::InvalidValue(property, Some(e))))) =>
-            (Some(ErrorString::Snippet(property.into())), Some(extract_value_error_param(e))),
+        ParseErrorKind::Custom(StyleParseErrorKind::InvalidColor(property, token)) |
+        ParseErrorKind::Custom(StyleParseErrorKind::InvalidFilter(property, token)) => {
+            (Some(ErrorString::Snippet(property.into())), Some(ErrorString::UnexpectedToken(token)))
+        }
 
-        CssParseError::Custom(SelectorParseError::Custom(
-            StyleParseError::MediaQueryExpectedFeatureName(ident))) =>
-            (Some(ErrorString::Ident(ident)), None),
+        ParseErrorKind::Custom(
+            StyleParseErrorKind::MediaQueryExpectedFeatureName(ident)
+        ) => {
+            (Some(ErrorString::Ident(ident)), None)
+        }
 
-        CssParseError::Custom(SelectorParseError::Custom(
-            StyleParseError::ExpectedIdentifier(token))) =>
-            (Some(ErrorString::UnexpectedToken(token)), None),
+        ParseErrorKind::Custom(
+            StyleParseErrorKind::ExpectedIdentifier(token)
+        ) => {
+            (Some(ErrorString::UnexpectedToken(token)), None)
+        }
 
-        CssParseError::Custom(SelectorParseError::UnexpectedTokenInAttributeSelector(t)) |
-        CssParseError::Custom(SelectorParseError::BadValueInAttr(t)) |
-        CssParseError::Custom(SelectorParseError::ExpectedBarInAttr(t)) |
-        CssParseError::Custom(SelectorParseError::NoQualifiedNameInAttributeSelector(t)) |
-        CssParseError::Custom(SelectorParseError::InvalidQualNameInAttr(t)) |
-        CssParseError::Custom(SelectorParseError::ExplicitNamespaceUnexpectedToken(t)) |
-        CssParseError::Custom(SelectorParseError::PseudoElementExpectedIdent(t)) |
-        CssParseError::Custom(SelectorParseError::NoIdentForPseudo(t)) |
-        CssParseError::Custom(SelectorParseError::ClassNeedsIdent(t)) |
-        CssParseError::Custom(SelectorParseError::PseudoElementExpectedColon(t)) =>
-            (None, Some(ErrorString::UnexpectedToken(t))),
-
-        CssParseError::Custom(SelectorParseError::ExpectedNamespace(namespace)) =>
-            (None, Some(ErrorString::Ident(namespace))),
-
-        CssParseError::Custom(SelectorParseError::UnsupportedPseudoClassOrElement(p)) =>
-            (None, Some(ErrorString::Ident(p))),
-
-        CssParseError::Custom(SelectorParseError::EmptySelector) |
-        CssParseError::Custom(SelectorParseError::DanglingCombinator) =>
-            (None, None),
-
-        CssParseError::Custom(SelectorParseError::EmptyNegation) =>
-            (None, Some(ErrorString::Snippet(")".into()))),
-
+        ParseErrorKind::Custom(StyleParseErrorKind::SelectorError(err)) => match err {
+            SelectorParseErrorKind::UnexpectedTokenInAttributeSelector(t) |
+            SelectorParseErrorKind::BadValueInAttr(t) |
+            SelectorParseErrorKind::ExpectedBarInAttr(t) |
+            SelectorParseErrorKind::NoQualifiedNameInAttributeSelector(t) |
+            SelectorParseErrorKind::InvalidQualNameInAttr(t) |
+            SelectorParseErrorKind::ExplicitNamespaceUnexpectedToken(t) |
+            SelectorParseErrorKind::PseudoElementExpectedIdent(t) |
+            SelectorParseErrorKind::NoIdentForPseudo(t) |
+            SelectorParseErrorKind::ClassNeedsIdent(t) |
+            SelectorParseErrorKind::PseudoElementExpectedColon(t) => {
+                (None, Some(ErrorString::UnexpectedToken(t)))
+            }
+            SelectorParseErrorKind::ExpectedNamespace(namespace) => {
+                (None, Some(ErrorString::Ident(namespace)))
+            }
+            SelectorParseErrorKind::UnsupportedPseudoClassOrElement(p) => {
+                (None, Some(ErrorString::Ident(p)))
+            }
+            SelectorParseErrorKind::EmptySelector |
+            SelectorParseErrorKind::DanglingCombinator => {
+                (None, None)
+            }
+            SelectorParseErrorKind::EmptyNegation => {
+                (None, Some(ErrorString::Snippet(")".into())))
+            }
+            err => match extract_error_param(ParseErrorKind::Custom(StyleParseErrorKind::SelectorError(err))) {
+                Some(e) => (Some(e), None),
+                None => return None,
+            }
+        },
         err => match extract_error_param(err) {
             Some(e) => (Some(e), None),
             None => return None,
         }
     };
     Some(ErrorParams {
         main_param: main,
         prefix_param: prefix,
     })
 }
 
 impl<'a> ErrorHelpers<'a> for ContextualParseError<'a> {
-    fn error_data(self) -> (CowRcStr<'a>, ParseError<'a>) {
+    fn error_data(self) -> (CowRcStr<'a>, ErrorKind<'a>) {
         match self {
             ContextualParseError::UnsupportedPropertyDeclaration(s, err) |
             ContextualParseError::UnsupportedFontFaceDescriptor(s, err) |
             ContextualParseError::UnsupportedFontFeatureValuesDescriptor(s, err) |
             ContextualParseError::InvalidKeyframeRule(s, err) |
             ContextualParseError::InvalidFontFeatureValuesRule(s, err) |
             ContextualParseError::UnsupportedKeyframePropertyDeclaration(s, err) |
             ContextualParseError::InvalidRule(s, err) |
             ContextualParseError::UnsupportedRule(s, err) |
             ContextualParseError::UnsupportedViewportDescriptorDeclaration(s, err) |
             ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(s, err) |
-            ContextualParseError::InvalidMediaRule(s, err) =>
-                (s.into(), err),
+            ContextualParseError::InvalidMediaRule(s, err) => {
+                (s.into(), err.kind)
+            }
             ContextualParseError::InvalidCounterStyleWithoutSymbols(s) |
-            ContextualParseError::InvalidCounterStyleNotEnoughSymbols(s) =>
-                (s.into(), StyleParseError::UnspecifiedError.into()),
+            ContextualParseError::InvalidCounterStyleNotEnoughSymbols(s) => {
+                (s.into(), ParseErrorKind::Custom(StyleParseErrorKind::UnspecifiedError.into()))
+            }
             ContextualParseError::InvalidCounterStyleWithoutAdditiveSymbols |
             ContextualParseError::InvalidCounterStyleExtendsWithSymbols |
-            ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols =>
-                ("".into(), StyleParseError::UnspecifiedError.into())
+            ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols => {
+                ("".into(), ParseErrorKind::Custom(StyleParseErrorKind::UnspecifiedError.into()))
+            }
         }
     }
 
     fn error_params(self) -> ErrorParams<'a> {
         let (s, error) = self.error_data();
         extract_error_params(error).unwrap_or_else(|| ErrorParams {
             main_param: Some(ErrorString::Snippet(s)),
             prefix_param: None
         })
     }
 
     fn to_gecko_message(&self) -> (Option<&'static [u8]>, &'static [u8], Action) {
         let (msg, action): (&[u8], Action) = match *self {
             ContextualParseError::UnsupportedPropertyDeclaration(
-                _, CssParseError::Basic(BasicParseError::UnexpectedToken(_))) |
+                _, ParseError { kind: ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(_)), .. }
+            ) |
             ContextualParseError::UnsupportedPropertyDeclaration(
-                _, CssParseError::Basic(BasicParseError::AtRuleInvalid(_))) =>
-                (b"PEParseDeclarationDeclExpected\0", Action::Skip),
+                _, ParseError { kind: ParseErrorKind::Basic(BasicParseErrorKind::AtRuleInvalid(_)), .. }
+            ) => {
+                (b"PEParseDeclarationDeclExpected\0", Action::Skip)
+            }
             ContextualParseError::UnsupportedPropertyDeclaration(
-                _, CssParseError::Custom(SelectorParseError::Custom(
-                    StyleParseError::PropertyDeclaration(
-                        PropertyDeclarationParseError::InvalidValue(_, ref err))))) => {
-                let prefix = match *err {
-                    Some(ValueParseError::InvalidColor(_)) => Some(&b"PEColorNotColor\0"[..]),
-                    Some(ValueParseError::InvalidFilter(_)) => Some(&b"PEExpectedNoneOrURLOrFilterFunction\0"[..]),
-                    _ => None,
-                };
-                return (prefix, b"PEValueParsingError\0", Action::Drop);
+                _, ParseError { kind: ParseErrorKind::Custom(ref err), .. }
+            ) => {
+                match *err {
+                    StyleParseErrorKind::InvalidColor(_, _) => {
+                        return (Some(b"PEColorNotColor\0"),
+                                b"PEValueParsingError\0", Action::Drop)
+                    }
+                    StyleParseErrorKind::InvalidFilter(_, _) => {
+                        return (Some(b"PEExpectedNoneOrURLOrFilterFunction\0"),
+                                b"PEValueParsingError\0", Action::Drop)
+                    }
+                    StyleParseErrorKind::OtherInvalidValue(_) => {
+                        (b"PEValueParsingError\0", Action::Drop)
+                    }
+                    _ => (b"PEUnknownProperty\0", Action::Drop)
+                }
             }
             ContextualParseError::UnsupportedPropertyDeclaration(..) =>
                 (b"PEUnknownProperty\0", Action::Drop),
             ContextualParseError::UnsupportedFontFaceDescriptor(..) =>
                 (b"PEUnknwnFontDesc\0", Action::Skip),
             ContextualParseError::InvalidKeyframeRule(..) =>
                 (b"PEKeyframeBadName\0", Action::Nothing),
             ContextualParseError::UnsupportedKeyframePropertyDeclaration(..) =>
                 (b"PEBadSelectorKeyframeRuleIgnored\0", Action::Nothing),
             ContextualParseError::InvalidRule(
-                _, CssParseError::Custom(SelectorParseError::Custom(
-                StyleParseError::UnexpectedTokenWithinNamespace(_)))) =>
-                (b"PEAtNSUnexpected\0", Action::Nothing),
+                _, ParseError { kind: ParseErrorKind::Custom(
+                    StyleParseErrorKind::UnexpectedTokenWithinNamespace(_)
+                ), .. }
+            ) => {
+                (b"PEAtNSUnexpected\0", Action::Nothing)
+            }
             ContextualParseError::InvalidRule(
-                _, CssParseError::Basic(BasicParseError::AtRuleInvalid(_))) |
+                _, ParseError { kind: ParseErrorKind::Basic(BasicParseErrorKind::AtRuleInvalid(_)), .. }
+            ) |
             ContextualParseError::InvalidRule(
-                _, CssParseError::Custom(SelectorParseError::Custom(
-                    StyleParseError::UnsupportedAtRule(_)))) =>
-                (b"PEUnknownAtRule\0", Action::Nothing),
+                _, ParseError { kind: ParseErrorKind::Custom(
+                    StyleParseErrorKind::UnsupportedAtRule(_)
+                ), .. }
+            ) => {
+                (b"PEUnknownAtRule\0", Action::Nothing)
+            }
             ContextualParseError::InvalidRule(_, ref err) => {
-                let prefix = match *err {
-                    CssParseError::Custom(SelectorParseError::UnexpectedTokenInAttributeSelector(_)) =>
-                        Some(&b"PEAttSelUnexpected\0"[..]),
-                    CssParseError::Custom(SelectorParseError::ExpectedBarInAttr(_)) =>
-                        Some(&b"PEAttSelNoBar\0"[..]),
-                    CssParseError::Custom(SelectorParseError::BadValueInAttr(_)) =>
-                        Some(&b"PEAttSelBadValue\0"[..]),
-                    CssParseError::Custom(SelectorParseError::NoQualifiedNameInAttributeSelector(_)) =>
-                        Some(&b"PEAttributeNameOrNamespaceExpected\0"[..]),
-                    CssParseError::Custom(SelectorParseError::InvalidQualNameInAttr(_)) =>
-                        Some(&b"PEAttributeNameExpected\0"[..]),
-                    CssParseError::Custom(SelectorParseError::ExplicitNamespaceUnexpectedToken(_)) =>
-                        Some(&b"PETypeSelNotType\0"[..]),
-                    CssParseError::Custom(SelectorParseError::ExpectedNamespace(_)) =>
-                       Some(&b"PEUnknownNamespacePrefix\0"[..]),
-                    CssParseError::Custom(SelectorParseError::EmptySelector) =>
-                        Some(&b"PESelectorGroupNoSelector\0"[..]),
-                    CssParseError::Custom(SelectorParseError::DanglingCombinator) =>
-                        Some(&b"PESelectorGroupExtraCombinator\0"[..]),
-                    CssParseError::Custom(SelectorParseError::UnsupportedPseudoClassOrElement(_)) =>
-                        Some(&b"PEPseudoSelUnknown\0"[..]),
-                    CssParseError::Custom(SelectorParseError::PseudoElementExpectedColon(_)) =>
-                        Some(&b"PEPseudoSelEndOrUserActionPC\0"[..]),
-                    CssParseError::Custom(SelectorParseError::NoIdentForPseudo(_)) =>
-                        Some(&b"PEPseudoClassArgNotIdent\0"[..]),
-                    CssParseError::Custom(SelectorParseError::PseudoElementExpectedIdent(_)) =>
-                        Some(&b"PEPseudoSelBadName\0"[..]),
-                    CssParseError::Custom(SelectorParseError::ClassNeedsIdent(_)) =>
-                        Some(&b"PEClassSelNotIdent\0"[..]),
-                    CssParseError::Custom(SelectorParseError::EmptyNegation) =>
-                        Some(&b"PENegationBadArg\0"[..]),
+                let prefix = match err.kind {
+                    ParseErrorKind::Custom(StyleParseErrorKind::SelectorError(ref err)) => match *err {
+                        SelectorParseErrorKind::UnexpectedTokenInAttributeSelector(_) => {
+                            Some(&b"PEAttSelUnexpected\0"[..])
+                        }
+                        SelectorParseErrorKind::ExpectedBarInAttr(_) => {
+                            Some(&b"PEAttSelNoBar\0"[..])
+                        }
+                        SelectorParseErrorKind::BadValueInAttr(_) => {
+                            Some(&b"PEAttSelBadValue\0"[..])
+                        }
+                        SelectorParseErrorKind::NoQualifiedNameInAttributeSelector(_) => {
+                            Some(&b"PEAttributeNameOrNamespaceExpected\0"[..])
+                        }
+                        SelectorParseErrorKind::InvalidQualNameInAttr(_) => {
+                            Some(&b"PEAttributeNameExpected\0"[..])
+                        }
+                        SelectorParseErrorKind::ExplicitNamespaceUnexpectedToken(_) => {
+                            Some(&b"PETypeSelNotType\0"[..])
+                        }
+                        SelectorParseErrorKind::ExpectedNamespace(_) => {
+                           Some(&b"PEUnknownNamespacePrefix\0"[..])
+                        }
+                        SelectorParseErrorKind::EmptySelector => {
+                            Some(&b"PESelectorGroupNoSelector\0"[..])
+                        }
+                        SelectorParseErrorKind::DanglingCombinator => {
+                            Some(&b"PESelectorGroupExtraCombinator\0"[..])
+                        }
+                        SelectorParseErrorKind::UnsupportedPseudoClassOrElement(_) => {
+                            Some(&b"PEPseudoSelUnknown\0"[..])
+                        }
+                        SelectorParseErrorKind::PseudoElementExpectedColon(_) => {
+                            Some(&b"PEPseudoSelEndOrUserActionPC\0"[..])
+                        }
+                        SelectorParseErrorKind::NoIdentForPseudo(_) => {
+                            Some(&b"PEPseudoClassArgNotIdent\0"[..])
+                        }
+                        SelectorParseErrorKind::PseudoElementExpectedIdent(_) => {
+                            Some(&b"PEPseudoSelBadName\0"[..])
+                        }
+                        SelectorParseErrorKind::ClassNeedsIdent(_) => {
+                            Some(&b"PEClassSelNotIdent\0"[..])
+                        }
+                        SelectorParseErrorKind::EmptyNegation => {
+                            Some(&b"PENegationBadArg\0"[..])
+                        }
+                        _ => None,
+                    },
                     _ => None,
                 };
                 return (prefix, b"PEBadSelectorRSIgnored\0", Action::Nothing);
             }
             ContextualParseError::InvalidMediaRule(_, ref err) => {
-                let err: &[u8] = match *err {
-                    CssParseError::Custom(SelectorParseError::Custom(
-                            StyleParseError::ExpectedIdentifier(..))) => {
+                let err: &[u8] = match err.kind {
+                    ParseErrorKind::Custom(StyleParseErrorKind::ExpectedIdentifier(..)) => {
                         b"PEGatherMediaNotIdent\0"
                     },
-                    CssParseError::Custom(SelectorParseError::Custom(
-                            StyleParseError::MediaQueryExpectedFeatureName(..))) => {
+                    ParseErrorKind::Custom(StyleParseErrorKind::MediaQueryExpectedFeatureName(..)) => {
                         b"PEMQExpectedFeatureName\0"
                     },
-                    CssParseError::Custom(SelectorParseError::Custom(
-                            StyleParseError::MediaQueryExpectedFeatureValue)) => {
+                    ParseErrorKind::Custom(StyleParseErrorKind::MediaQueryExpectedFeatureValue) => {
                         b"PEMQExpectedFeatureValue\0"
                     },
-                    CssParseError::Custom(SelectorParseError::Custom(
-                            StyleParseError::RangedExpressionWithNoValue)) => {
+                    ParseErrorKind::Custom(StyleParseErrorKind::RangedExpressionWithNoValue) => {
                         b"PEMQNoMinMaxWithoutValue\0"
                     },
                     _ => {
                         b"PEDeclDropped\0"
                     },
                 };
                 (err, Action::Nothing)
             }
--- a/servo/tests/unit/gfx/Cargo.toml
+++ b/servo/tests/unit/gfx/Cargo.toml
@@ -5,12 +5,12 @@ authors = ["The Servo Project Developers
 license = "MPL-2.0"
 
 [lib]
 name = "gfx_tests"
 path = "lib.rs"
 doctest = false
 
 [dependencies]
-cssparser = "0.21.1"
+cssparser = "0.22.0"
 gfx = {path = "../../../components/gfx"}
 ipc-channel = "0.8"
 style = {path = "../../../components/style"}
--- a/servo/tests/unit/style/Cargo.toml
+++ b/servo/tests/unit/style/Cargo.toml
@@ -7,17 +7,17 @@ license = "MPL-2.0"
 [lib]
 name = "style_tests"
 path = "lib.rs"
 doctest = false
 
 [dependencies]
 byteorder = "1.0"
 app_units = "0.5"
-cssparser = "0.21.1"
+cssparser = "0.22.0"
 euclid = "0.15"
 html5ever = "0.20"
 parking_lot = "0.4"
 rayon = "0.8"
 rustc-serialize = "0.3"
 selectors = {path = "../../../components/selectors"}
 servo_arc = {path = "../../../components/servo_arc"}
 servo_atoms = {path = "../../../components/atoms"}
--- a/servo/tests/unit/style/size_of.rs
+++ b/servo/tests/unit/style/size_of.rs
@@ -1,14 +1,23 @@
 /* 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 selectors::parser::{SelectorParseError, SelectorParseErrorKind};
 use style::invalidation::element::invalidation_map::Dependency;
 use style::properties;
 
 size_of_test!(test_size_of_dependency, Dependency, 16);
 
 size_of_test!(test_size_of_property_declaration, properties::PropertyDeclaration, 32);
 
 // This is huge, but we allocate it on the stack and then never move it,
 // we only pass `&mut SourcePropertyDeclaration` references around.
 size_of_test!(test_size_of_parsed_declaration, properties::SourcePropertyDeclaration, 576);
+
+size_of_test!(test_size_of_selector_parse_error_kind, SelectorParseErrorKind, 40);
+size_of_test!(test_size_of_style_parse_error_kind, ::style_traits::StyleParseErrorKind, 56);
+size_of_test!(test_size_of_value_parse_error_kind, ::style_traits::ValueParseErrorKind, 40);
+
+size_of_test!(test_size_of_selector_parse_error, SelectorParseError, 56);
+size_of_test!(test_size_of_style_traits_parse_error, ::style_traits::ParseError, 72);
+size_of_test!(test_size_of_value_parse_error, ::style_traits::ValueParseError, 56);
--- a/servo/tests/unit/style/stylesheets.rs
+++ b/servo/tests/unit/style/stylesheets.rs
@@ -5,19 +5,20 @@
 use cssparser::{self, SourceLocation};
 use html5ever::{Namespace as NsAtom};
 use media_queries::CSSErrorReporterTest;
 use parking_lot::RwLock;
 use selectors::attr::*;
 use selectors::parser::*;
 use servo_arc::Arc;
 use servo_atoms::Atom;
+use servo_config::prefs::{PREFS, PrefValue};
 use servo_url::ServoUrl;
 use std::borrow::ToOwned;
-use std::sync::Mutex;
+use std::cell::RefCell;
 use std::sync::atomic::AtomicBool;
 use style::context::QuirksMode;
 use style::error_reporting::{ParseErrorReporter, ContextualParseError};
 use style::media_queries::MediaList;
 use style::properties::Importance;
 use style::properties::{CSSWideKeyword, DeclaredValueOwned, PropertyDeclaration, PropertyDeclarationBlock};
 use style::properties::longhands;
 use style::properties::longhands::animation_timing_function;
@@ -254,115 +255,143 @@ fn test_parse_stylesheet() {
         media: Arc::new(stylesheet.shared_lock.wrap(MediaList::empty())),
         shared_lock: stylesheet.shared_lock.clone(),
         disabled: AtomicBool::new(false),
     };
 
     assert_eq!(format!("{:#?}", stylesheet), format!("{:#?}", expected));
 }
 
+#[derive(Debug)]
 struct CSSError {
     pub url : ServoUrl,
     pub line: u32,
     pub column: u32,
     pub message: String
 }
 
-struct CSSInvalidErrorReporterTest {
-    pub errors: Arc<Mutex<Vec<CSSError>>>
+struct TestingErrorReporter {
+    errors: RefCell<Vec<CSSError>>,
 }
 
-impl CSSInvalidErrorReporterTest {
-    pub fn new() -> CSSInvalidErrorReporterTest {
-        return CSSInvalidErrorReporterTest{
-            errors: Arc::new(Mutex::new(Vec::new()))
+impl TestingErrorReporter {
+    pub fn new() -> Self {
+        TestingErrorReporter {
+            errors: RefCell::new(Vec::new()),
+        }
+    }
+
+    fn assert_messages_contain(&self, expected_errors: &[(u32, u32, &str)]) {
+        let errors = self.errors.borrow();
+        for (i, (error, &(line, column, message))) in errors.iter().zip(expected_errors).enumerate() {
+            assert_eq!((error.line, error.column), (line, column),
+                       "line/column numbers of the {}th error: {:?}", i + 1, error.message);
+            assert!(error.message.contains(message),
+                    "{:?} does not contain {:?}", error.message, message);
+        }
+        if errors.len() < expected_errors.len() {
+            panic!("Missing errors: {:#?}", &expected_errors[errors.len()..]);
+        }
+        if errors.len() > expected_errors.len() {
+            panic!("Extra errors: {:#?}", &errors[expected_errors.len()..]);
         }
     }
 }
 
-impl ParseErrorReporter for CSSInvalidErrorReporterTest {
+impl ParseErrorReporter for TestingErrorReporter {
     fn report_error(&self,
                     url: &ServoUrl,
                     location: SourceLocation,
                     error: ContextualParseError) {
-        let mut errors = self.errors.lock().unwrap();
-        errors.push(
+        self.errors.borrow_mut().push(
             CSSError{
                 url: url.clone(),
                 line: location.line,
                 column: location.column,
                 message: error.to_string(),
             }
-        );
+        )
     }
 }
 
 
 #[test]
 fn test_report_error_stylesheet() {
+    PREFS.set("layout.viewport.enabled", PrefValue::Boolean(true));
     let css = r"
     div {
         background-color: red;
         display: invalid;
+        background-image: linear-gradient(0deg, black, invalid, transparent);
         invalid: true;
     }
+    @media (min-width: 10px invalid 1000px) {}
+    @font-face { src: url(), invalid, url(); }
+    @counter-style foo { symbols: a 0invalid b }
+    @font-feature-values Sans Sans { @foo {} @swash { foo: 1 invalid 2 } }
+    @invalid;
+    @media screen { @invalid; }
+    @supports (color: green) and invalid and (margin: 0) {}
+    @keyframes foo { from invalid {} to { margin: 0 invalid 0; } }
+    @viewport { width: 320px invalid auto; }
     ";
     let url = ServoUrl::parse("about::test").unwrap();
-    let error_reporter = CSSInvalidErrorReporterTest::new();
-
-    let errors = error_reporter.errors.clone();
+    let error_reporter = TestingErrorReporter::new();
 
     let lock = SharedRwLock::new();
     let media = Arc::new(lock.wrap(MediaList::empty()));
     Stylesheet::from_str(css, url.clone(), Origin::UserAgent, media, lock,
                          None, &error_reporter, QuirksMode::NoQuirks, 5);
 
-    let mut errors = errors.lock().unwrap();
+    error_reporter.assert_messages_contain(&[
+        (8, 18, "Unsupported property declaration: 'display: invalid;'"),
+        (9, 27, "Unsupported property declaration: 'background-image:"),  // FIXME: column should be around 56
+        (10, 17, "Unsupported property declaration: 'invalid: true;'"),
+        (12, 28, "Invalid media rule"),
+        (13, 30, "Unsupported @font-face descriptor declaration"),
 
-    let error = errors.pop().unwrap();
-    assert_eq!("Unsupported property declaration: 'invalid: true;', \
-                Custom(PropertyDeclaration(UnknownProperty(\"invalid\")))", error.message);
-    assert_eq!(9, error.line);
-    assert_eq!(9, error.column);
+        // When @counter-style is supported, this should be replaced with two errors
+        (14, 19, "Invalid rule: '@counter-style "),
 
-    let error = errors.pop().unwrap();
-    assert_eq!("Unsupported property declaration: 'display: invalid;', \
-                Custom(PropertyDeclaration(InvalidValue(\"display\", None)))", error.message);
-    assert_eq!(8, error.line);
-    assert_eq!(9, error.column);
+        // When @font-feature-values is supported, this should be replaced with two errors
+        (15, 25, "Invalid rule: '@font-feature-values "),
+
+        // FIXME: the message of these two should be consistent
+        (16, 13, "Invalid rule: '@invalid'"),
+        (17, 29, "Unsupported rule: '@invalid'"),
 
-    // testing for the url
-    assert_eq!(url, error.url);
+        (18, 34, "Invalid rule: '@supports "),
+        (19, 26, "Invalid keyframe rule: 'from invalid '"),
+        (19, 52, "Unsupported keyframe property declaration: 'margin: 0 invalid 0;'"),
+        (20, 29, "Unsupported @viewport descriptor declaration: 'width: 320px invalid auto;'"),
+    ]);
+
+    assert_eq!(error_reporter.errors.borrow()[0].url, url);
 }
 
 #[test]
 fn test_no_report_unrecognized_vendor_properties() {
     let css = r"
     div {
         -o-background-color: red;
         _background-color: red;
         -moz-background-color: red;
     }
     ";
     let url = ServoUrl::parse("about::test").unwrap();
-    let error_reporter = CSSInvalidErrorReporterTest::new();
-
-    let errors = error_reporter.errors.clone();
+    let error_reporter = TestingErrorReporter::new();
 
     let lock = SharedRwLock::new();
     let media = Arc::new(lock.wrap(MediaList::empty()));
     Stylesheet::from_str(css, url, Origin::UserAgent, media, lock,
                          None, &error_reporter, QuirksMode::NoQuirks, 0);
 
-    let mut errors = errors.lock().unwrap();
-    let error = errors.pop().unwrap();
-    assert_eq!("Unsupported property declaration: '-moz-background-color: red;', \
-                Custom(PropertyDeclaration(UnknownProperty(\"-moz-background-color\")))",
-               error.message);
-    assert!(errors.is_empty());
+    error_reporter.assert_messages_contain(&[
+        (4, 31, "Unsupported property declaration: '-moz-background-color: red;'"),
+    ]);
 }
 
 #[test]
 fn test_source_map_url() {
     let tests = vec![
         ("", None),
         ("/*# sourceMappingURL=something */", Some("something".to_string())),
     ];