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 436109 c0005a5aa643e2894a639dda34bf9e6250f55c13
parent 436108 447614924cd21b68c55aef75f500e2659c9962f5
child 436110 eb7f98e69e26b838334556015835471aeaafa757
push id8114
push userjlorenzo@mozilla.com
push dateThu, 02 Nov 2017 16:33:21 +0000
treeherdermozilla-beta@73e0d89a540f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
milestone58.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
servo: Merge #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())),
     ];
--- a/servo/tests/unit/stylo/Cargo.toml
+++ b/servo/tests/unit/stylo/Cargo.toml
@@ -8,17 +8,17 @@ build = "build.rs"
 
 [lib]
 name = "stylo_tests"
 path = "lib.rs"
 doctest = false
 
 [dependencies]
 atomic_refcell = "0.1"
-cssparser = "0.21.1"
+cssparser = "0.22.0"
 env_logger = "0.4"
 euclid = "0.15"
 geckoservo = {path = "../../../ports/geckolib"}
 libc = "0.2"
 log = {version = "0.3.5", features = ["release_max_level_info"]}
 malloc_size_of = {path = "../../../components/malloc_size_of"}
 selectors = {path = "../../../components/selectors", features = ["gecko_like_types"]}
 size_of_test = {path = "../../../components/size_of_test"}