servo: Merge #16373 - Stylo: Disable viewport units for @page (from jryans:at-page-viewport-units); r=emilio
authorJ. Ryan Stinnett <jryans@gmail.com>
Wed, 12 Apr 2017 05:38:50 -0500
changeset 400777 c9955343ba525bdb70877163f97836351eb03dee
parent 400776 86a7adbdc6ef878b06834b1d32c918a189aaba28
child 400778 4906cc8b148cf8802f0720b9bf0a16b1f7bc19ad
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
bugs1353191
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
servo: Merge #16373 - Stylo: Disable viewport units for @page (from jryans:at-page-viewport-units); r=emilio Reviewed by @emilio in [bug 1353191](https://bugzilla.mozilla.org/show_bug.cgi?id=1353191). --- - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors Source-Repo: https://github.com/servo/servo Source-Revision: 5f6c27bb945eba1242be2fb4812ea5095ef26377
servo/components/script/dom/css.rs
servo/components/script/dom/cssmediarule.rs
servo/components/script/dom/csssupportsrule.rs
servo/components/script/dom/htmllinkelement.rs
servo/components/script/dom/htmlstyleelement.rs
servo/components/script/dom/medialist.rs
servo/components/script/dom/window.rs
servo/components/style/gecko/media_queries.rs
servo/components/style/keyframes.rs
servo/components/style/media_queries.rs
servo/components/style/parser.rs
servo/components/style/properties/declaration_block.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/effects.mako.rs
servo/components/style/properties/longhand/font.mako.rs
servo/components/style/properties/longhand/inherited_svg.mako.rs
servo/components/style/properties/longhand/inherited_table.mako.rs
servo/components/style/properties/longhand/inherited_text.mako.rs
servo/components/style/properties/longhand/outline.mako.rs
servo/components/style/properties/longhand/padding.mako.rs
servo/components/style/properties/longhand/position.mako.rs
servo/components/style/properties/longhand/text.mako.rs
servo/components/style/properties/longhand/xul.mako.rs
servo/components/style/properties/properties.mako.rs
servo/components/style/properties/shorthand/position.mako.rs
servo/components/style/servo/media_queries.rs
servo/components/style/stylesheets.rs
servo/components/style/supports.rs
servo/components/style/values/specified/basic_shape.rs
servo/components/style/values/specified/grid.rs
servo/components/style/values/specified/length.rs
servo/components/style/values/specified/mod.rs
servo/components/style/viewport.rs
servo/ports/geckolib/glue.rs
servo/tests/unit/style/parsing/animation.rs
servo/tests/unit/style/parsing/background.rs
servo/tests/unit/style/parsing/basic_shape.rs
servo/tests/unit/style/parsing/border.rs
servo/tests/unit/style/parsing/box_.rs
servo/tests/unit/style/parsing/column.rs
servo/tests/unit/style/parsing/containment.rs
servo/tests/unit/style/parsing/effects.rs
servo/tests/unit/style/parsing/font.rs
servo/tests/unit/style/parsing/image.rs
servo/tests/unit/style/parsing/inherited_box.rs
servo/tests/unit/style/parsing/inherited_text.rs
servo/tests/unit/style/parsing/length.rs
servo/tests/unit/style/parsing/mask.rs
servo/tests/unit/style/parsing/mod.rs
servo/tests/unit/style/parsing/outline.rs
servo/tests/unit/style/parsing/position.rs
servo/tests/unit/style/parsing/selectors.rs
servo/tests/unit/style/parsing/text.rs
servo/tests/unit/style/parsing/text_overflow.rs
servo/tests/unit/style/parsing/transition_timing_function.rs
servo/tests/unit/style/parsing/ui.rs
servo/tests/unit/style/properties/background.rs
servo/tests/unit/style/properties/serialization.rs
servo/tests/unit/style/value.rs
servo/tests/unit/style/viewport.rs
--- a/servo/components/script/dom/css.rs
+++ b/servo/components/script/dom/css.rs
@@ -5,16 +5,17 @@
 use cssparser::{Parser, serialize_identifier};
 use dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods;
 use dom::bindings::error::Fallible;
 use dom::bindings::reflector::Reflector;
 use dom::bindings::str::DOMString;
 use dom::window::Window;
 use dom_struct::dom_struct;
 use style::parser::ParserContext;
+use style::stylesheets::CssRuleType;
 use style::supports::{Declaration, parse_condition_or_declaration};
 
 #[dom_struct]
 pub struct CSS {
     reflector_: Reflector,
 }
 
 impl CSS {
@@ -24,25 +25,25 @@ impl CSS {
         serialize_identifier(&ident, &mut escaped).unwrap();
         Ok(DOMString::from(escaped))
     }
 
     /// https://drafts.csswg.org/css-conditional/#dom-css-supports
     pub fn Supports(win: &Window, property: DOMString, value: DOMString) -> bool {
         let decl = Declaration { prop: property.into(), val: value.into() };
         let url = win.Document().url();
-        let context = ParserContext::new_for_cssom(&url, win.css_error_reporter());
+        let context = ParserContext::new_for_cssom(&url, win.css_error_reporter(), Some(CssRuleType::Supports));
         decl.eval(&context)
     }
 
     /// https://drafts.csswg.org/css-conditional/#dom-css-supports
     pub fn Supports_(win: &Window, condition: DOMString) -> bool {
         let mut input = Parser::new(&condition);
         let cond = parse_condition_or_declaration(&mut input);
         if let Ok(cond) = cond {
             let url = win.Document().url();
-            let context = ParserContext::new_for_cssom(&url, win.css_error_reporter());
+            let context = ParserContext::new_for_cssom(&url, win.css_error_reporter(), Some(CssRuleType::Supports));
             cond.eval(&context)
         } else {
             false
         }
     }
 }
--- a/servo/components/script/dom/cssmediarule.rs
+++ b/servo/components/script/dom/cssmediarule.rs
@@ -11,18 +11,19 @@ use dom::bindings::str::DOMString;
 use dom::cssconditionrule::CSSConditionRule;
 use dom::cssrule::SpecificCSSRule;
 use dom::cssstylesheet::CSSStyleSheet;
 use dom::medialist::MediaList;
 use dom::window::Window;
 use dom_struct::dom_struct;
 use std::sync::Arc;
 use style::media_queries::parse_media_query_list;
+use style::parser::ParserContext;
 use style::shared_lock::{Locked, ToCssWithGuard};
-use style::stylesheets::MediaRule;
+use style::stylesheets::{CssRuleType, MediaRule};
 use style_traits::ToCss;
 
 #[dom_struct]
 pub struct CSSMediaRule {
     cssconditionrule: CSSConditionRule,
     #[ignore_heap_size_of = "Arc"]
     mediarule: Arc<Locked<MediaRule>>,
     medialist: MutNullableJS<MediaList>,
@@ -63,17 +64,21 @@ impl CSSMediaRule {
         let rule = self.mediarule.read_with(&guard);
         let list = rule.media_queries.read_with(&guard);
         list.to_css_string().into()
     }
 
     /// https://drafts.csswg.org/css-conditional-3/#the-cssmediarule-interface
     pub fn set_condition_text(&self, text: DOMString) {
         let mut input = Parser::new(&text);
-        let new_medialist = parse_media_query_list(&mut input);
+        let global = self.global();
+        let win = global.as_window();
+        let url = win.get_url();
+        let context = ParserContext::new_for_cssom(&url, win.css_error_reporter(), Some(CssRuleType::Media));
+        let new_medialist = parse_media_query_list(&context, &mut input);
         let mut guard = self.cssconditionrule.shared_lock().write();
 
         // Clone an Arc because we can’t borrow `guard` twice at the same time.
 
         // FIXME(SimonSapin): allow access to multiple objects with one write guard?
         // Would need a set of usize pointer addresses or something,
         // the same object is not accessed more than once.
         let mqs = Arc::clone(&self.mediarule.write_with(&mut guard).media_queries);
--- a/servo/components/script/dom/csssupportsrule.rs
+++ b/servo/components/script/dom/csssupportsrule.rs
@@ -11,17 +11,17 @@ use dom::bindings::str::DOMString;
 use dom::cssconditionrule::CSSConditionRule;
 use dom::cssrule::SpecificCSSRule;
 use dom::cssstylesheet::CSSStyleSheet;
 use dom::window::Window;
 use dom_struct::dom_struct;
 use std::sync::Arc;
 use style::parser::ParserContext;
 use style::shared_lock::{Locked, ToCssWithGuard};
-use style::stylesheets::SupportsRule;
+use style::stylesheets::{CssRuleType, SupportsRule};
 use style::supports::SupportsCondition;
 use style_traits::ToCss;
 
 #[dom_struct]
 pub struct CSSSupportsRule {
     cssconditionrule: CSSConditionRule,
     #[ignore_heap_size_of = "Arc"]
     supportsrule: Arc<Locked<SupportsRule>>,
@@ -56,17 +56,17 @@ impl CSSSupportsRule {
     /// https://drafts.csswg.org/css-conditional-3/#the-csssupportsrule-interface
     pub fn set_condition_text(&self, text: DOMString) {
         let mut input = Parser::new(&text);
         let cond = SupportsCondition::parse(&mut input);
         if let Ok(cond) = cond {
             let global = self.global();
             let win = global.as_window();
             let url = win.Document().url();
-            let context = ParserContext::new_for_cssom(&url, win.css_error_reporter());
+            let context = ParserContext::new_for_cssom(&url, win.css_error_reporter(), Some(CssRuleType::Supports));
             let enabled = cond.eval(&context);
             let mut guard = self.cssconditionrule.shared_lock().write();
             let rule = self.supportsrule.write_with(&mut guard);
             rule.condition = cond;
             rule.enabled = enabled;
         }
     }
 }
--- a/servo/components/script/dom/htmllinkelement.rs
+++ b/servo/components/script/dom/htmllinkelement.rs
@@ -27,18 +27,19 @@ use net_traits::ReferrerPolicy;
 use script_traits::{MozBrowserEvent, ScriptMsg as ConstellationMsg};
 use std::ascii::AsciiExt;
 use std::borrow::ToOwned;
 use std::cell::Cell;
 use std::default::Default;
 use std::sync::Arc;
 use style::attr::AttrValue;
 use style::media_queries::parse_media_query_list;
+use style::parser::ParserContext as CssParserContext;
 use style::str::HTML_SPACE_CHARACTERS;
-use style::stylesheets::Stylesheet;
+use style::stylesheets::{CssRuleType, Stylesheet};
 use stylesheet_loader::{StylesheetLoader, StylesheetContextSource, StylesheetOwner};
 
 unsafe_no_jsmanaged_fields!(Stylesheet);
 
 #[derive(JSTraceable, PartialEq, Clone, Copy, HeapSizeOf)]
 pub struct RequestGenerationId(u32);
 
 impl RequestGenerationId {
@@ -250,17 +251,17 @@ impl HTMLLinkElement {
         }
 
         // Step 1.
         if href.is_empty() {
             return;
         }
 
         // Step 2.
-        let url = match document.base_url().join(href) {
+        let link_url = match document.base_url().join(href) {
             Ok(url) => url,
             Err(e) => {
                 debug!("Parsing url {} failed: {}", href, e);
                 return;
             }
         };
 
         let element = self.upcast::<Element>();
@@ -271,33 +272,36 @@ impl HTMLLinkElement {
         let mq_attribute = element.get_attribute(&ns!(), &local_name!("media"));
         let value = mq_attribute.r().map(|a| a.value());
         let mq_str = match value {
             Some(ref value) => &***value,
             None => "",
         };
 
         let mut css_parser = CssParser::new(&mq_str);
-        let media = parse_media_query_list(&mut css_parser);
+        let win = document.window();
+        let doc_url = document.url();
+        let context = CssParserContext::new_for_cssom(&doc_url, win.css_error_reporter(), Some(CssRuleType::Media));
+        let media = parse_media_query_list(&context, &mut css_parser);
 
         let im_attribute = element.get_attribute(&ns!(), &local_name!("integrity"));
         let integrity_val = im_attribute.r().map(|a| a.value());
         let integrity_metadata = match integrity_val {
             Some(ref value) => &***value,
             None => "",
         };
 
         self.request_generation_id.set(self.request_generation_id.get().increment());
 
         // TODO: #8085 - Don't load external stylesheets if the node's mq
         // doesn't match.
         let loader = StylesheetLoader::for_element(self.upcast());
         loader.load(StylesheetContextSource::LinkElement {
             media: Some(media),
-        }, url, cors_setting, integrity_metadata.to_owned());
+        }, link_url, cors_setting, integrity_metadata.to_owned());
     }
 
     fn handle_favicon_url(&self, rel: &str, href: &str, sizes: &Option<String>) {
         let document = document_from_node(self);
         match document.base_url().join(href) {
             Ok(url) => {
                 let event = ConstellationMsg::NewFavicon(url.clone());
                 document.window().upcast::<GlobalScope>().constellation_chan().send(event).unwrap();
--- a/servo/components/script/dom/htmlstyleelement.rs
+++ b/servo/components/script/dom/htmlstyleelement.rs
@@ -20,17 +20,18 @@ use dom::stylesheet::StyleSheet as DOMSt
 use dom::virtualmethods::VirtualMethods;
 use dom_struct::dom_struct;
 use html5ever_atoms::LocalName;
 use net_traits::ReferrerPolicy;
 use script_layout_interface::message::Msg;
 use std::cell::Cell;
 use std::sync::Arc;
 use style::media_queries::parse_media_query_list;
-use style::stylesheets::{Stylesheet, Origin};
+use style::parser::ParserContext as CssParserContext;
+use style::stylesheets::{CssRuleType, Stylesheet, Origin};
 use stylesheet_loader::{StylesheetLoader, StylesheetOwner};
 
 #[dom_struct]
 pub struct HTMLStyleElement {
     htmlelement: HTMLElement,
     #[ignore_heap_size_of = "Arc"]
     stylesheet: DOMRefCell<Option<Arc<Stylesheet>>>,
     cssom_stylesheet: MutNullableJS<CSSStyleSheet>,
@@ -68,29 +69,32 @@ impl HTMLStyleElement {
     }
 
     pub fn parse_own_css(&self) {
         let node = self.upcast::<Node>();
         let element = self.upcast::<Element>();
         assert!(node.is_in_doc());
 
         let win = window_from_node(node);
-        let url = win.get_url();
 
         let mq_attribute = element.get_attribute(&ns!(), &local_name!("media"));
         let mq_str = match mq_attribute {
             Some(a) => String::from(&**a.value()),
             None => String::new(),
         };
 
         let data = node.GetTextContent().expect("Element.textContent must be a string");
-        let mq = parse_media_query_list(&mut CssParser::new(&mq_str));
+        let url = win.get_url();
+        let context = CssParserContext::new_for_cssom(&url,
+                                                      win.css_error_reporter(),
+                                                      Some(CssRuleType::Media));
+        let mq = parse_media_query_list(&context, &mut CssParser::new(&mq_str));
         let shared_lock = node.owner_doc().style_shared_lock().clone();
         let loader = StylesheetLoader::for_element(self.upcast());
-        let sheet = Stylesheet::from_str(&data, url, Origin::Author, mq,
+        let sheet = Stylesheet::from_str(&data, win.get_url(), Origin::Author, mq,
                                          shared_lock, Some(&loader),
                                          win.css_error_reporter());
 
         let sheet = Arc::new(sheet);
 
         // No subresource loads were triggered, just fire the load event now.
         if self.pending_loads.get() == 0 {
             self.upcast::<EventTarget>().fire_event(atom!("load"));
--- a/servo/components/script/dom/medialist.rs
+++ b/servo/components/script/dom/medialist.rs
@@ -2,25 +2,27 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use core::default::Default;
 use cssparser::Parser;
 use dom::bindings::codegen::Bindings::MediaListBinding;
 use dom::bindings::codegen::Bindings::MediaListBinding::MediaListMethods;
 use dom::bindings::js::{JS, Root};
-use dom::bindings::reflector::{Reflector, reflect_dom_object};
+use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object};
 use dom::bindings::str::DOMString;
 use dom::cssstylesheet::CSSStyleSheet;
 use dom::window::Window;
 use dom_struct::dom_struct;
 use std::sync::Arc;
 use style::media_queries::{MediaQuery, parse_media_query_list};
 use style::media_queries::MediaList as StyleMediaList;
+use style::parser::ParserContext;
 use style::shared_lock::{SharedRwLock, Locked};
+use style::stylesheets::CssRuleType;
 use style_traits::ToCss;
 
 #[dom_struct]
 pub struct MediaList {
     reflector_: Reflector,
     parent_stylesheet: JS<CSSStyleSheet>,
     #[ignore_heap_size_of = "Arc"]
     media_queries: Arc<Locked<StyleMediaList>>,
@@ -65,17 +67,21 @@ impl MediaListMethods for MediaList {
         // Step 2
         if value.is_empty() {
             // Step 1
             *media_queries = StyleMediaList::default();
             return;
         }
         // Step 3
         let mut parser = Parser::new(&value);
-        *media_queries = parse_media_query_list(&mut parser);
+        let global = self.global();
+        let win = global.as_window();
+        let url = win.get_url();
+        let context = ParserContext::new_for_cssom(&url, win.css_error_reporter(), Some(CssRuleType::Media));
+        *media_queries = parse_media_query_list(&context, &mut parser);
     }
 
     // https://drafts.csswg.org/cssom/#dom-medialist-length
     fn Length(&self) -> u32 {
         let guard = self.shared_lock().read();
         self.media_queries.read_with(&guard).media_queries.len() as u32
     }
 
@@ -94,17 +100,21 @@ impl MediaListMethods for MediaList {
     fn IndexedGetter(&self, index: u32) -> Option<DOMString> {
         self.Item(index)
     }
 
     // https://drafts.csswg.org/cssom/#dom-medialist-appendmedium
     fn AppendMedium(&self, medium: DOMString) {
         // Step 1
         let mut parser = Parser::new(&medium);
-        let m = MediaQuery::parse(&mut parser);
+        let global = self.global();
+        let win = global.as_window();
+        let url = win.get_url();
+        let context = ParserContext::new_for_cssom(&url, win.css_error_reporter(), Some(CssRuleType::Media));
+        let m = MediaQuery::parse(&context, &mut parser);
         // Step 2
         if let Err(_) = m {
             return;
         }
         // Step 3
         let m_serialized = m.clone().unwrap().to_css_string();
         let mut guard = self.shared_lock().write();
         let mq = self.media_queries.write_with(&mut guard);
@@ -115,17 +125,21 @@ impl MediaListMethods for MediaList {
         // Step 4
         mq.media_queries.push(m.unwrap());
     }
 
     // https://drafts.csswg.org/cssom/#dom-medialist-deletemedium
     fn DeleteMedium(&self, medium: DOMString) {
         // Step 1
         let mut parser = Parser::new(&medium);
-        let m = MediaQuery::parse(&mut parser);
+        let global = self.global();
+        let win = global.as_window();
+        let url = win.get_url();
+        let context = ParserContext::new_for_cssom(&url, win.css_error_reporter(), Some(CssRuleType::Media));
+        let m = MediaQuery::parse(&context, &mut parser);
         // Step 2
         if let Err(_) = m {
             return;
         }
         // Step 3
         let m_serialized = m.unwrap().to_css_string();
         let mut guard = self.shared_lock().write();
         let mut media_list = self.media_queries.write_with(&mut guard);
--- a/servo/components/script/dom/window.rs
+++ b/servo/components/script/dom/window.rs
@@ -98,20 +98,22 @@ use std::mem;
 use std::rc::Rc;
 use std::sync::{Arc, Mutex};
 use std::sync::atomic::{AtomicBool, Ordering};
 use std::sync::mpsc::{Sender, channel};
 use std::sync::mpsc::TryRecvError::{Disconnected, Empty};
 use style::context::ReflowGoal;
 use style::error_reporting::ParseErrorReporter;
 use style::media_queries;
+use style::parser::ParserContext as CssParserContext;
 use style::properties::PropertyId;
 use style::properties::longhands::overflow_x;
 use style::selector_parser::PseudoElement;
 use style::str::HTML_SPACE_CHARACTERS;
+use style::stylesheets::CssRuleType;
 use task_source::dom_manipulation::DOMManipulationTaskSource;
 use task_source::file_reading::FileReadingTaskSource;
 use task_source::history_traversal::HistoryTraversalTaskSource;
 use task_source::networking::NetworkingTaskSource;
 use task_source::user_interaction::UserInteractionTaskSource;
 use time;
 use timers::{IsInterval, TimerCallback};
 #[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
@@ -958,17 +960,19 @@ impl WindowMethods for Window {
             Ok(_) => Ok(()),
             Err(e) => Err(Error::Type(format!("Couldn't open URL: {}", e))),
         }
     }
 
     // https://drafts.csswg.org/cssom-view/#dom-window-matchmedia
     fn MatchMedia(&self, query: DOMString) -> Root<MediaQueryList> {
         let mut parser = Parser::new(&query);
-        let media_query_list = media_queries::parse_media_query_list(&mut parser);
+        let url = self.get_url();
+        let context = CssParserContext::new_for_cssom(&url, self.css_error_reporter(), Some(CssRuleType::Media));
+        let media_query_list = media_queries::parse_media_query_list(&context, &mut parser);
         let document = self.Document();
         let mql = MediaQueryList::new(&document, media_query_list);
         self.media_query_lists.push(&*mql);
         mql
     }
 
     #[allow(unrooted_must_root)]
     // https://fetch.spec.whatwg.org/#fetch-method
--- a/servo/components/style/gecko/media_queries.rs
+++ b/servo/components/style/gecko/media_queries.rs
@@ -9,16 +9,17 @@ use cssparser::{CssStringWriter, Parser,
 use euclid::Size2D;
 use font_metrics::get_metrics_provider_for_product;
 use gecko_bindings::bindings;
 use gecko_bindings::structs::{nsCSSValue, nsCSSUnit, nsStringBuffer};
 use gecko_bindings::structs::{nsMediaExpression_Range, nsMediaFeature};
 use gecko_bindings::structs::{nsMediaFeature_ValueType, nsMediaFeature_RangeType, nsMediaFeature_RequirementFlags};
 use gecko_bindings::structs::RawGeckoPresContextOwned;
 use media_queries::MediaType;
+use parser::ParserContext;
 use properties::ComputedValues;
 use std::ascii::AsciiExt;
 use std::fmt::{self, Write};
 use std::sync::Arc;
 use string_cache::Atom;
 use style_traits::ToCss;
 use style_traits::viewport::ViewportConstraints;
 use values::{CSSFloat, specified};
@@ -361,17 +362,17 @@ impl Expression {
         }
     }
 
     /// Parse a media expression of the form:
     ///
     /// ```
     /// (media-feature: media-value)
     /// ```
-    pub fn parse(input: &mut Parser) -> Result<Self, ()> {
+    pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         try!(input.expect_parenthesis_block());
         input.parse_nested_block(|input| {
             let ident = try!(input.expect_ident());
 
             let mut flags = 0;
             let mut feature_name = &*ident;
 
             // TODO(emilio): this is under a pref in Gecko.
@@ -416,17 +417,17 @@ impl Expression {
                     return Err(())
                 }
                 return Ok(Expression::new(feature, None, range));
             }
 
             let value = match feature.mValueType {
                 nsMediaFeature_ValueType::eLength => {
                     MediaExpressionValue::Length(
-                        specified::Length::parse_non_negative(input)?)
+                        specified::Length::parse_non_negative(context, input)?)
                 },
                 nsMediaFeature_ValueType::eInteger => {
                     let i = input.expect_integer()?;
                     if i < 0 {
                         return Err(())
                     }
                     MediaExpressionValue::Integer(i as u32)
                 }
--- a/servo/components/style/keyframes.rs
+++ b/servo/components/style/keyframes.rs
@@ -123,17 +123,18 @@ impl ToCssWithGuard for Keyframe {
 
 impl Keyframe {
     /// Parse a CSS keyframe.
     pub fn parse(css: &str, parent_stylesheet: &Stylesheet)
                  -> Result<Arc<Locked<Self>>, ()> {
         let error_reporter = MemoryHoleReporter;
         let context = ParserContext::new(parent_stylesheet.origin,
                                          &parent_stylesheet.url_data,
-                                         &error_reporter);
+                                         &error_reporter,
+                                         Some(CssRuleType::Keyframe));
         let mut input = Parser::new(css);
 
         let mut rule_parser = KeyframeListParser {
             context: &context,
             shared_lock: &parent_stylesheet.shared_lock,
         };
         parse_one_rule(&mut input, &mut rule_parser)
     }
@@ -359,29 +360,30 @@ impl<'a> QualifiedRuleParser for Keyfram
                 log_css_error(input, start, &message, self.context);
                 Err(())
             }
         }
     }
 
     fn parse_block(&mut self, prelude: Self::Prelude, input: &mut Parser)
                    -> Result<Self::QualifiedRule, ()> {
+        let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::Keyframe));
         let parser = KeyframeDeclarationParser {
-            context: self.context,
+            context: &context,
         };
         let mut iter = DeclarationListParser::new(input, parser);
         let mut block = PropertyDeclarationBlock::new();
         while let Some(declaration) = iter.next() {
             match declaration {
                 Ok(parsed) => parsed.expand_push_into(&mut block, Importance::Normal),
                 Err(range) => {
                     let pos = range.start;
                     let message = format!("Unsupported keyframe property declaration: '{}'",
                                           iter.input.slice(range));
-                    log_css_error(iter.input, pos, &*message, self.context);
+                    log_css_error(iter.input, pos, &*message, &context);
                 }
             }
             // `parse_important` is not called here, `!important` is not allowed in keyframe blocks.
         }
         Ok(Arc::new(self.shared_lock.wrap(Keyframe {
             selector: prelude,
             block: Arc::new(self.shared_lock.wrap(block)),
         })))
@@ -398,17 +400,17 @@ impl<'a, 'b> AtRuleParser for KeyframeDe
     type AtRule = ParsedDeclaration;
 }
 
 impl<'a, 'b> DeclarationParser for KeyframeDeclarationParser<'a, 'b> {
     type Declaration = ParsedDeclaration;
 
     fn parse_value(&mut self, name: &str, input: &mut Parser) -> Result<ParsedDeclaration, ()> {
         let id = try!(PropertyId::parse(name.into()));
-        match ParsedDeclaration::parse(id, self.context, input, true, CssRuleType::Keyframe) {
+        match ParsedDeclaration::parse(id, self.context, input) {
             Ok(parsed) => {
                 // In case there is still unparsed text in the declaration, we should roll back.
                 if !input.is_exhausted() {
                     Err(())
                 } else {
                     Ok(parsed)
                 }
             }
--- a/servo/components/style/media_queries.rs
+++ b/servo/components/style/media_queries.rs
@@ -3,32 +3,33 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! [Media queries][mq].
 //!
 //! [mq]: https://drafts.csswg.org/mediaqueries/
 
 use Atom;
 use cssparser::{Delimiter, Parser, Token};
+use parser::ParserContext;
 use serialize_comma_separated_list;
 use std::ascii::AsciiExt;
 use std::fmt;
 use style_traits::ToCss;
 
 #[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.
 #[derive(Debug)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub struct MediaList {
     /// The list of media queries.
-    pub media_queries: Vec<MediaQuery>
+    pub media_queries: Vec<MediaQuery>,
 }
 
 impl ToCss for MediaList {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result
         where W: fmt::Write
     {
         serialize_comma_separated_list(dest, &self.media_queries)
     }
@@ -201,17 +202,17 @@ impl MediaType {
             _ => return None
         })
     }
 }
 impl MediaQuery {
     /// Parse a media query given css input.
     ///
     /// Returns an error if any of the expressions is unknown.
-    pub fn parse(input: &mut Parser) -> Result<MediaQuery, ()> {
+    pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<MediaQuery, ()> {
         let mut expressions = vec![];
 
         let qualifier = if input.try(|input| input.expect_ident_matching("only")).is_ok() {
             Some(Qualifier::Only)
         } else if input.try(|input| input.expect_ident_matching("not")).is_ok() {
             Some(Qualifier::Not)
         } else {
             None
@@ -221,46 +222,46 @@ impl MediaQuery {
             Ok(ident) => try!(MediaQueryType::parse(&*ident)),
             Err(()) => {
                 // Media type is only optional if qualifier is not specified.
                 if qualifier.is_some() {
                     return Err(())
                 }
 
                 // Without a media type, require at least one expression.
-                expressions.push(try!(Expression::parse(input)));
+                expressions.push(try!(Expression::parse(context, input)));
 
                 MediaQueryType::All
             }
         };
 
         // Parse any subsequent expressions
         loop {
             if input.try(|input| input.expect_ident_matching("and")).is_err() {
                 return Ok(MediaQuery::new(qualifier, media_type, expressions))
             }
-            expressions.push(try!(Expression::parse(input)))
+            expressions.push(try!(Expression::parse(context, input)))
         }
     }
 }
 
 /// Parse a media query list from CSS.
 ///
 /// Always returns a media query list. If any invalid media query is found, the
 /// media query list is only filled with the equivalent of "not all", see:
 ///
 /// https://drafts.csswg.org/mediaqueries/#error-handling
-pub fn parse_media_query_list(input: &mut Parser) -> MediaList {
+pub fn parse_media_query_list(context: &ParserContext, input: &mut Parser) -> MediaList {
     if input.is_exhausted() {
         return Default::default()
     }
 
     let mut media_queries = vec![];
     loop {
-        match input.parse_until_before(Delimiter::Comma, MediaQuery::parse) {
+        match input.parse_until_before(Delimiter::Comma, |i| MediaQuery::parse(context, i)) {
             Ok(mq) => {
                 media_queries.push(mq);
             },
             Err(..) => {
                 media_queries.push(MediaQuery::never_matching());
             },
         }
 
@@ -302,37 +303,37 @@ impl MediaList {
     pub fn is_empty(&self) -> bool {
         self.media_queries.is_empty()
     }
 
     /// Append a new media query item to the media list.
     /// https://drafts.csswg.org/cssom/#dom-medialist-appendmedium
     ///
     /// Returns true if added, false if fail to parse the medium string.
-    pub fn append_medium(&mut self, new_medium: &str) -> bool {
+    pub fn append_medium(&mut self, context: &ParserContext, new_medium: &str) -> bool {
         let mut parser = Parser::new(new_medium);
-        let new_query = match MediaQuery::parse(&mut parser) {
+        let new_query = match MediaQuery::parse(&context, &mut parser) {
             Ok(query) => query,
             Err(_) => { return false; }
         };
         // This algorithm doesn't actually matches the current spec,
         // but it matches the behavior of Gecko and Edge.
         // See https://github.com/w3c/csswg-drafts/issues/697
         self.media_queries.retain(|query| query != &new_query);
         self.media_queries.push(new_query);
         true
     }
 
     /// Delete a media query from the media list.
     /// https://drafts.csswg.org/cssom/#dom-medialist-deletemedium
     ///
     /// Returns true if found and deleted, false otherwise.
-    pub fn delete_medium(&mut self, old_medium: &str) -> bool {
+    pub fn delete_medium(&mut self, context: &ParserContext, old_medium: &str) -> bool {
         let mut parser = Parser::new(old_medium);
-        let old_query = match MediaQuery::parse(&mut parser) {
+        let old_query = match MediaQuery::parse(context, &mut parser) {
             Ok(query) => query,
             Err(_) => { return false; }
         };
         let old_len = self.media_queries.len();
         self.media_queries.retain(|query| query != &old_query);
         old_len != self.media_queries.len()
     }
 }
--- a/servo/components/style/parser.rs
+++ b/servo/components/style/parser.rs
@@ -4,47 +4,67 @@
 
 //! The context within which CSS code is parsed.
 
 #![deny(missing_docs)]
 
 use cssparser::{Parser, SourcePosition, UnicodeRange};
 use error_reporting::ParseErrorReporter;
 use style_traits::OneOrMoreCommaSeparated;
-use stylesheets::{Origin, UrlExtraData};
+use stylesheets::{CssRuleType, Origin, UrlExtraData};
 
 /// The data that the parser needs from outside in order to parse a stylesheet.
 pub struct ParserContext<'a> {
     /// The `Origin` of the stylesheet, whether it's a user, author or
     /// user-agent stylesheet.
     pub stylesheet_origin: Origin,
     /// The extra data we need for resolving url values.
     pub url_data: &'a UrlExtraData,
     /// An error reporter to report syntax errors.
     pub error_reporter: &'a ParseErrorReporter,
+    /// The current rule type, if any.
+    pub rule_type: Option<CssRuleType>,
 }
 
 impl<'a> ParserContext<'a> {
     /// Create a parser context.
     pub fn new(stylesheet_origin: Origin,
                url_data: &'a UrlExtraData,
-               error_reporter: &'a ParseErrorReporter)
+               error_reporter: &'a ParseErrorReporter,
+               rule_type: Option<CssRuleType>)
                -> ParserContext<'a> {
         ParserContext {
             stylesheet_origin: stylesheet_origin,
             url_data: url_data,
             error_reporter: error_reporter,
+            rule_type: rule_type,
         }
     }
 
     /// Create a parser context for on-the-fly parsing in CSSOM
     pub fn new_for_cssom(url_data: &'a UrlExtraData,
-                         error_reporter: &'a ParseErrorReporter)
+                         error_reporter: &'a ParseErrorReporter,
+                         rule_type: Option<CssRuleType>)
                          -> ParserContext<'a> {
-        Self::new(Origin::Author, url_data, error_reporter)
+        Self::new(Origin::Author, url_data, error_reporter, rule_type)
+    }
+
+    /// Create a parser context based on a previous context, but with a modified rule type.
+    pub fn new_with_rule_type(context: &'a ParserContext,
+                              rule_type: Option<CssRuleType>)
+                              -> ParserContext<'a> {
+        Self::new(context.stylesheet_origin,
+                  context.url_data,
+                  context.error_reporter,
+                  rule_type)
+    }
+
+    /// Get the rule type, which assumes that one is available.
+    pub fn rule_type(&self) -> CssRuleType {
+        self.rule_type.expect("Rule type expected, but none was found.")
     }
 }
 
 /// Defaults to a no-op.
 /// Set a `RUST_LOG=style::errors` environment variable
 /// to log CSS parse errors to stderr.
 pub fn log_css_error(input: &mut Parser,
                      position: SourcePosition,
--- a/servo/components/style/properties/declaration_block.rs
+++ b/servo/components/style/properties/declaration_block.rs
@@ -607,41 +607,40 @@ pub fn append_serialization<'a, W, I, N>
 }
 
 /// A helper to parse the style attribute of an element, in order for this to be
 /// shared between Servo and Gecko.
 pub fn parse_style_attribute(input: &str,
                              url_data: &UrlExtraData,
                              error_reporter: &ParseErrorReporter)
                              -> PropertyDeclarationBlock {
-    let context = ParserContext::new(Origin::Author, url_data, error_reporter);
-    parse_property_declaration_list(&context, &mut Parser::new(input), CssRuleType::Style)
+    let context = ParserContext::new(Origin::Author, url_data, error_reporter, Some(CssRuleType::Style));
+    parse_property_declaration_list(&context, &mut Parser::new(input))
 }
 
 /// Parse a given property declaration. Can result in multiple
 /// `PropertyDeclaration`s when expanding a shorthand, for example.
 ///
 /// The vector returned will not have the importance set;
 /// this does not attempt to parse !important at all
 pub fn parse_one_declaration(id: PropertyId,
                              input: &str,
                              url_data: &UrlExtraData,
                              error_reporter: &ParseErrorReporter)
                              -> Result<ParsedDeclaration, ()> {
-    let context = ParserContext::new(Origin::Author, url_data, error_reporter);
+    let context = ParserContext::new(Origin::Author, url_data, error_reporter, Some(CssRuleType::Style));
     Parser::new(input).parse_entirely(|parser| {
-        ParsedDeclaration::parse(id, &context, parser, false, CssRuleType::Style)
+        ParsedDeclaration::parse(id, &context, parser)
             .map_err(|_| ())
     })
 }
 
 /// A struct to parse property declarations.
 struct PropertyDeclarationParser<'a, 'b: 'a> {
     context: &'a ParserContext<'b>,
-    rule_type: CssRuleType,
 }
 
 
 /// Default methods reject all at rules.
 impl<'a, 'b> AtRuleParser for PropertyDeclarationParser<'a, 'b> {
     type Prelude = ();
     type AtRule = (ParsedDeclaration, Importance);
 }
@@ -649,17 +648,17 @@ impl<'a, 'b> AtRuleParser for PropertyDe
 
 impl<'a, 'b> DeclarationParser for PropertyDeclarationParser<'a, 'b> {
     type Declaration = (ParsedDeclaration, Importance);
 
     fn parse_value(&mut self, name: &str, input: &mut Parser)
                    -> Result<(ParsedDeclaration, Importance), ()> {
         let id = try!(PropertyId::parse(name.into()));
         let parsed = input.parse_until_before(Delimiter::Bang, |input| {
-            ParsedDeclaration::parse(id, self.context, input, false, self.rule_type)
+            ParsedDeclaration::parse(id, self.context, input)
                 .map_err(|_| ())
         })?;
         let importance = match input.try(parse_important) {
             Ok(()) => Importance::Important,
             Err(()) => Importance::Normal,
         };
         // In case there is still unparsed text in the declaration, we should roll back.
         if !input.is_exhausted() {
@@ -668,23 +667,21 @@ impl<'a, 'b> DeclarationParser for Prope
         Ok((parsed, importance))
     }
 }
 
 
 /// Parse a list of property declarations and return a property declaration
 /// block.
 pub fn parse_property_declaration_list(context: &ParserContext,
-                                       input: &mut Parser,
-                                       rule_type: CssRuleType)
+                                       input: &mut Parser)
                                        -> PropertyDeclarationBlock {
     let mut block = PropertyDeclarationBlock::new();
     let parser = PropertyDeclarationParser {
         context: context,
-        rule_type: rule_type,
     };
     let mut iter = DeclarationListParser::new(input, parser);
     while let Some(declaration) = iter.next() {
         match declaration {
             Ok((parsed, importance)) => parsed.expand_push_into(&mut block, importance),
             Err(range) => {
                 let pos = range.start;
                 let message = format!("Unsupported property declaration: '{}'",
--- a/servo/components/style/properties/longhand/background.mako.rs
+++ b/servo/components/style/properties/longhand/background.mako.rs
@@ -477,32 +477,32 @@
     #[inline]
     pub fn get_initial_specified_value() -> SpecifiedValue {
         SpecifiedValue::Explicit(ExplicitSize {
             width: specified::LengthOrPercentageOrAuto::Auto,
             height: specified::LengthOrPercentageOrAuto::Auto,
         })
     }
 
-    pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
+    pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
         if input.try(|input| input.expect_ident_matching("cover")).is_ok() {
             return Ok(SpecifiedValue::Cover);
         }
 
         if input.try(|input| input.expect_ident_matching("contain")).is_ok() {
             return Ok(SpecifiedValue::Contain);
         }
 
         let width =
-            try!(specified::LengthOrPercentageOrAuto::parse_non_negative(input));
+            try!(specified::LengthOrPercentageOrAuto::parse_non_negative(context, input));
 
         let height = if input.is_exhausted() {
             specified::LengthOrPercentageOrAuto::Auto
         } else {
-            try!(specified::LengthOrPercentageOrAuto::parse_non_negative(input))
+            try!(specified::LengthOrPercentageOrAuto::parse_non_negative(context, input))
         };
 
         Ok(SpecifiedValue::Explicit(ExplicitSize {
             width: width,
             height: height,
         }))
     }
 </%helpers:vector_longhand>
--- a/servo/components/style/properties/longhand/border.mako.rs
+++ b/servo/components/style/properties/longhand/border.mako.rs
@@ -520,26 +520,26 @@
             SpecifiedValue(vec![ToComputedValue::from_computed_value(&computed.0),
                                 ToComputedValue::from_computed_value(&computed.1),
                                 ToComputedValue::from_computed_value(&computed.2),
                                 ToComputedValue::from_computed_value(&computed.3)])
         }
     }
 
     impl Parse for SingleSpecifiedValue {
-        fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
+        fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
             if input.try(|input| input.expect_ident_matching("auto")).is_ok() {
                 return Ok(SingleSpecifiedValue::Auto);
             }
 
-            if let Ok(len) = input.try(|input| LengthOrPercentage::parse_non_negative(input)) {
+            if let Ok(len) = input.try(|input| LengthOrPercentage::parse_non_negative(context, input)) {
                 return Ok(SingleSpecifiedValue::LengthOrPercentage(len));
             }
 
-            let num = try!(Number::parse_non_negative(input));
+            let num = try!(Number::parse_non_negative(context, input));
             Ok(SingleSpecifiedValue::Number(num))
         }
     }
 
     pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
         let mut values = vec![];
         for _ in 0..4 {
             let value = input.try(|input| SingleSpecifiedValue::parse(context, input));
--- a/servo/components/style/properties/longhand/box.mako.rs
+++ b/servo/components/style/properties/longhand/box.mako.rs
@@ -553,44 +553,44 @@
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub enum SpecifiedValue {
         CubicBezier(Point2D<Number>, Point2D<Number>),
         Steps(specified::Integer, StartEnd),
         Keyword(FunctionKeyword),
     }
 
     impl Parse for SpecifiedValue {
-        fn parse(_context: &ParserContext, input: &mut ::cssparser::Parser) -> Result<Self, ()> {
+        fn parse(context: &ParserContext, input: &mut ::cssparser::Parser) -> Result<Self, ()> {
             if let Ok(function_name) = input.try(|input| input.expect_function()) {
                 return match_ignore_ascii_case! { &function_name,
                     "cubic-bezier" => {
                         let (mut p1x, mut p1y, mut p2x, mut p2y) =
                             (Number::new(0.0), Number::new(0.0), Number::new(0.0), Number::new(0.0));
                         try!(input.parse_nested_block(|input| {
-                            p1x = try!(specified::parse_number(input));
+                            p1x = try!(specified::parse_number(context, input));
                             try!(input.expect_comma());
-                            p1y = try!(specified::parse_number(input));
+                            p1y = try!(specified::parse_number(context, input));
                             try!(input.expect_comma());
-                            p2x = try!(specified::parse_number(input));
+                            p2x = try!(specified::parse_number(context, input));
                             try!(input.expect_comma());
-                            p2y = try!(specified::parse_number(input));
+                            p2y = try!(specified::parse_number(context, input));
                             Ok(())
                         }));
                         if p1x.value < 0.0 || p1x.value > 1.0 ||
                            p2x.value < 0.0 || p2x.value > 1.0 {
                             return Err(())
                         }
 
                         let (p1, p2) = (Point2D::new(p1x, p1y), Point2D::new(p2x, p2y));
                         Ok(SpecifiedValue::CubicBezier(p1, p2))
                     },
                     "steps" => {
                         let (mut step_count, mut start_end) = (specified::Integer::new(0), StartEnd::End);
                         try!(input.parse_nested_block(|input| {
-                            step_count = try!(specified::parse_integer(input));
+                            step_count = try!(specified::parse_integer(context, input));
                             if step_count.value() < 1 {
                                 return Err(())
                             }
 
                             if input.try(|input| input.expect_comma()).is_ok() {
                                 start_end = try!(match_ignore_ascii_case! {
                                     &try!(input.expect_ident()),
                                     "start" => Ok(StartEnd::Start),
@@ -1052,22 +1052,22 @@
             match *computed {
                 computed_value::T(None) => SpecifiedValue::None,
                 computed_value::T(Some(l)) =>
                     SpecifiedValue::Repeat(ToComputedValue::from_computed_value(&l))
             }
         }
     }
 
-    pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
+    pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
         if input.try(|input| input.expect_ident_matching("none")).is_ok() {
             Ok(SpecifiedValue::None)
         } else if input.try(|input| input.expect_function_matching("repeat")).is_ok() {
             input.parse_nested_block(|input| {
-                LengthOrPercentage::parse_non_negative(input).map(SpecifiedValue::Repeat)
+                LengthOrPercentage::parse_non_negative(context, input).map(SpecifiedValue::Repeat)
             })
         } else {
             Err(())
         }
     }
 </%helpers:longhand>
 
 <%helpers:longhand products="gecko" name="scroll-snap-points-x" animation_type="none"
@@ -1344,17 +1344,17 @@
                 Ok(name) => name,
                 Err(_) => break,
             };
             match_ignore_ascii_case! {
                 &name,
                 "matrix" => {
                     try!(input.parse_nested_block(|input| {
                         let values = try!(input.parse_comma_separated(|input| {
-                            specified::parse_number(input)
+                            specified::parse_number(context, input)
                         }));
                         if values.len() != 6 {
                             return Err(())
                         }
                         result.push(SpecifiedOperation::Matrix {
                             a: values[0],
                             b: values[1],
                             c: values[2],
@@ -1362,17 +1362,17 @@
                             e: values[4],
                             f: values[5]
                         });
                         Ok(())
                     }))
                 },
                 "matrix3d" => {
                     try!(input.parse_nested_block(|input| {
-                        let values = try!(input.parse_comma_separated(specified::parse_number));
+                        let values = try!(input.parse_comma_separated(|i| specified::parse_number(context, i)));
                         if values.len() != 16 {
                             return Err(())
                         }
                         result.push(SpecifiedOperation::Matrix3D {
                             m11: values[ 0], m12: values[ 1], m13: values[ 2], m14: values[ 3],
                             m21: values[ 4], m22: values[ 5], m23: values[ 6], m24: values[ 7],
                             m31: values[ 8], m32: values[ 9], m33: values[10], m34: values[11],
                             m41: values[12], m42: values[13], m43: values[14], m44: values[15]
@@ -1421,54 +1421,54 @@
                         try!(input.expect_comma());
                         let tz = try!(specified::Length::parse(context, input));
                         result.push(SpecifiedOperation::Translate3D(tx, ty, tz));
                         Ok(())
                     }))
                 },
                 "scale" => {
                     try!(input.parse_nested_block(|input| {
-                        let sx = try!(specified::parse_number(input));
+                        let sx = try!(specified::parse_number(context, input));
                         if input.try(|input| input.expect_comma()).is_ok() {
-                            let sy = try!(specified::parse_number(input));
+                            let sy = try!(specified::parse_number(context, input));
                             result.push(SpecifiedOperation::Scale(sx, Some(sy)));
                         } else {
                             result.push(SpecifiedOperation::Scale(sx, None));
                         }
                         Ok(())
                     }))
                 },
                 "scalex" => {
                     try!(input.parse_nested_block(|input| {
-                        let sx = try!(specified::parse_number(input));
+                        let sx = try!(specified::parse_number(context, input));
                         result.push(SpecifiedOperation::ScaleX(sx));
                         Ok(())
                     }))
                 },
                 "scaley" => {
                     try!(input.parse_nested_block(|input| {
-                        let sy = try!(specified::parse_number(input));
+                        let sy = try!(specified::parse_number(context, input));
                         result.push(SpecifiedOperation::ScaleY(sy));
                         Ok(())
                     }))
                 },
                 "scalez" => {
                     try!(input.parse_nested_block(|input| {
-                        let sz = try!(specified::parse_number(input));
+                        let sz = try!(specified::parse_number(context, input));
                         result.push(SpecifiedOperation::ScaleZ(sz));
                         Ok(())
                     }))
                 },
                 "scale3d" => {
                     try!(input.parse_nested_block(|input| {
-                        let sx = try!(specified::parse_number(input));
+                        let sx = try!(specified::parse_number(context, input));
                         try!(input.expect_comma());
-                        let sy = try!(specified::parse_number(input));
+                        let sy = try!(specified::parse_number(context, input));
                         try!(input.expect_comma());
-                        let sz = try!(specified::parse_number(input));
+                        let sz = try!(specified::parse_number(context, input));
                         result.push(SpecifiedOperation::Scale3D(sx, sy, sz));
                         Ok(())
                     }))
                 },
                 "rotate" => {
                     try!(input.parse_nested_block(|input| {
                         let theta = try!(specified::Angle::parse_with_unitless(context,input));
                         result.push(SpecifiedOperation::Rotate(theta));
@@ -1493,21 +1493,21 @@
                     try!(input.parse_nested_block(|input| {
                         let theta = try!(specified::Angle::parse_with_unitless(context,input));
                         result.push(SpecifiedOperation::RotateZ(theta));
                         Ok(())
                     }))
                 },
                 "rotate3d" => {
                     try!(input.parse_nested_block(|input| {
-                        let ax = try!(specified::parse_number(input));
+                        let ax = try!(specified::parse_number(context, input));
                         try!(input.expect_comma());
-                        let ay = try!(specified::parse_number(input));
+                        let ay = try!(specified::parse_number(context, input));
                         try!(input.expect_comma());
-                        let az = try!(specified::parse_number(input));
+                        let az = try!(specified::parse_number(context, input));
                         try!(input.expect_comma());
                         let theta = try!(specified::Angle::parse_with_unitless(context,input));
                         // TODO(gw): Check the axis can be normalized!!
                         result.push(SpecifiedOperation::Rotate3D(ax, ay, az, theta));
                         Ok(())
                     }))
                 },
                 "skew" => {
@@ -1533,17 +1533,17 @@
                     try!(input.parse_nested_block(|input| {
                         let theta_y = try!(specified::Angle::parse_with_unitless(context,input));
                         result.push(SpecifiedOperation::SkewY(theta_y));
                         Ok(())
                     }))
                 },
                 "perspective" => {
                     try!(input.parse_nested_block(|input| {
-                        let d = try!(specified::Length::parse_non_negative(input));
+                        let d = try!(specified::Length::parse_non_negative(context, input));
                         result.push(SpecifiedOperation::Perspective(d));
                         Ok(())
                     }))
                 },
                 _ => return Err(())
             }
         }
 
--- a/servo/components/style/properties/longhand/counters.mako.rs
+++ b/servo/components/style/properties/longhand/counters.mako.rs
@@ -325,49 +325,49 @@
                 try!(dest.write_str(" "));
                 try!(pair.1.to_css(dest));
             }
 
             Ok(())
         }
     }
 
-    pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
-        parse_common(1, input)
+    pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
+        parse_common(context, 1, input)
     }
 
-    pub fn parse_common(default_value: i32, input: &mut Parser) -> Result<SpecifiedValue, ()> {
+    pub fn parse_common(context: &ParserContext, default_value: i32, input: &mut Parser) -> Result<SpecifiedValue, ()> {
         if input.try(|input| input.expect_ident_matching("none")).is_ok() {
             return Ok(SpecifiedValue(Vec::new()))
         }
 
         let mut counters = Vec::new();
         loop {
             let counter_name = match input.next() {
                 Ok(Token::Ident(ident)) => (*ident).to_owned(),
                 Ok(_) => return Err(()),
                 Err(_) => break,
             };
             if content::counter_name_is_illegal(&counter_name) {
                 return Err(())
             }
-            let counter_delta =
-                input.try(|input| specified::parse_integer(input)).unwrap_or(specified::Integer::new(default_value));
+            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(())
         }
     }
 </%helpers:longhand>
 
 <%helpers:longhand name="counter-reset" animation_type="none"
                    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;
 
-    pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
-        parse_common(0, input)
+    pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
+        parse_common(context, 0, input)
     }
 </%helpers:longhand>
--- a/servo/components/style/properties/longhand/effects.mako.rs
+++ b/servo/components/style/properties/longhand/effects.mako.rs
@@ -334,17 +334,17 @@
             % if product == "gecko":
                 if let Ok(url) = input.try(|i| SpecifiedUrl::parse(context, i)) {
                     filters.push(SpecifiedFilter::Url(url));
                 } else
             % endif
             if let Ok(function_name) = input.try(|input| input.expect_function()) {
                 filters.push(try!(input.parse_nested_block(|input| {
                     match_ignore_ascii_case! { &function_name,
-                        "blur" => specified::Length::parse_non_negative(input).map(SpecifiedFilter::Blur),
+                        "blur" => specified::Length::parse_non_negative(context, input).map(SpecifiedFilter::Blur),
                         "brightness" => parse_factor(input).map(SpecifiedFilter::Brightness),
                         "contrast" => parse_factor(input).map(SpecifiedFilter::Contrast),
                         "grayscale" => parse_factor(input).map(SpecifiedFilter::Grayscale),
                         "hue-rotate" => Angle::parse(context, input).map(SpecifiedFilter::HueRotate),
                         "invert" => parse_factor(input).map(SpecifiedFilter::Invert),
                         "opacity" => parse_factor(input).map(SpecifiedFilter::Opacity),
                         "saturate" => parse_factor(input).map(SpecifiedFilter::Saturate),
                         "sepia" => parse_factor(input).map(SpecifiedFilter::Sepia),
--- a/servo/components/style/properties/longhand/font.mako.rs
+++ b/servo/components/style/properties/longhand/font.mako.rs
@@ -676,18 +676,18 @@
         #[inline]
         fn from_computed_value(computed: &computed_value::T) -> Self {
                 SpecifiedValue::Length(LengthOrPercentage::Length(
                         ToComputedValue::from_computed_value(computed)
                 ))
         }
     }
     /// <length> | <percentage> | <absolute-size> | <relative-size>
-    pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
-        if let Ok(lop) = input.try(specified::LengthOrPercentage::parse_non_negative) {
+    pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
+        if let Ok(lop) = input.try(|i| specified::LengthOrPercentage::parse_non_negative(context, i)) {
             return Ok(SpecifiedValue::Length(lop))
         }
 
         if let Ok(kw) = input.try(KeywordSize::parse) {
             return Ok(SpecifiedValue::Keyword(kw, 1.))
         }
 
         match_ignore_ascii_case! {&*input.expect_ident()?,
@@ -783,24 +783,24 @@
     }
 
     #[inline]
     pub fn get_initial_specified_value() -> SpecifiedValue {
         SpecifiedValue::None
     }
 
     /// none | <number>
-    pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
+    pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
         use values::specified::Number;
 
         if input.try(|input| input.expect_ident_matching("none")).is_ok() {
             return Ok(SpecifiedValue::None);
         }
 
-        Ok(SpecifiedValue::Number(try!(Number::parse_non_negative(input))))
+        Ok(SpecifiedValue::Number(try!(Number::parse_non_negative(context, input))))
     }
 </%helpers:longhand>
 
 <%helpers:longhand products="gecko" name="font-synthesis" animation_type="none"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-synthesis">
     use std::fmt;
     use style_traits::ToCss;
     use values::HasViewportPercentage;
--- a/servo/components/style/properties/longhand/inherited_svg.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_svg.mako.rs
@@ -66,30 +66,28 @@
     spec="https://www.w3.org/TR/SVG2/painting.html#SpecifyingStrokePaint")}
 
 ${helpers.predefined_type(
     "stroke-width", "LengthOrPercentage",
     "computed::LengthOrPercentage::one()",
     "parse_numbers_are_pixels_non_negative",
     products="gecko",
     animation_type="normal",
-    needs_context=False,
     spec="https://www.w3.org/TR/SVG2/painting.html#StrokeWidth")}
 
 ${helpers.single_keyword("stroke-linecap", "butt round square",
                          products="gecko", animation_type="none",
                          spec="https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty")}
 
 ${helpers.single_keyword("stroke-linejoin", "miter round bevel",
                          products="gecko", animation_type="none",
                          spec="https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty")}
 
 ${helpers.predefined_type("stroke-miterlimit", "Number", "4.0",
                           "parse_at_least_one", products="gecko",
-                          needs_context=False,
                           animation_type="none",
                           spec="https://www.w3.org/TR/SVG11/painting.html#StrokeMiterlimitProperty")}
 
 ${helpers.predefined_type("stroke-opacity", "Opacity", "1.0",
                           products="gecko", animation_type="none",
                           spec="https://www.w3.org/TR/SVG11/painting.html#StrokeOpacityProperty")}
 
 ${helpers.predefined_type("stroke-dasharray",
@@ -103,17 +101,16 @@
                           spec="https://www.w3.org/TR/SVG2/painting.html#StrokeDashing")}
 
 ${helpers.predefined_type(
     "stroke-dashoffset", "LengthOrPercentage",
     "computed::LengthOrPercentage::zero()",
     "parse_numbers_are_pixels",
     products="gecko",
     animation_type="normal",
-    needs_context=False,
     spec="https://www.w3.org/TR/SVG2/painting.html#StrokeDashing")}
 
 // Section 14 - Clipping, Masking and Compositing
 ${helpers.single_keyword("clip-rule", "nonzero evenodd",
                          products="gecko",
                          gecko_enum_prefix="StyleFillRule",
                          gecko_inexhaustive=True,
                          animation_type="none",
--- a/servo/components/style/properties/longhand/inherited_table.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_table.mako.rs
@@ -109,24 +109,24 @@
         fn from_computed_value(computed: &computed_value::T) -> Self {
             SpecifiedValue {
                 horizontal: ToComputedValue::from_computed_value(&computed.horizontal),
                 vertical: Some(ToComputedValue::from_computed_value(&computed.vertical)),
             }
         }
     }
 
-    pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
+    pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
         let mut first = None;
         let mut second = None;
-        match specified::Length::parse_non_negative(input) {
+        match specified::Length::parse_non_negative(context, input) {
             Err(()) => (),
             Ok(length) => {
                 first = Some(length);
-                if let Ok(len) = input.try(|input| specified::Length::parse_non_negative(input)) {
+                if let Ok(len) = input.try(|input| specified::Length::parse_non_negative(context, input)) {
                     second = Some(len);
                 }
             }
         }
         match (first, second) {
             (None, None) => Err(()),
             (Some(length), None) => {
                 Ok(SpecifiedValue {
--- a/servo/components/style/properties/longhand/inherited_text.mako.rs
+++ b/servo/components/style/properties/longhand/inherited_text.mako.rs
@@ -40,28 +40,28 @@
                     SpecifiedValue::MozBlockHeight => dest.write_str("-moz-block-height"),
                 % endif
                 SpecifiedValue::LengthOrPercentage(ref value) => value.to_css(dest),
                 SpecifiedValue::Number(number) => number.to_css(dest),
             }
         }
     }
     /// normal | <number> | <length> | <percentage>
-    pub fn parse(_: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
+    pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
         use cssparser::Token;
         use std::ascii::AsciiExt;
 
         // We try to parse as a Number first because, for 'line-height', we want
         // "0" to be parsed as a plain Number rather than a Length (0px); this
         // matches the behaviour of all major browsers
-        if let Ok(number) = input.try(specified::Number::parse_non_negative) {
+        if let Ok(number) = input.try(|i| specified::Number::parse_non_negative(context, i)) {
             return Ok(SpecifiedValue::Number(number))
         }
 
-        if let Ok(lop) = input.try(specified::LengthOrPercentage::parse_non_negative) {
+        if let Ok(lop) = input.try(|i| specified::LengthOrPercentage::parse_non_negative(context, i)) {
             return Ok(SpecifiedValue::LengthOrPercentage(lop))
         }
 
 
         match try!(input.next()) {
             Token::Ident(ref value) if value.eq_ignore_ascii_case("normal") => {
                 Ok(SpecifiedValue::Normal)
             }
--- a/servo/components/style/properties/longhand/outline.mako.rs
+++ b/servo/components/style/properties/longhand/outline.mako.rs
@@ -72,18 +72,18 @@
     use values::HasViewportPercentage;
 
     impl ToCss for SpecifiedValue {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             self.0.to_css(dest)
         }
     }
 
-    pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
-        specified::parse_border_width(input).map(SpecifiedValue)
+    pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
+        specified::parse_border_width(context, input).map(SpecifiedValue)
     }
 
     impl HasViewportPercentage for SpecifiedValue {
         fn has_viewport_percentage(&self) -> bool {
             let &SpecifiedValue(ref length) = self;
             length.has_viewport_percentage()
         }
     }
--- a/servo/components/style/properties/longhand/padding.mako.rs
+++ b/servo/components/style/properties/longhand/padding.mako.rs
@@ -11,13 +11,12 @@
         spec = "https://drafts.csswg.org/css-box/#propdef-padding-%s" % side[0]
         if side[1]:
             spec = "https://drafts.csswg.org/css-logical-props/#propdef-padding-%s" % side[1]
     %>
     ${helpers.predefined_type("padding-%s" % side[0], "LengthOrPercentage",
                               "computed::LengthOrPercentage::Length(Au(0))",
                               "parse_non_negative",
                               alias=maybe_moz_logical_alias(product, side, "-moz-padding-%s"),
-                              needs_context=False,
                               animation_type="normal",
                               logical = side[1],
                               spec = spec)}
 % endfor
--- a/servo/components/style/properties/longhand/position.mako.rs
+++ b/servo/components/style/properties/longhand/position.mako.rs
@@ -90,24 +90,22 @@
                               animation_type="none")}
 % endif
 
 // Flex item properties
 ${helpers.predefined_type("flex-grow", "Number",
                           "0.0", "parse_non_negative",
                           spec="https://drafts.csswg.org/css-flexbox/#flex-grow-property",
                           extra_prefixes="webkit",
-                          needs_context=False,
                           animation_type="normal")}
 
 ${helpers.predefined_type("flex-shrink", "Number",
                           "1.0", "parse_non_negative",
                           spec="https://drafts.csswg.org/css-flexbox/#flex-shrink-property",
                           extra_prefixes="webkit",
-                          needs_context=False,
                           animation_type="normal")}
 
 // https://drafts.csswg.org/css-align/#align-self-property
 % if product == "servo":
     // FIXME: Update Servo to support the same syntax as Gecko.
     ${helpers.single_keyword("align-self", "auto stretch flex-start flex-end center baseline",
                              need_clone=True,
                              extra_prefixes="webkit",
@@ -137,33 +135,31 @@
 // FIXME: Gecko doesn't support content value yet.
 // FIXME: This property should be animatable.
 ${helpers.predefined_type("flex-basis",
                           "LengthOrPercentageOrAuto" if product == "gecko" else
                           "LengthOrPercentageOrAutoOrContent",
                           "computed::LengthOrPercentageOrAuto::Auto" if product == "gecko" else
                           "computed::LengthOrPercentageOrAutoOrContent::Auto",
                           "parse_non_negative",
-                          needs_context=False,
                           spec="https://drafts.csswg.org/css-flexbox/#flex-basis-property",
                           extra_prefixes="webkit",
                           animation_type="normal" if product == "gecko" else "none")}
 
 % for (size, logical) in ALL_SIZES:
     <%
       spec = "https://drafts.csswg.org/css-box/#propdef-%s"
       if logical:
         spec = "https://drafts.csswg.org/css-logical-props/#propdef-%s"
     %>
     // width, height, block-size, inline-size
     ${helpers.predefined_type("%s" % size,
                               "LengthOrPercentageOrAuto",
                               "computed::LengthOrPercentageOrAuto::Auto",
                               "parse_non_negative",
-                              needs_context=False,
                               spec=spec % size,
                               animation_type="normal", logical = logical)}
     % if product == "gecko":
         % for min_max in ["min", "max"]:
             <%
                 MinMax = min_max.title()
                 initial = "None" if "max" == min_max else "Auto"
             %>
@@ -250,24 +246,22 @@
             </%helpers:longhand>
         % endfor
     % else:
         // servo versions (no keyword support)
         ${helpers.predefined_type("min-%s" % size,
                                   "LengthOrPercentage",
                                   "computed::LengthOrPercentage::Length(Au(0))",
                                   "parse_non_negative",
-                                  needs_context=False,
                                   spec=spec % ("min-%s" % size),
                                   animation_type="normal", logical = logical)}
         ${helpers.predefined_type("max-%s" % size,
                                   "LengthOrPercentageOrNone",
                                   "computed::LengthOrPercentageOrNone::None",
                                   "parse_non_negative",
-                                  needs_context=False,
                                   spec=spec % ("min-%s" % size),
                                   animation_type="normal", logical = logical)}
     % endif
 % endfor
 
 ${helpers.single_keyword("box-sizing",
                          "content-box border-box",
                          extra_prefixes="moz webkit",
--- a/servo/components/style/properties/longhand/text.mako.rs
+++ b/servo/components/style/properties/longhand/text.mako.rs
@@ -283,17 +283,17 @@
     }
 
     /// normal | <number> <integer>?
     pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
         if input.try(|input| input.expect_ident_matching("normal")).is_ok() {
             return Ok(SpecifiedValue::Normal);
         }
 
-        let size = try!(Number::parse_at_least_one(input));
+        let size = try!(Number::parse_at_least_one(context, input));
 
         match input.try(|input| Integer::parse(context, input)) {
             Ok(number) => {
                 if number.value() < 1 {
                     return Err(());
                 }
                 Ok(SpecifiedValue::Specified(size, Some(number)))
             }
--- a/servo/components/style/properties/longhand/xul.mako.rs
+++ b/servo/components/style/properties/longhand/xul.mako.rs
@@ -20,17 +20,16 @@
                          products="gecko", gecko_ffi_name="mBoxDirection",
                          gecko_enum_prefix="StyleBoxDirection",
                          animation_type="none",
                          alias="-webkit-box-direction",
                          spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-direction)")}
 
 ${helpers.predefined_type("-moz-box-flex", "Number", "0.0", "parse_non_negative",
                           products="gecko", gecko_ffi_name="mBoxFlex",
-                          needs_context=False,
                           animation_type="none",
                           alias="-webkit-box-flex",
                           spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-flex)")}
 
 ${helpers.single_keyword("-moz-box-orient", "horizontal vertical",
                          products="gecko", gecko_ffi_name="mBoxOrient",
                          gecko_enum_prefix="StyleBoxOrient",
                          animation_type="none",
@@ -47,14 +46,13 @@
 ${helpers.single_keyword("-moz-stack-sizing", "stretch-to-fit ignore",
                          products="gecko", gecko_ffi_name="mStretchStack",
                          gecko_constant_prefix="NS_STYLE_STACK_SIZING",
                          animation_type="none",
                          spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-stack-sizing)")}
 
 ${helpers.predefined_type("-moz-box-ordinal-group", "Integer", "0",
                           parse_method="parse_non_negative",
-                          needs_context=False,
                           products="gecko",
                           alias="-webkit-box-ordinal-group",
                           gecko_ffi_name="mBoxOrdinal",
                           animation_type="none",
                           spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-box-ordinal-group)")}
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -325,17 +325,17 @@ impl PropertyDeclarationIdSet {
         {
             f(&
                 ::custom_properties::substitute(css, first_token_type, custom_properties)
                 .and_then(|css| {
                     // As of this writing, only the base URL is used for property values:
                     //
                     // FIXME(pcwalton): Cloning the error reporter is slow! But so are custom
                     // properties, so whatever...
-                    let context = ParserContext::new(Origin::Author, url_data, error_reporter);
+                    let context = ParserContext::new(Origin::Author, url_data, error_reporter, None);
                     Parser::new(&css).parse_entirely(|input| {
                         match from_shorthand {
                             None => {
                                 longhands::${property.ident}
                                          ::parse_specified(&context, input).map(DeclaredValueOwned::Value)
                             }
                             % for shorthand in data.shorthands:
                                 % if property in shorthand.sub_properties:
@@ -971,19 +971,19 @@ impl ParsedDeclaration {
     /// https://drafts.csswg.org/css-animations/#keyframes
     /// > The <declaration-list> inside of <keyframe-block> accepts any CSS property
     /// > except those defined in this specification,
     /// > 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(id: PropertyId, context: &ParserContext, input: &mut Parser,
-                 in_keyframe_block: bool, rule_type: CssRuleType)
+    pub fn parse(id: PropertyId, context: &ParserContext, input: &mut Parser)
                  -> Result<ParsedDeclaration, PropertyDeclarationParseError> {
+        let rule_type = context.rule_type();
         debug_assert!(rule_type == CssRuleType::Keyframe ||
                       rule_type == CssRuleType::Page ||
                       rule_type == CssRuleType::Style,
                       "Declarations are only expected inside a keyframe, page, or style rule.");
         match id {
             PropertyId::Custom(name) => {
                 let value = match input.try(|i| CSSWideKeyword::parse(context, i)) {
                     Ok(keyword) => DeclaredValueOwned::CSSWideKeyword(keyword),
@@ -994,17 +994,17 @@ impl ParsedDeclaration {
                 };
                 Ok(ParsedDeclaration::LonghandOrCustom(PropertyDeclaration::Custom(name, value)))
             }
             PropertyId::Longhand(id) => match id {
             % for property in data.longhands:
                 LonghandId::${property.camel_case} => {
                     % if not property.derived_from:
                         % if not property.allowed_in_keyframe_block:
-                            if in_keyframe_block {
+                            if rule_type == CssRuleType::Keyframe {
                                 return Err(PropertyDeclarationParseError::AnimationPropertyInKeyframeBlock)
                             }
                         % endif
                         % if property.internal:
                             if context.stylesheet_origin != Origin::UserAgent {
                                 return Err(PropertyDeclarationParseError::UnknownProperty)
                             }
                         % endif
@@ -1027,17 +1027,17 @@ impl ParsedDeclaration {
                     % endif
                 }
             % endfor
             },
             PropertyId::Shorthand(id) => match id {
             % for shorthand in data.shorthands:
                 ShorthandId::${shorthand.camel_case} => {
                     % if not shorthand.allowed_in_keyframe_block:
-                        if in_keyframe_block {
+                        if rule_type == CssRuleType::Keyframe {
                             return Err(PropertyDeclarationParseError::AnimationPropertyInKeyframeBlock)
                         }
                     % endif
                     % if shorthand.internal:
                         if context.stylesheet_origin != Origin::UserAgent {
                             return Err(PropertyDeclarationParseError::UnknownProperty)
                         }
                     % endif
--- a/servo/components/style/properties/shorthand/position.mako.rs
+++ b/servo/components/style/properties/shorthand/position.mako.rs
@@ -45,20 +45,20 @@
         }
     }
 </%helpers:shorthand>
 
 <%helpers:shorthand name="flex" sub_properties="flex-grow flex-shrink flex-basis" extra_prefixes="webkit"
                     spec="https://drafts.csswg.org/css-flexbox/#flex-property">
     use values::specified::Number;
 
-    fn parse_flexibility(input: &mut Parser)
+    fn parse_flexibility(context: &ParserContext, input: &mut Parser)
                          -> Result<(Number, Option<Number>),()> {
-        let grow = try!(Number::parse_non_negative(input));
-        let shrink = input.try(Number::parse_non_negative).ok();
+        let grow = try!(Number::parse_non_negative(context, input));
+        let shrink = input.try(|i| Number::parse_non_negative(context, i)).ok();
         Ok((grow, shrink))
     }
 
     pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
         let mut grow = None;
         let mut shrink = None;
         let mut basis = None;
 
@@ -66,17 +66,17 @@
             return Ok(Longhands {
                 flex_grow: Number::new(0.0),
                 flex_shrink: Number::new(0.0),
                 flex_basis: longhands::flex_basis::SpecifiedValue::auto(),
             })
         }
         loop {
             if grow.is_none() {
-                if let Ok((flex_grow, flex_shrink)) = input.try(parse_flexibility) {
+                if let Ok((flex_grow, flex_shrink)) = input.try(|i| parse_flexibility(context, i)) {
                     grow = Some(flex_grow);
                     shrink = flex_shrink;
                     continue
                 }
             }
             if basis.is_none() {
                 if let Ok(value) = input.try(|input| longhands::flex_basis::parse(context, input)) {
                     basis = Some(value);
--- a/servo/components/style/servo/media_queries.rs
+++ b/servo/components/style/servo/media_queries.rs
@@ -4,16 +4,17 @@
 
 //! Servo's media-query device and expression representation.
 
 use app_units::Au;
 use cssparser::Parser;
 use euclid::{Size2D, TypedSize2D};
 use font_metrics::ServoMetricsProvider;
 use media_queries::MediaType;
+use parser::ParserContext;
 use properties::ComputedValues;
 use std::fmt;
 use style_traits::{CSSPixel, ToCss};
 use style_traits::viewport::ViewportConstraints;
 use values::computed::{self, ToComputedValue};
 use values::specified;
 
 /// A device is a structure that represents the current media a given document
@@ -99,31 +100,31 @@ impl Expression {
 
     /// Parse a media expression of the form:
     ///
     /// ```
     /// (media-feature: media-value)
     /// ```
     ///
     /// Only supports width and width ranges for now.
-    pub fn parse(input: &mut Parser) -> Result<Self, ()> {
+    pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         try!(input.expect_parenthesis_block());
         input.parse_nested_block(|input| {
             let name = try!(input.expect_ident());
             try!(input.expect_colon());
             // TODO: Handle other media features
             Ok(Expression(match_ignore_ascii_case! { &name,
                 "min-width" => {
-                    ExpressionKind::Width(Range::Min(try!(specified::Length::parse_non_negative(input))))
+                    ExpressionKind::Width(Range::Min(try!(specified::Length::parse_non_negative(context, input))))
                 },
                 "max-width" => {
-                    ExpressionKind::Width(Range::Max(try!(specified::Length::parse_non_negative(input))))
+                    ExpressionKind::Width(Range::Max(try!(specified::Length::parse_non_negative(context, input))))
                 },
                 "width" => {
-                    ExpressionKind::Width(Range::Eq(try!(specified::Length::parse_non_negative(input))))
+                    ExpressionKind::Width(Range::Eq(try!(specified::Length::parse_non_negative(context, input))))
                 },
                 _ => return Err(())
             }))
         })
     }
 
     /// Evaluate this expression and return whether it matches the current
     /// device.
--- a/servo/components/style/stylesheets.rs
+++ b/servo/components/style/stylesheets.rs
@@ -382,17 +382,18 @@ impl CssRule {
                  parent_stylesheet: &Stylesheet,
                  state: Option<State>,
                  loader: Option<&StylesheetLoader>)
                  -> Result<(Self, State), SingleRuleParseError> {
         let error_reporter = MemoryHoleReporter;
         let mut namespaces = parent_stylesheet.namespaces.write();
         let context = ParserContext::new(parent_stylesheet.origin,
                                          &parent_stylesheet.url_data,
-                                         &error_reporter);
+                                         &error_reporter,
+                                         None);
         let mut input = Parser::new(css);
 
         // nested rules are in the body state
         let state = state.unwrap_or(State::Body);
         let mut rule_parser = TopLevelRuleParser {
             stylesheet_origin: parent_stylesheet.origin,
             context: context,
             shared_lock: &parent_stylesheet.shared_lock,
@@ -653,17 +654,17 @@ impl Stylesheet {
                    -> (Vec<CssRule>, bool) {
         let mut rules = Vec::new();
         let mut input = Parser::new(css);
         let rule_parser = TopLevelRuleParser {
             stylesheet_origin: origin,
             namespaces: namespaces,
             shared_lock: shared_lock,
             loader: stylesheet_loader,
-            context: ParserContext::new(origin, url_data, error_reporter),
+            context: ParserContext::new(origin, url_data, error_reporter, None),
             state: Cell::new(State::Start),
         };
 
         input.look_for_viewport_percentages();
 
         {
             let mut iter = RuleListParser::new_for_stylesheet(&mut input, rule_parser);
             while let Some(result) = iter.next() {
@@ -899,17 +900,17 @@ impl<'a> AtRuleParser for TopLevelRulePa
                      -> Result<AtRuleType<AtRulePrelude, CssRule>, ()> {
         match_ignore_ascii_case! { name,
             "import" => {
                 if self.state.get() <= State::Imports {
                     self.state.set(State::Imports);
                     let url_string = input.expect_url_or_string()?;
                     let specified_url = SpecifiedUrl::parse_from_string(url_string, &self.context)?;
 
-                    let media = parse_media_query_list(input);
+                    let media = parse_media_query_list(&self.context, input);
 
                     let noop_loader = NoOpLoader;
                     let loader = if !specified_url.is_invalid() {
                         self.loader.expect("Expected a stylesheet loader for @import")
                     } else {
                         &noop_loader
                     };
 
@@ -1006,18 +1007,25 @@ impl<'a> QualifiedRuleParser for TopLeve
 struct NestedRuleParser<'a, 'b: 'a> {
     stylesheet_origin: Origin,
     shared_lock: &'a SharedRwLock,
     context: &'a ParserContext<'b>,
     namespaces: &'b Namespaces,
 }
 
 impl<'a, 'b> NestedRuleParser<'a, 'b> {
-    fn parse_nested_rules(&self, input: &mut Parser) -> Arc<Locked<CssRules>> {
-        let mut iter = RuleListParser::new_for_nested_rule(input, self.clone());
+    fn parse_nested_rules(&self, input: &mut Parser, rule_type: CssRuleType) -> Arc<Locked<CssRules>> {
+        let context = ParserContext::new_with_rule_type(self.context, Some(rule_type));
+        let nested_parser = NestedRuleParser {
+            stylesheet_origin: self.stylesheet_origin,
+            shared_lock: self.shared_lock,
+            context: &context,
+            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(range) => {
                     let pos = range.start;
                     let message = format!("Unsupported rule: '{}'", iter.input.slice(range));
                     log_css_error(iter.input, pos, &*message, self.context);
@@ -1041,17 +1049,17 @@ fn is_viewport_enabled() -> bool {
 impl<'a, 'b> AtRuleParser for NestedRuleParser<'a, 'b> {
     type Prelude = AtRulePrelude;
     type AtRule = CssRule;
 
     fn parse_prelude(&mut self, name: &str, input: &mut Parser)
                      -> Result<AtRuleType<AtRulePrelude, CssRule>, ()> {
         match_ignore_ascii_case! { name,
             "media" => {
-                let media_queries = parse_media_query_list(input);
+                let media_queries = parse_media_query_list(self.context, input);
                 let arc = Arc::new(self.shared_lock.wrap(media_queries));
                 Ok(AtRuleType::WithBlock(AtRulePrelude::Media(arc)))
             },
             "supports" => {
                 let cond = SupportsCondition::parse(input)?;
                 Ok(AtRuleType::WithBlock(AtRulePrelude::Supports(cond)))
             },
             "font-face" => {
@@ -1082,45 +1090,49 @@ impl<'a, 'b> AtRuleParser for NestedRule
             },
             _ => Err(())
         }
     }
 
     fn parse_block(&mut self, prelude: AtRulePrelude, input: &mut Parser) -> Result<CssRule, ()> {
         match prelude {
             AtRulePrelude::FontFace => {
+                let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::FontFace));
                 Ok(CssRule::FontFace(Arc::new(self.shared_lock.wrap(
-                    parse_font_face_block(self.context, input).into()))))
+                   parse_font_face_block(&context, input).into()))))
             }
             AtRulePrelude::Media(media_queries) => {
                 Ok(CssRule::Media(Arc::new(self.shared_lock.wrap(MediaRule {
                     media_queries: media_queries,
-                    rules: self.parse_nested_rules(input),
+                    rules: self.parse_nested_rules(input, CssRuleType::Media),
                 }))))
             }
             AtRulePrelude::Supports(cond) => {
                 let enabled = cond.eval(self.context);
                 Ok(CssRule::Supports(Arc::new(self.shared_lock.wrap(SupportsRule {
                     condition: cond,
-                    rules: self.parse_nested_rules(input),
+                    rules: self.parse_nested_rules(input, CssRuleType::Supports),
                     enabled: enabled,
                 }))))
             }
             AtRulePrelude::Viewport => {
+                let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::Viewport));
                 Ok(CssRule::Viewport(Arc::new(self.shared_lock.wrap(
-                    try!(ViewportRule::parse(self.context, input))))))
+                   try!(ViewportRule::parse(&context, input))))))
             }
             AtRulePrelude::Keyframes(name) => {
+                let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::Keyframes));
                 Ok(CssRule::Keyframes(Arc::new(self.shared_lock.wrap(KeyframesRule {
                     name: name,
-                    keyframes: parse_keyframe_list(&self.context, input, self.shared_lock),
+                    keyframes: parse_keyframe_list(&context, input, self.shared_lock),
                 }))))
             }
             AtRulePrelude::Page => {
-                let declarations = parse_property_declaration_list(self.context, input, CssRuleType::Page);
+                let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::Page));
+                let declarations = parse_property_declaration_list(&context, input);
                 Ok(CssRule::Page(Arc::new(self.shared_lock.wrap(PageRule(
                     Arc::new(self.shared_lock.wrap(declarations))
                 )))))
             }
         }
     }
 }
 
@@ -1133,15 +1145,16 @@ impl<'a, 'b> QualifiedRuleParser for Nes
             stylesheet_origin: self.stylesheet_origin,
             namespaces: self.namespaces,
         };
         SelectorList::parse(&selector_parser, input)
     }
 
     fn parse_block(&mut self, prelude: SelectorList<SelectorImpl>, input: &mut Parser)
                    -> Result<CssRule, ()> {
-        let declarations = parse_property_declaration_list(self.context, input, CssRuleType::Style);
+        let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::Style));
+        let declarations = parse_property_declaration_list(&context, input);
         Ok(CssRule::Style(Arc::new(self.shared_lock.wrap(StyleRule {
             selectors: prelude,
             block: Arc::new(self.shared_lock.wrap(declarations))
         }))))
     }
 }
--- a/servo/components/style/supports.rs
+++ b/servo/components/style/supports.rs
@@ -207,13 +207,14 @@ impl Declaration {
     /// https://drafts.csswg.org/css-conditional-3/#support-definition
     pub fn eval(&self, cx: &ParserContext) -> bool {
         let id = if let Ok(id) = PropertyId::parse((&*self.prop).into()) {
             id
         } else {
             return false
         };
         let mut input = Parser::new(&self.val);
-        let res = ParsedDeclaration::parse(id, cx, &mut input, /* in_keyframe */ false, CssRuleType::Style);
+        let context = ParserContext::new_with_rule_type(cx, Some(CssRuleType::Style));
+        let res = ParsedDeclaration::parse(id, &context, &mut input);
         let _ = input.try(parse_important);
         res.is_ok() && input.is_exhausted()
     }
 }
--- a/servo/components/style/values/specified/basic_shape.rs
+++ b/servo/components/style/values/specified/basic_shape.rs
@@ -657,18 +657,18 @@ impl ShapeRadius {
 
 impl Default for ShapeRadius {
     fn default() -> Self {
         ShapeRadius::ClosestSide
     }
 }
 
 impl Parse for ShapeRadius {
-    fn parse(_: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
-        input.try(|i| LengthOrPercentage::parse_non_negative(i)).map(ShapeRadius::Length).or_else(|_| {
+    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
+        input.try(|i| LengthOrPercentage::parse_non_negative(context, i)).map(ShapeRadius::Length).or_else(|_| {
             match_ignore_ascii_case! { &try!(input.expect_ident()),
                 "closest-side" => Ok(ShapeRadius::ClosestSide),
                 "farthest-side" => Ok(ShapeRadius::FarthestSide),
                 _ => Err(())
             }
         })
     }
 }
@@ -744,51 +744,51 @@ impl ToCss for BorderRadius {
                                  &self.top_right.0.height,
                                  &self.bottom_right.0.height,
                                  &self.bottom_left.0.height)
         }
     }
 }
 
 impl Parse for BorderRadius {
-    fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
-        let mut widths = try!(parse_one_set_of_border_values(input));
+    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
+        let mut widths = try!(parse_one_set_of_border_values(context, input));
         let mut heights = if input.try(|input| input.expect_delim('/')).is_ok() {
-            try!(parse_one_set_of_border_values(input))
+            try!(parse_one_set_of_border_values(context, input))
         } else {
             [widths[0].clone(),
              widths[1].clone(),
              widths[2].clone(),
              widths[3].clone()]
         };
         Ok(BorderRadius {
             top_left: BorderRadiusSize::new(widths[0].take(), heights[0].take()),
             top_right: BorderRadiusSize::new(widths[1].take(), heights[1].take()),
             bottom_right: BorderRadiusSize::new(widths[2].take(), heights[2].take()),
             bottom_left: BorderRadiusSize::new(widths[3].take(), heights[3].take()),
         })
     }
 }
 
-fn parse_one_set_of_border_values(mut input: &mut Parser)
+fn parse_one_set_of_border_values(context: &ParserContext, mut input: &mut Parser)
                                  -> Result<[LengthOrPercentage; 4], ()> {
-    let a = try!(LengthOrPercentage::parse_non_negative(input));
-    let b = if let Ok(b) = input.try(|i| LengthOrPercentage::parse_non_negative(i)) {
+    let a = try!(LengthOrPercentage::parse_non_negative(context, input));
+    let b = if let Ok(b) = input.try(|i| LengthOrPercentage::parse_non_negative(context, i)) {
         b
     } else {
         return Ok([a.clone(), a.clone(), a.clone(), a])
     };
 
-    let c = if let Ok(c) = input.try(|i| LengthOrPercentage::parse_non_negative(i)) {
+    let c = if let Ok(c) = input.try(|i| LengthOrPercentage::parse_non_negative(context, i)) {
         c
     } else {
         return Ok([a.clone(), b.clone(), a, b])
     };
 
-    if let Ok(d) = input.try(|i| LengthOrPercentage::parse_non_negative(i)) {
+    if let Ok(d) = input.try(|i| LengthOrPercentage::parse_non_negative(context, i)) {
         Ok([a, b, c, d])
     } else {
         Ok([a, b.clone(), c, b])
     }
 }
 
 
 impl ToComputedValue for BorderRadius {
--- a/servo/components/style/values/specified/grid.rs
+++ b/servo/components/style/values/specified/grid.rs
@@ -133,18 +133,18 @@ pub fn parse_flex(input: &mut Parser) ->
     match try!(input.next()) {
         Token::Dimension(ref value, ref unit) if unit.eq_ignore_ascii_case("fr") && value.value.is_sign_positive()
             => Ok(value.value),
         _ => Err(()),
     }
 }
 
 impl Parse for TrackBreadth<LengthOrPercentage> {
-    fn parse(_: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
-        if let Ok(lop) = input.try(LengthOrPercentage::parse_non_negative) {
+    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
+        if let Ok(lop) = input.try(|i| LengthOrPercentage::parse_non_negative(context, i)) {
             return Ok(TrackBreadth::Breadth(lop))
         }
 
         if let Ok(f) = input.try(parse_flex) {
             return Ok(TrackBreadth::Flex(f))
         }
 
         TrackKeyword::parse(input).map(TrackBreadth::Keyword)
@@ -225,17 +225,17 @@ impl Parse for TrackSize<LengthOrPercent
     fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         if let Ok(b) = input.try(|i| TrackBreadth::parse(context, i)) {
             return Ok(TrackSize::Breadth(b))
         }
 
         if input.try(|i| i.expect_function_matching("minmax")).is_ok() {
             return input.parse_nested_block(|input| {
                 let inflexible_breadth =
-                    match input.try(LengthOrPercentage::parse_non_negative) {
+                    match input.try(|i| LengthOrPercentage::parse_non_negative(context, i)) {
                         Ok(lop) => TrackBreadth::Breadth(lop),
                         Err(..) => {
                             let keyword = try!(TrackKeyword::parse(input));
                             TrackBreadth::Keyword(keyword)
                         }
                     };
 
                 try!(input.expect_comma());
--- a/servo/components/style/values/specified/length.rs
+++ b/servo/components/style/values/specified/length.rs
@@ -11,16 +11,17 @@ use cssparser::{Parser, Token};
 use euclid::size::Size2D;
 use font_metrics::FontMetricsQueryResult;
 use parser::{Parse, ParserContext};
 use std::{cmp, fmt, mem};
 use std::ascii::AsciiExt;
 use std::ops::Mul;
 use style_traits::ToCss;
 use style_traits::values::specified::AllowedNumericType;
+use stylesheets::CssRuleType;
 use super::{Angle, Number, SimplifiedValueNode, SimplifiedSumNode, Time, ToComputedValue};
 use values::{Auto, CSSFloat, Either, FONT_MEDIUM_PX, HasViewportPercentage, None_, Normal};
 use values::ExtremumLength;
 use values::computed::{ComputedValueAsSpecified, Context};
 
 pub use super::image::{AngleOrCorner, ColorStop, EndingShape as GradientEndingShape, Gradient};
 pub use super::image::{GradientKind, HorizontalDirection, Image, LengthOrKeyword, LengthOrPercentageOrKeyword};
 pub use super::image::{SizeKeyword, VerticalDirection};
@@ -368,35 +369,56 @@ impl Mul<CSSFloat> for NoCalcLength {
             NoCalcLength::ViewportPercentage(v) => NoCalcLength::ViewportPercentage(v * scalar),
             NoCalcLength::ServoCharacterWidth(_) => panic!("Can't multiply ServoCharacterWidth!"),
         }
     }
 }
 
 impl NoCalcLength {
     /// Parse a given absolute or relative dimension.
-    pub fn parse_dimension(value: CSSFloat, unit: &str) -> Result<NoCalcLength, ()> {
+    pub fn parse_dimension(context: &ParserContext, value: CSSFloat, unit: &str) -> Result<NoCalcLength, ()> {
+        let in_page_rule = context.rule_type.map_or(false, |rule_type| rule_type == CssRuleType::Page);
         match_ignore_ascii_case! { unit,
             "px" => Ok(NoCalcLength::Absolute(AbsoluteLength::Px(value))),
             "in" => Ok(NoCalcLength::Absolute(AbsoluteLength::In(value))),
             "cm" => Ok(NoCalcLength::Absolute(AbsoluteLength::Cm(value))),
             "mm" => Ok(NoCalcLength::Absolute(AbsoluteLength::Mm(value))),
             "q" => Ok(NoCalcLength::Absolute(AbsoluteLength::Q(value))),
             "pt" => Ok(NoCalcLength::Absolute(AbsoluteLength::Pt(value))),
             "pc" => Ok(NoCalcLength::Absolute(AbsoluteLength::Pc(value))),
             // font-relative
             "em" => Ok(NoCalcLength::FontRelative(FontRelativeLength::Em(value))),
             "ex" => Ok(NoCalcLength::FontRelative(FontRelativeLength::Ex(value))),
             "ch" => Ok(NoCalcLength::FontRelative(FontRelativeLength::Ch(value))),
             "rem" => Ok(NoCalcLength::FontRelative(FontRelativeLength::Rem(value))),
             // viewport percentages
-            "vw" => Ok(NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vw(value))),
-            "vh" => Ok(NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vh(value))),
-            "vmin" => Ok(NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vmin(value))),
-            "vmax" => Ok(NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vmax(value))),
+            "vw" => {
+                if in_page_rule {
+                    return Err(())
+                }
+                Ok(NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vw(value)))
+            },
+            "vh" => {
+                if in_page_rule {
+                    return Err(())
+                }
+                Ok(NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vh(value)))
+            },
+            "vmin" => {
+                if in_page_rule {
+                    return Err(())
+                }
+                Ok(NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vmin(value)))
+            },
+            "vmax" => {
+                if in_page_rule {
+                    return Err(())
+                }
+                Ok(NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vmax(value)))
+            },
             _ => Err(())
         }
     }
 
     #[inline]
     /// Returns a `zero` length.
     pub fn zero() -> NoCalcLength {
         NoCalcLength::Absolute(AbsoluteLength::Px(0.))
@@ -507,38 +529,39 @@ impl Mul<CSSFloat> for ViewportPercentag
 impl Length {
     #[inline]
     /// Returns a `zero` length.
     pub fn zero() -> Length {
         Length::NoCalc(NoCalcLength::zero())
     }
 
     /// Parse a given absolute or relative dimension.
-    pub fn parse_dimension(value: CSSFloat, unit: &str) -> Result<Length, ()> {
-        NoCalcLength::parse_dimension(value, unit).map(Length::NoCalc)
+    pub fn parse_dimension(context: &ParserContext, value: CSSFloat, unit: &str) -> Result<Length, ()> {
+        NoCalcLength::parse_dimension(context, value, unit).map(Length::NoCalc)
     }
 
     #[inline]
-    fn parse_internal(input: &mut Parser, context: AllowedNumericType) -> Result<Length, ()> {
+    fn parse_internal(context: &ParserContext, input: &mut Parser, num_context: AllowedNumericType)
+                      -> Result<Length, ()> {
         match try!(input.next()) {
-            Token::Dimension(ref value, ref unit) if context.is_ok(value.value) =>
-                Length::parse_dimension(value.value, unit),
+            Token::Dimension(ref value, ref unit) if num_context.is_ok(value.value) =>
+                Length::parse_dimension(context, value.value, unit),
             Token::Number(ref value) if value.value == 0. => Ok(Length::zero()),
             Token::Function(ref name) if name.eq_ignore_ascii_case("calc") =>
                 input.parse_nested_block(|input| {
-                    CalcLengthOrPercentage::parse_length(input, context)
+                    CalcLengthOrPercentage::parse_length(context, input, num_context)
                 }),
             _ => Err(())
         }
     }
 
     /// Parse a non-negative length
     #[inline]
-    pub fn parse_non_negative(input: &mut Parser) -> Result<Length, ()> {
-        Self::parse_internal(input, AllowedNumericType::NonNegative)
+    pub fn parse_non_negative(context: &ParserContext, input: &mut Parser) -> Result<Length, ()> {
+        Self::parse_internal(context, input, AllowedNumericType::NonNegative)
     }
 
     /// Get an absolute length from a px value.
     #[inline]
     pub fn from_px(px_value: CSSFloat) -> Length {
         Length::NoCalc(NoCalcLength::from_px(px_value))
     }
 
@@ -547,51 +570,51 @@ impl Length {
     /// Use when you need to move out of a length array without cloning
     #[inline]
     pub fn take(&mut self) -> Self {
         mem::replace(self, Length::zero())
     }
 }
 
 impl Parse for Length {
-    fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
-        Self::parse_internal(input, AllowedNumericType::All)
+    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
+        Self::parse_internal(context, input, AllowedNumericType::All)
     }
 }
 
 impl Either<Length, None_> {
     /// Parse a non-negative length or none
     #[inline]
     pub fn parse_non_negative_length(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         if input.try(|input| None_::parse(context, input)).is_ok() {
             return Ok(Either::Second(None_));
         }
-        Length::parse_non_negative(input).map(Either::First)
+        Length::parse_non_negative(context, input).map(Either::First)
     }
 }
 
 impl Either<Length, Normal> {
     #[inline]
     #[allow(missing_docs)]
     pub fn parse_non_negative_length(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         if input.try(|input| Normal::parse(context, input)).is_ok() {
             return Ok(Either::Second(Normal));
         }
-        Length::parse_internal(input, AllowedNumericType::NonNegative).map(Either::First)
+        Length::parse_internal(context, input, AllowedNumericType::NonNegative).map(Either::First)
     }
 }
 
 impl Either<Length, Auto> {
     #[inline]
     #[allow(missing_docs)]
     pub fn parse_non_negative_length(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         if input.try(|input| Auto::parse(context, input)).is_ok() {
             return Ok(Either::Second(Auto));
         }
-        Length::parse_internal(input, AllowedNumericType::NonNegative).map(Either::First)
+        Length::parse_internal(context, input, AllowedNumericType::NonNegative).map(Either::First)
     }
 }
 
 /// A calc sum expression node.
 #[derive(Clone, Debug)]
 pub struct CalcSumNode {
     /// The products of this node.
     pub products: Vec<CalcProductNode>,
@@ -640,33 +663,33 @@ pub struct CalcLengthOrPercentage {
     pub ex: Option<CSSFloat>,
     pub ch: Option<CSSFloat>,
     pub rem: Option<CSSFloat>,
     pub percentage: Option<CSSFloat>,
 }
 
 impl CalcLengthOrPercentage {
     /// Parse a calc sum node.
-    pub fn parse_sum(input: &mut Parser, expected_unit: CalcUnit) -> Result<CalcSumNode, ()> {
+    pub fn parse_sum(context: &ParserContext, input: &mut Parser, expected_unit: CalcUnit) -> Result<CalcSumNode, ()> {
         let mut products = Vec::new();
-        products.push(try!(CalcLengthOrPercentage::parse_product(input, expected_unit)));
+        products.push(try!(CalcLengthOrPercentage::parse_product(context, input, expected_unit)));
 
         loop {
             let position = input.position();
             match input.next_including_whitespace() {
                 Ok(Token::WhiteSpace(_)) => {
                     if input.is_exhausted() {
                         break; // allow trailing whitespace
                     }
                     match input.next() {
                         Ok(Token::Delim('+')) => {
-                            products.push(try!(CalcLengthOrPercentage::parse_product(input, expected_unit)));
+                            products.push(try!(CalcLengthOrPercentage::parse_product(context, input, expected_unit)));
                         }
                         Ok(Token::Delim('-')) => {
-                            let mut right = try!(CalcLengthOrPercentage::parse_product(input, expected_unit));
+                            let mut right = try!(CalcLengthOrPercentage::parse_product(context, input, expected_unit));
                             right.values.push(CalcValueNode::Number(-1.));
                             products.push(right);
                         }
                         _ => {
                             return Err(());
                         }
                     }
                 }
@@ -674,25 +697,26 @@ impl CalcLengthOrPercentage {
                     input.reset(position);
                     break
                 }
             }
         }
         Ok(CalcSumNode { products: products })
     }
 
-    fn parse_product(input: &mut Parser, expected_unit: CalcUnit) -> Result<CalcProductNode, ()> {
+    fn parse_product(context: &ParserContext, input: &mut Parser, expected_unit: CalcUnit)
+                     -> Result<CalcProductNode, ()> {
         let mut values = Vec::new();
-        values.push(try!(CalcLengthOrPercentage::parse_value(input, expected_unit)));
+        values.push(try!(CalcLengthOrPercentage::parse_value(context, input, expected_unit)));
 
         loop {
             let position = input.position();
             match input.next() {
                 Ok(Token::Delim('*')) => {
-                    values.push(try!(CalcLengthOrPercentage::parse_value(input, expected_unit)));
+                    values.push(try!(CalcLengthOrPercentage::parse_value(context, input, expected_unit)));
                 }
                 Ok(Token::Delim('/')) if expected_unit != CalcUnit::Integer => {
                     if let Ok(Token::Number(ref value)) = input.next() {
                         if value.value == 0. {
                             return Err(());
                         }
                         values.push(CalcValueNode::Number(1. / value.value));
                     } else {
@@ -704,37 +728,37 @@ impl CalcLengthOrPercentage {
                     break
                 }
             }
         }
 
         Ok(CalcProductNode { values: values })
     }
 
-    fn parse_value(input: &mut Parser, expected_unit: CalcUnit) -> Result<CalcValueNode, ()> {
+    fn parse_value(context: &ParserContext, input: &mut Parser, expected_unit: CalcUnit) -> Result<CalcValueNode, ()> {
         match (try!(input.next()), expected_unit) {
             (Token::Number(ref value), _) => Ok(CalcValueNode::Number(value.value)),
             (Token::Dimension(ref value, ref unit), CalcUnit::Length) |
             (Token::Dimension(ref value, ref unit), CalcUnit::LengthOrPercentage) => {
-                NoCalcLength::parse_dimension(value.value, unit).map(CalcValueNode::Length)
+                NoCalcLength::parse_dimension(context, value.value, unit).map(CalcValueNode::Length)
             }
             (Token::Dimension(ref value, ref unit), CalcUnit::Angle) => {
                 Angle::parse_dimension(value.value, unit).map(|angle| {
                     CalcValueNode::Angle(angle.radians())
                 })
             }
             (Token::Dimension(ref value, ref unit), CalcUnit::Time) => {
                 Time::parse_dimension(value.value, unit).map(|time| {
                     CalcValueNode::Time(time.seconds())
                 })
             }
             (Token::Percentage(ref value), CalcUnit::LengthOrPercentage) =>
                 Ok(CalcValueNode::Percentage(value.unit_value)),
             (Token::ParenthesisBlock, _) => {
-                input.parse_nested_block(|i| CalcLengthOrPercentage::parse_sum(i, expected_unit))
+                input.parse_nested_block(|i| CalcLengthOrPercentage::parse_sum(context, i, expected_unit))
                      .map(|result| CalcValueNode::Sum(Box::new(result)))
             },
             _ => Err(())
         }
     }
 
     fn simplify_value_to_number(node: &CalcValueNode) -> Option<CSSFloat> {
         match *node {
@@ -805,31 +829,33 @@ impl CalcLengthOrPercentage {
         }
 
         match node_with_unit {
             None => Ok(SimplifiedValueNode::Number(multiplier)),
             Some(ref value) => Ok(value * multiplier)
         }
     }
 
-    fn parse_length(input: &mut Parser,
-                    context: AllowedNumericType) -> Result<Length, ()> {
-        CalcLengthOrPercentage::parse(input, CalcUnit::Length).map(|calc| {
-            Length::Calc(Box::new(calc), context)
+    fn parse_length(context: &ParserContext,
+                    input: &mut Parser,
+                    num_context: AllowedNumericType) -> Result<Length, ()> {
+        CalcLengthOrPercentage::parse(context, input, CalcUnit::Length).map(|calc| {
+            Length::Calc(Box::new(calc), num_context)
         })
     }
 
-    fn parse_length_or_percentage(input: &mut Parser) -> Result<CalcLengthOrPercentage, ()> {
-        CalcLengthOrPercentage::parse(input, CalcUnit::LengthOrPercentage)
+    fn parse_length_or_percentage(context: &ParserContext, input: &mut Parser) -> Result<CalcLengthOrPercentage, ()> {
+        CalcLengthOrPercentage::parse(context, input, CalcUnit::LengthOrPercentage)
     }
 
     #[allow(missing_docs)]
-    pub fn parse(input: &mut Parser,
+    pub fn parse(context: &ParserContext,
+                 input: &mut Parser,
                  expected_unit: CalcUnit) -> Result<CalcLengthOrPercentage, ()> {
-        let ast = try!(CalcLengthOrPercentage::parse_sum(input, expected_unit));
+        let ast = try!(CalcLengthOrPercentage::parse_sum(context, input, expected_unit));
 
         let mut simplified = Vec::new();
         for ref node in ast.products {
             match try!(CalcLengthOrPercentage::simplify_product(node)) {
                 SimplifiedValueNode::Sum(sum) => simplified.extend_from_slice(&sum.values),
                 value => simplified.push(value),
             }
         }
@@ -888,18 +914,18 @@ impl CalcLengthOrPercentage {
             ex: ex,
             ch: ch,
             rem: rem,
             percentage: percentage,
         })
     }
 
     #[allow(missing_docs)]
-    pub fn parse_time(input: &mut Parser) -> Result<Time, ()> {
-        let ast = try!(CalcLengthOrPercentage::parse_sum(input, CalcUnit::Time));
+    pub fn parse_time(context: &ParserContext, input: &mut Parser) -> Result<Time, ()> {
+        let ast = try!(CalcLengthOrPercentage::parse_sum(context, input, CalcUnit::Time));
 
         let mut simplified = Vec::new();
         for ref node in ast.products {
             match try!(CalcLengthOrPercentage::simplify_product(node)) {
                 SimplifiedValueNode::Sum(sum) => simplified.extend_from_slice(&sum.values),
                 value => simplified.push(value),
             }
         }
@@ -916,18 +942,18 @@ impl CalcLengthOrPercentage {
 
         match time {
             Some(time) => Ok(Time::from_calc(time)),
             _ => Err(())
         }
     }
 
     #[allow(missing_docs)]
-    pub fn parse_angle(input: &mut Parser) -> Result<Angle, ()> {
-        let ast = try!(CalcLengthOrPercentage::parse_sum(input, CalcUnit::Angle));
+    pub fn parse_angle(context: &ParserContext, input: &mut Parser) -> Result<Angle, ()> {
+        let ast = try!(CalcLengthOrPercentage::parse_sum(context, input, CalcUnit::Angle));
 
         let mut simplified = Vec::new();
         for ref node in ast.products {
             match try!(CalcLengthOrPercentage::simplify_product(node)) {
                 SimplifiedValueNode::Sum(sum) => simplified.extend_from_slice(&sum.values),
                 value => simplified.push(value),
             }
         }
@@ -1114,59 +1140,63 @@ impl ToCss for LengthOrPercentage {
     }
 }
 impl LengthOrPercentage {
     /// Returns a `zero` length.
     pub fn zero() -> LengthOrPercentage {
         LengthOrPercentage::Length(NoCalcLength::zero())
     }
 
-    fn parse_internal(input: &mut Parser, context: AllowedNumericType)
+    fn parse_internal(context: &ParserContext, input: &mut Parser, num_context: AllowedNumericType)
                       -> Result<LengthOrPercentage, ()>
     {
         match try!(input.next()) {
-            Token::Dimension(ref value, ref unit) if context.is_ok(value.value) =>
-                NoCalcLength::parse_dimension(value.value, unit).map(LengthOrPercentage::Length),
-            Token::Percentage(ref value) if context.is_ok(value.unit_value) =>
+            Token::Dimension(ref value, ref unit) if num_context.is_ok(value.value) =>
+                NoCalcLength::parse_dimension(context, value.value, unit).map(LengthOrPercentage::Length),
+            Token::Percentage(ref value) if num_context.is_ok(value.unit_value) =>
                 Ok(LengthOrPercentage::Percentage(Percentage(value.unit_value))),
             Token::Number(ref value) if value.value == 0. =>
                 Ok(LengthOrPercentage::zero()),
             Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
-                let calc = try!(input.parse_nested_block(CalcLengthOrPercentage::parse_length_or_percentage));
+                let calc = try!(input.parse_nested_block(|i| {
+                    CalcLengthOrPercentage::parse_length_or_percentage(context, i)
+                }));
                 Ok(LengthOrPercentage::Calc(Box::new(calc)))
             },
             _ => Err(())
         }
     }
 
     /// Parse a non-negative length.
     #[inline]
-    pub fn parse_non_negative(input: &mut Parser) -> Result<LengthOrPercentage, ()> {
-        Self::parse_internal(input, AllowedNumericType::NonNegative)
+    pub fn parse_non_negative(context: &ParserContext, input: &mut Parser) -> Result<LengthOrPercentage, ()> {
+        Self::parse_internal(context, input, AllowedNumericType::NonNegative)
     }
 
     /// Parse a length, treating dimensionless numbers as pixels
     ///
     /// https://www.w3.org/TR/SVG2/types.html#presentation-attribute-css-value
-    pub fn parse_numbers_are_pixels(input: &mut Parser) -> Result<LengthOrPercentage, ()> {
-        if let Ok(lop) = input.try(|i| Self::parse_internal(i, AllowedNumericType::All)) {
+    pub fn parse_numbers_are_pixels(context: &ParserContext, input: &mut Parser) -> Result<LengthOrPercentage, ()> {
+        if let Ok(lop) = input.try(|i| Self::parse_internal(context, i, AllowedNumericType::All)) {
             return Ok(lop)
         }
 
         // TODO(emilio): Probably should use Number::parse_non_negative to
         // handle calc()?
         let num = input.expect_number()?;
         Ok(LengthOrPercentage::Length(NoCalcLength::Absolute(AbsoluteLength::Px(num))))
     }
 
     /// Parse a non-negative length, treating dimensionless numbers as pixels
     ///
     /// This is nonstandard behavior used by Firefox for SVG
-    pub fn parse_numbers_are_pixels_non_negative(input: &mut Parser) -> Result<LengthOrPercentage, ()> {
-        if let Ok(lop) = input.try(|i| Self::parse_internal(i, AllowedNumericType::NonNegative)) {
+    pub fn parse_numbers_are_pixels_non_negative(context: &ParserContext,
+                                                 input: &mut Parser)
+                                                 -> Result<LengthOrPercentage, ()> {
+        if let Ok(lop) = input.try(|i| Self::parse_internal(context, i, AllowedNumericType::NonNegative)) {
             return Ok(lop)
         }
 
         // 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))))
@@ -1181,18 +1211,18 @@ impl LengthOrPercentage {
     #[inline]
     pub fn take(&mut self) -> Self {
         mem::replace(self, LengthOrPercentage::zero())
     }
 }
 
 impl Parse for LengthOrPercentage {
     #[inline]
-    fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
-        Self::parse_internal(input, AllowedNumericType::All)
+    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
+        Self::parse_internal(context, input, AllowedNumericType::All)
     }
 }
 
 /// TODO(emilio): Do the Length and Percentage variants make any sense with
 /// CalcLengthOrPercentage?
 #[derive(Clone, PartialEq, Debug)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[allow(missing_docs)]
@@ -1235,56 +1265,58 @@ impl ToCss for LengthOrPercentageOrAuto 
             LengthOrPercentageOrAuto::Percentage(percentage) => percentage.to_css(dest),
             LengthOrPercentageOrAuto::Auto => dest.write_str("auto"),
             LengthOrPercentageOrAuto::Calc(ref calc) => calc.to_css(dest),
         }
     }
 }
 
 impl LengthOrPercentageOrAuto {
-    fn parse_internal(input: &mut Parser, context: AllowedNumericType)
+    fn parse_internal(context: &ParserContext, input: &mut Parser, num_context: AllowedNumericType)
                       -> Result<Self, ()> {
         match try!(input.next()) {
-            Token::Dimension(ref value, ref unit) if context.is_ok(value.value) =>
-                NoCalcLength::parse_dimension(value.value, unit).map(LengthOrPercentageOrAuto::Length),
-            Token::Percentage(ref value) if context.is_ok(value.unit_value) =>
+            Token::Dimension(ref value, ref unit) if num_context.is_ok(value.value) =>
+                NoCalcLength::parse_dimension(context, value.value, unit).map(LengthOrPercentageOrAuto::Length),
+            Token::Percentage(ref value) if num_context.is_ok(value.unit_value) =>
                 Ok(LengthOrPercentageOrAuto::Percentage(Percentage(value.unit_value))),
             Token::Number(ref value) if value.value == 0. =>
                 Ok(Self::zero()),
             Token::Ident(ref value) if value.eq_ignore_ascii_case("auto") =>
                 Ok(LengthOrPercentageOrAuto::Auto),
             Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
-                let calc = try!(input.parse_nested_block(CalcLengthOrPercentage::parse_length_or_percentage));
+                let calc = try!(input.parse_nested_block(|i| {
+                    CalcLengthOrPercentage::parse_length_or_percentage(context, i)
+                }));
                 Ok(LengthOrPercentageOrAuto::Calc(Box::new(calc)))
             },
             _ => Err(())
         }
     }
 
     /// Parse a non-negative length, percentage, or auto.
     #[inline]
-    pub fn parse_non_negative(input: &mut Parser) -> Result<LengthOrPercentageOrAuto, ()> {
-        Self::parse_internal(input, AllowedNumericType::NonNegative)
+    pub fn parse_non_negative(context: &ParserContext, input: &mut Parser) -> Result<LengthOrPercentageOrAuto, ()> {
+        Self::parse_internal(context, input, AllowedNumericType::NonNegative)
     }
 
     /// Returns the `auto` value.
     pub fn auto() -> Self {
         LengthOrPercentageOrAuto::Auto
     }
 
     /// Returns a value representing a `0` length.
     pub fn zero() -> Self {
         LengthOrPercentageOrAuto::Length(NoCalcLength::zero())
     }
 }
 
 impl Parse for LengthOrPercentageOrAuto {
     #[inline]
-    fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
-        Self::parse_internal(input, AllowedNumericType::All)
+    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
+        Self::parse_internal(context, input, AllowedNumericType::All)
     }
 }
 
 /// TODO(emilio): Do the Length and Percentage variants make any sense with
 /// CalcLengthOrPercentage?
 #[derive(Clone, PartialEq, Debug)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[allow(missing_docs)]
@@ -1311,46 +1343,48 @@ impl ToCss for LengthOrPercentageOrNone 
             LengthOrPercentageOrNone::Length(ref length) => length.to_css(dest),
             LengthOrPercentageOrNone::Percentage(ref percentage) => percentage.to_css(dest),
             LengthOrPercentageOrNone::Calc(ref calc) => calc.to_css(dest),
             LengthOrPercentageOrNone::None => dest.write_str("none"),
         }
     }
 }
 impl LengthOrPercentageOrNone {
-    fn parse_internal(input: &mut Parser, context: AllowedNumericType)
+    fn parse_internal(context: &ParserContext, input: &mut Parser, num_context: AllowedNumericType)
                       -> Result<LengthOrPercentageOrNone, ()>
     {
         match try!(input.next()) {
-            Token::Dimension(ref value, ref unit) if context.is_ok(value.value) =>
-                NoCalcLength::parse_dimension(value.value, unit).map(LengthOrPercentageOrNone::Length),
-            Token::Percentage(ref value) if context.is_ok(value.unit_value) =>
+            Token::Dimension(ref value, ref unit) if num_context.is_ok(value.value) =>
+                NoCalcLength::parse_dimension(context, value.value, unit).map(LengthOrPercentageOrNone::Length),
+            Token::Percentage(ref value) if num_context.is_ok(value.unit_value) =>
                 Ok(LengthOrPercentageOrNone::Percentage(Percentage(value.unit_value))),
             Token::Number(ref value) if value.value == 0. =>
                 Ok(LengthOrPercentageOrNone::Length(NoCalcLength::zero())),
             Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
-                let calc = try!(input.parse_nested_block(CalcLengthOrPercentage::parse_length_or_percentage));
+                let calc = try!(input.parse_nested_block(|i| {
+                    CalcLengthOrPercentage::parse_length_or_percentage(context, i)
+                }));
                 Ok(LengthOrPercentageOrNone::Calc(Box::new(calc)))
             },
             Token::Ident(ref value) if value.eq_ignore_ascii_case("none") =>
                 Ok(LengthOrPercentageOrNone::None),
             _ => Err(())
         }
     }
     /// Parse a non-negative LengthOrPercentageOrNone.
     #[inline]
-    pub fn parse_non_negative(input: &mut Parser) -> Result<Self, ()> {
-        Self::parse_internal(input, AllowedNumericType::NonNegative)
+    pub fn parse_non_negative(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
+        Self::parse_internal(context, input, AllowedNumericType::NonNegative)
     }
 }
 
 impl Parse for LengthOrPercentageOrNone {
     #[inline]
-    fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
-        Self::parse_internal(input, AllowedNumericType::All)
+    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
+        Self::parse_internal(context, input, AllowedNumericType::All)
     }
 }
 
 /// Either a `<length>` or the `none` keyword.
 pub type LengthOrNone = Either<Length, None_>;
 
 /// Either a `<length>` or the `normal` keyword.
 pub type LengthOrNormal = Either<Length, Normal>;
@@ -1374,31 +1408,34 @@ pub enum LengthOrPercentageOrAutoOrConte
     /// The `auto` keyword.
     Auto,
     /// The `content` keyword.
     Content
 }
 
 impl LengthOrPercentageOrAutoOrContent {
     /// Parse a non-negative LengthOrPercentageOrAutoOrContent.
-    pub fn parse_non_negative(input: &mut Parser) -> Result<Self, ()> {
-        let context = AllowedNumericType::NonNegative;
+    pub fn parse_non_negative(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
+        let num_context = AllowedNumericType::NonNegative;
         match try!(input.next()) {
-            Token::Dimension(ref value, ref unit) if context.is_ok(value.value) =>
-                NoCalcLength::parse_dimension(value.value, unit).map(LengthOrPercentageOrAutoOrContent::Length),
-            Token::Percentage(ref value) if context.is_ok(value.unit_value) =>
+            Token::Dimension(ref value, ref unit) if num_context.is_ok(value.value) =>
+                NoCalcLength::parse_dimension(context, value.value, unit)
+                             .map(LengthOrPercentageOrAutoOrContent::Length),
+            Token::Percentage(ref value) if num_context.is_ok(value.unit_value) =>
                 Ok(LengthOrPercentageOrAutoOrContent::Percentage(Percentage(value.unit_value))),
             Token::Number(ref value) if value.value == 0. =>
                 Ok(Self::zero()),
             Token::Ident(ref value) if value.eq_ignore_ascii_case("auto") =>
                 Ok(LengthOrPercentageOrAutoOrContent::Auto),
             Token::Ident(ref value) if value.eq_ignore_ascii_case("content") =>
                 Ok(LengthOrPercentageOrAutoOrContent::Content),
             Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
-                let calc = try!(input.parse_nested_block(CalcLengthOrPercentage::parse_length_or_percentage));
+                let calc = try!(input.parse_nested_block(|i| {
+                    CalcLengthOrPercentage::parse_length_or_percentage(context, i)
+                }));
                 Ok(LengthOrPercentageOrAutoOrContent::Calc(Box::new(calc)))
             },
             _ => Err(())
         }
     }
 
     /// Returns the `auto` value.
     pub fn auto() -> Self {
@@ -1433,25 +1470,25 @@ impl ToCss for LengthOrPercentageOrAutoO
     }
 }
 
 /// Either a `<length>` or a `<number>`.
 pub type LengthOrNumber = Either<Length, Number>;
 
 impl LengthOrNumber {
     /// Parse a non-negative LengthOrNumber.
-    pub fn parse_non_negative(_: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
+    pub fn parse_non_negative(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         // We try to parse as a Number first because, for cases like
         // LengthOrNumber, we want "0" to be parsed as a plain Number rather
         // than a Length (0px); this matches the behaviour of all major browsers
-        if let Ok(v) = input.try(Number::parse_non_negative) {
+        if let Ok(v) = input.try(|i| Number::parse_non_negative(context, i)) {
             return Ok(Either::Second(v))
         }
 
-        Length::parse_non_negative(input).map(Either::First)
+        Length::parse_non_negative(context, input).map(Either::First)
     }
 }
 
 /// A value suitable for a `min-width` or `min-height` property.
 /// Unlike `max-width` or `max-height` properties, a MinLength can be
 /// `auto`, and cannot be `none`.
 #[derive(Debug, Clone, PartialEq)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
@@ -1480,19 +1517,20 @@ impl ToCss for MinLength {
                 dest.write_str("auto"),
             MinLength::ExtremumLength(ref ext) =>
                 ext.to_css(dest),
         }
     }
 }
 
 impl Parse for MinLength {
-    fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
+    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         input.try(ExtremumLength::parse).map(MinLength::ExtremumLength)
-            .or_else(|()| input.try(LengthOrPercentage::parse_non_negative).map(MinLength::LengthOrPercentage))
+            .or_else(|()| input.try(|i| LengthOrPercentage::parse_non_negative(context, i))
+                               .map(MinLength::LengthOrPercentage))
             .or_else(|()| input.expect_ident_matching("auto").map(|()| MinLength::Auto))
     }
 }
 
 /// A value suitable for a `max-width` or `max-height` property.
 #[derive(Debug, Clone, PartialEq)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[allow(missing_docs)]
@@ -1520,19 +1558,20 @@ impl ToCss for MaxLength {
                 dest.write_str("none"),
             MaxLength::ExtremumLength(ref ext) =>
                 ext.to_css(dest),
         }
     }
 }
 
 impl Parse for MaxLength {
-    fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
+    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         input.try(ExtremumLength::parse).map(MaxLength::ExtremumLength)
-            .or_else(|()| input.try(LengthOrPercentage::parse_non_negative).map(MaxLength::LengthOrPercentage))
+            .or_else(|()| input.try(|i| LengthOrPercentage::parse_non_negative(context, i))
+                               .map(MaxLength::LengthOrPercentage))
             .or_else(|()| {
                 match_ignore_ascii_case! { &try!(input.expect_ident()),
                     "none" =>
                         Ok(MaxLength::None),
                     _ => Err(())
                 }
             })
     }
--- a/servo/components/style/values/specified/mod.rs
+++ b/servo/components/style/values/specified/mod.rs
@@ -203,22 +203,22 @@ impl<'a> Mul<CSSFloat> for &'a Simplifie
                 let sum = &**s * scalar;
                 SimplifiedValueNode::Sum(Box::new(sum))
             },
         }
     }
 }
 
 #[allow(missing_docs)]
-pub fn parse_integer(input: &mut Parser) -> Result<Integer, ()> {
+pub fn parse_integer(context: &ParserContext, input: &mut Parser) -> Result<Integer, ()> {
     match try!(input.next()) {
         Token::Number(ref value) => value.int_value.ok_or(()).map(Integer::new),
         Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
             let ast = try!(input.parse_nested_block(|i| {
-                CalcLengthOrPercentage::parse_sum(i, CalcUnit::Integer)
+                CalcLengthOrPercentage::parse_sum(context, i, CalcUnit::Integer)
             }));
 
             let mut result = None;
 
             for ref node in ast.products {
                 match try!(CalcLengthOrPercentage::simplify_product(node)) {
                     SimplifiedValueNode::Number(val) =>
                         result = Some(result.unwrap_or(0) + val as CSSInteger),
@@ -231,26 +231,28 @@ pub fn parse_integer(input: &mut Parser)
                 _ => Err(())
             }
         }
         _ => Err(())
     }
 }
 
 #[allow(missing_docs)]
-pub fn parse_number(input: &mut Parser) -> Result<Number, ()> {
+pub fn parse_number(context: &ParserContext, input: &mut Parser) -> Result<Number, ()> {
     match try!(input.next()) {
         Token::Number(ref value) => {
             Ok(Number {
                 value: value.value.min(f32::MAX).max(f32::MIN),
                 was_calc: false,
             })
         },
         Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
-            let ast = try!(input.parse_nested_block(|i| CalcLengthOrPercentage::parse_sum(i, CalcUnit::Number)));
+            let ast = try!(input.parse_nested_block(|i| {
+                CalcLengthOrPercentage::parse_sum(context, i, CalcUnit::Number)
+            }));
 
             let mut result = None;
 
             for ref node in ast.products {
                 match try!(CalcLengthOrPercentage::simplify_product(node)) {
                     SimplifiedValueNode::Number(val) =>
                         result = Some(result.unwrap_or(0.) + val),
                     _ => unreachable!()
@@ -293,19 +295,19 @@ impl BorderRadiusSize {
     #[allow(missing_docs)]
     pub fn circle(radius: LengthOrPercentage) -> BorderRadiusSize {
         BorderRadiusSize(Size2D::new(radius.clone(), radius))
     }
 }
 
 impl Parse for BorderRadiusSize {
     #[inline]
-    fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
-        let first = try!(LengthOrPercentage::parse_non_negative(input));
-        let second = input.try(LengthOrPercentage::parse_non_negative)
+    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
+        let first = try!(LengthOrPercentage::parse_non_negative(context, input));
+        let second = input.try(|i| LengthOrPercentage::parse_non_negative(context, i))
             .unwrap_or_else(|()| first.clone());
         Ok(BorderRadiusSize(Size2D::new(first, second)))
     }
 }
 
 impl ToCss for BorderRadiusSize {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         try!(self.0.width.to_css(dest));
@@ -423,21 +425,21 @@ impl Angle {
             unit: AngleUnit::Radian,
             was_calc: true,
         }
     }
 }
 
 impl Parse for Angle {
     /// Parses an angle according to CSS-VALUES § 6.1.
-    fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
+    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         match try!(input.next()) {
             Token::Dimension(ref value, ref unit) => Angle::parse_dimension(value.value, unit),
             Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
-                input.parse_nested_block(CalcLengthOrPercentage::parse_angle)
+                input.parse_nested_block(|i| CalcLengthOrPercentage::parse_angle(context, i))
             },
             _ => Err(())
         }
     }
 }
 
 impl Angle {
     #[allow(missing_docs)]
@@ -452,22 +454,22 @@ impl Angle {
         Ok(angle)
     }
     /// Parse an angle, including unitless 0 degree.
     /// Note that numbers without any AngleUnit, including unitless 0
     /// angle, should be invalid. However, some properties still accept
     /// unitless 0 angle and stores it as '0deg'. We can remove this and
     /// get back to the unified version Angle::parse once
     /// https://github.com/w3c/csswg-drafts/issues/1162 is resolved.
-    pub fn parse_with_unitless(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
+    pub fn parse_with_unitless(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         match try!(input.next()) {
             Token::Dimension(ref value, ref unit) => Angle::parse_dimension(value.value, unit),
             Token::Number(ref value) if value.value == 0. => Ok(Angle::zero()),
             Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
-                input.parse_nested_block(CalcLengthOrPercentage::parse_angle)
+                input.parse_nested_block(|i| CalcLengthOrPercentage::parse_angle(context, i))
             },
             _ => Err(())
         }
     }
 }
 
 #[allow(missing_docs)]
 pub fn parse_border_radius(context: &ParserContext, input: &mut Parser) -> Result<BorderRadiusSize, ()> {
@@ -480,18 +482,18 @@ pub fn parse_border_radius(context: &Par
             "thick" => Ok(BorderRadiusSize::circle(
                               LengthOrPercentage::Length(NoCalcLength::from_px(5.)))),
             _ => Err(())
         }
     })
 }
 
 #[allow(missing_docs)]
-pub fn parse_border_width(input: &mut Parser) -> Result<Length, ()> {
-    input.try(Length::parse_non_negative).or_else(|()| {
+pub fn parse_border_width(context: &ParserContext, input: &mut Parser) -> Result<Length, ()> {
+    input.try(|i| Length::parse_non_negative(context, i)).or_else(|()| {
         match_ignore_ascii_case! { &try!(input.expect_ident()),
             "thin" => Ok(Length::from_px(1.)),
             "medium" => Ok(Length::from_px(3.)),
             "thick" => Ok(Length::from_px(5.)),
             _ => Err(())
         }
     })
 }
@@ -502,18 +504,18 @@ pub fn parse_border_width(input: &mut Pa
 pub enum BorderWidth {
     Thin,
     Medium,
     Thick,
     Width(Length),
 }
 
 impl Parse for BorderWidth {
-    fn parse(_context: &ParserContext, input: &mut Parser) -> Result<BorderWidth, ()> {
-        match input.try(Length::parse_non_negative) {
+    fn parse(context: &ParserContext, input: &mut Parser) -> Result<BorderWidth, ()> {
+        match input.try(|i| Length::parse_non_negative(context, i)) {
             Ok(length) => Ok(BorderWidth::Width(length)),
             Err(_) => match_ignore_ascii_case! { &try!(input.expect_ident()),
                "thin" => Ok(BorderWidth::Thin),
                "medium" => Ok(BorderWidth::Medium),
                "thick" => Ok(BorderWidth::Thick),
                _ => Err(())
             }
         }
@@ -654,23 +656,23 @@ impl ToComputedValue for Time {
         Time {
             seconds: computed.seconds(),
             was_calc: false,
         }
     }
 }
 
 impl Parse for Time {
-    fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
+    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         match input.next() {
             Ok(Token::Dimension(ref value, ref unit)) => {
                 Time::parse_dimension(value.value, &unit)
             }
             Ok(Token::Function(ref name)) if name.eq_ignore_ascii_case("calc") => {
-                input.parse_nested_block(CalcLengthOrPercentage::parse_time)
+                input.parse_nested_block(|i| CalcLengthOrPercentage::parse_time(context, i))
             }
             _ => Err(())
         }
     }
 }
 
 impl ToCss for Time {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
@@ -695,45 +697,45 @@ pub struct Number {
     /// serialization purposes, since `calc(1)` should still serialize to
     /// `calc(1)`, not just `1`.
     was_calc: bool,
 }
 
 no_viewport_percentage!(Number);
 
 impl Parse for Number {
-    fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
-        parse_number(input)
+    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
+        parse_number(context, input)
     }
 }
 
 impl Number {
-    fn parse_with_minimum(input: &mut Parser, min: CSSFloat) -> Result<Number, ()> {
-        match parse_number(input) {
+    fn parse_with_minimum(context: &ParserContext, input: &mut Parser, min: CSSFloat) -> Result<Number, ()> {
+        match parse_number(context, input) {
             Ok(value) if value.value >= min => Ok(value),
             _ => Err(()),
         }
     }
 
     /// Returns a new number with the value `val`.
     pub fn new(val: CSSFloat) -> Self {
         Number {
             value: val,
             was_calc: false,
         }
     }
 
     #[allow(missing_docs)]
-    pub fn parse_non_negative(input: &mut Parser) -> Result<Number, ()> {
-        Number::parse_with_minimum(input, 0.0)
+    pub fn parse_non_negative(context: &ParserContext, input: &mut Parser) -> Result<Number, ()> {
+        Number::parse_with_minimum(context, input, 0.0)
     }
 
     #[allow(missing_docs)]
-    pub fn parse_at_least_one(input: &mut Parser) -> Result<Number, ()> {
-        Number::parse_with_minimum(input, 1.0)
+    pub fn parse_at_least_one(context: &ParserContext, input: &mut Parser) -> Result<Number, ()> {
+        Number::parse_with_minimum(context, input, 1.0)
     }
 }
 
 impl ToComputedValue for Number {
     type ComputedValue = CSSFloat;
 
     #[inline]
     fn to_computed_value(&self, _: &Context) -> CSSFloat { self.value }
@@ -770,22 +772,22 @@ impl ToCss for Number {
 pub enum NumberOrPercentage {
     Percentage(Percentage),
     Number(Number),
 }
 
 no_viewport_percentage!(NumberOrPercentage);
 
 impl Parse for NumberOrPercentage {
-    fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
+    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         if let Ok(per) = input.try(Percentage::parse_non_negative) {
             return Ok(NumberOrPercentage::Percentage(per));
         }
 
-        Number::parse_non_negative(input).map(NumberOrPercentage::Number)
+        Number::parse_non_negative(context, input).map(NumberOrPercentage::Number)
     }
 }
 
 impl ToCss for NumberOrPercentage {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         match *self {
             NumberOrPercentage::Percentage(percentage) => percentage.to_css(dest),
             NumberOrPercentage::Number(number) => number.to_css(dest),
@@ -796,18 +798,18 @@ impl ToCss for NumberOrPercentage {
 #[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[allow(missing_docs)]
 pub struct Opacity(Number);
 
 no_viewport_percentage!(Opacity);
 
 impl Parse for Opacity {
-    fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
-        parse_number(input).map(Opacity)
+    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
+        parse_number(context, input).map(Opacity)
     }
 }
 
 impl ToComputedValue for Opacity {
     type ComputedValue = CSSFloat;
 
     #[inline]
     fn to_computed_value(&self, context: &Context) -> CSSFloat {
@@ -855,37 +857,37 @@ impl Integer {
             was_calc: true,
         }
     }
 }
 
 no_viewport_percentage!(Integer);
 
 impl Parse for Integer {
-    fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
-        parse_integer(input)
+    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
+        parse_integer(context, input)
     }
 }
 
 impl Integer {
-    fn parse_with_minimum(input: &mut Parser, min: i32) -> Result<Integer, ()> {
-        match parse_integer(input) {
+    fn parse_with_minimum(context: &ParserContext, input: &mut Parser, min: i32) -> Result<Integer, ()> {
+        match parse_integer(context, input) {
             Ok(value) if value.value() >= min => Ok(value),
             _ => Err(()),
         }
     }
 
     #[allow(missing_docs)]
-    pub fn parse_non_negative(input: &mut Parser) -> Result<Integer, ()> {
-        Integer::parse_with_minimum(input, 0)
+    pub fn parse_non_negative(context: &ParserContext, input: &mut Parser) -> Result<Integer, ()> {
+        Integer::parse_with_minimum(context, input, 0)
     }
 
     #[allow(missing_docs)]
-    pub fn parse_positive(input: &mut Parser) -> Result<Integer, ()> {
-        Integer::parse_with_minimum(input, 1)
+    pub fn parse_positive(context: &ParserContext, input: &mut Parser) -> Result<Integer, ()> {
+        Integer::parse_with_minimum(context, input, 1)
     }
 }
 
 impl ToComputedValue for Integer {
     type ComputedValue = i32;
 
     #[inline]
     fn to_computed_value(&self, _: &Context) -> i32 { self.value }
@@ -985,34 +987,34 @@ impl ToComputedValue for Shadow {
             inset: computed.inset,
         }
     }
 }
 
 impl Shadow {
     // disable_spread_and_inset is for filter: drop-shadow(...)
     #[allow(missing_docs)]
-    pub fn parse(context:  &ParserContext, input: &mut Parser, disable_spread_and_inset: bool) -> Result<Shadow, ()> {
+    pub fn parse(context: &ParserContext, input: &mut Parser, disable_spread_and_inset: bool) -> Result<Shadow, ()> {
         let mut lengths = [Length::zero(), Length::zero(), Length::zero(), Length::zero()];
         let mut lengths_parsed = false;
         let mut color = None;
         let mut inset = false;
 
         loop {
             if !inset && !disable_spread_and_inset {
                 if input.try(|input| input.expect_ident_matching("inset")).is_ok() {
                     inset = true;
                     continue
                 }
             }
             if !lengths_parsed {
                 if let Ok(value) = input.try(|i| Length::parse(context, i)) {
                     lengths[0] = value;
                     lengths[1] = try!(Length::parse(context, input));
-                    if let Ok(value) = input.try(|i| Length::parse_non_negative(i)) {
+                    if let Ok(value) = input.try(|i| Length::parse_non_negative(context, i)) {
                         lengths[2] = value;
                         if !disable_spread_and_inset {
                             if let Ok(value) = input.try(|i| Length::parse(context, i)) {
                                 lengths[3] = value;
                             }
                         }
                     }
                     lengths_parsed = true;
@@ -1199,24 +1201,24 @@ impl ToComputedValue for SVGPaintKind {
     }
 }
 
 /// <length> | <percentage> | <number>
 pub type LengthOrPercentageOrNumber = Either<LengthOrPercentage, Number>;
 
 impl LengthOrPercentageOrNumber {
     /// parse a <length-percentage> | <number> enforcing that the contents aren't negative
-    pub fn parse_non_negative(_: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
+    pub fn parse_non_negative(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         // NB: Parse numbers before Lengths so we are consistent about how to
         // recognize and serialize "0".
-        if let Ok(num) = input.try(Number::parse_non_negative) {
+        if let Ok(num) = input.try(|i| Number::parse_non_negative(context, i)) {
             return Ok(Either::Second(num))
         }
 
-        LengthOrPercentage::parse_non_negative(input).map(Either::First)
+        LengthOrPercentage::parse_non_negative(context, input).map(Either::First)
     }
 }
 
 impl HasViewportPercentage for ClipRect {
     fn has_viewport_percentage(&self) -> bool {
         self.top.as_ref().map_or(false, |x| x.has_viewport_percentage()) ||
         self.right.as_ref().map_or(false, |x| x.has_viewport_percentage()) ||
         self.bottom.as_ref().map_or(false, |x| x.has_viewport_percentage()) ||
--- a/servo/components/style/viewport.rs
+++ b/servo/components/style/viewport.rs
@@ -161,20 +161,20 @@ impl FromMeta for ViewportLength {
                     Err(_) => specified!(NoCalcLength::from_px(1.))
                 }
             }
         })
     }
 }
 
 impl ViewportLength {
-    fn parse(input: &mut Parser) -> Result<Self, ()> {
+    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
         // we explicitly do not accept 'extend-to-zoom', since it is a UA
         // internal value for <META> viewport translation
-        LengthOrPercentageOrAuto::parse_non_negative(input).map(ViewportLength::Specified)
+        LengthOrPercentageOrAuto::parse_non_negative(context, input).map(ViewportLength::Specified)
     }
 }
 
 impl FromMeta for Zoom {
     fn from_meta(value: &str) -> Option<Zoom> {
         Some(match value {
             v if v.eq_ignore_ascii_case("yes") => Zoom::Number(1.),
             v if v.eq_ignore_ascii_case("no") => Zoom::Number(0.1),
@@ -240,65 +240,65 @@ impl ToCss for ViewportDescriptorDeclara
         try!(self.descriptor.to_css(dest));
         if self.important {
             try!(dest.write_str(" !important"));
         }
         dest.write_str(";")
     }
 }
 
-fn parse_shorthand(input: &mut Parser) -> Result<(ViewportLength, ViewportLength), ()> {
-    let min = try!(ViewportLength::parse(input));
-    match input.try(ViewportLength::parse) {
+fn parse_shorthand(context: &ParserContext, input: &mut Parser) -> Result<(ViewportLength, ViewportLength), ()> {
+    let min = try!(ViewportLength::parse(context, input));
+    match input.try(|i| ViewportLength::parse(context, i)) {
         Err(()) => Ok((min.clone(), min)),
         Ok(max) => Ok((min, max))
     }
 }
 
 impl<'a, 'b> AtRuleParser for ViewportRuleParser<'a, 'b> {
     type Prelude = ();
     type AtRule = Vec<ViewportDescriptorDeclaration>;
 }
 
 impl<'a, 'b> DeclarationParser for ViewportRuleParser<'a, 'b> {
     type Declaration = Vec<ViewportDescriptorDeclaration>;
 
     fn parse_value(&mut self, name: &str, input: &mut Parser) -> Result<Vec<ViewportDescriptorDeclaration>, ()> {
         macro_rules! declaration {
-            ($declaration:ident($parse:path)) => {
+            ($declaration:ident($parse:expr)) => {
                 declaration!($declaration(value: try!($parse(input)),
                                           important: input.try(parse_important).is_ok()))
             };
             ($declaration:ident(value: $value:expr, important: $important:expr)) => {
                 ViewportDescriptorDeclaration::new(
                     self.context.stylesheet_origin,
                     ViewportDescriptor::$declaration($value),
                     $important)
             }
         }
 
         macro_rules! ok {
-            ($declaration:ident($parse:path)) => {
+            ($declaration:ident($parse:expr)) => {
                 Ok(vec![declaration!($declaration($parse))])
             };
             (shorthand -> [$min:ident, $max:ident]) => {{
-                let shorthand = try!(parse_shorthand(input));
+                let shorthand = try!(parse_shorthand(self.context, input));
                 let important = input.try(parse_important).is_ok();
 
                 Ok(vec![declaration!($min(value: shorthand.0, important: important)),
                         declaration!($max(value: shorthand.1, important: important))])
             }}
         }
 
         match_ignore_ascii_case! { name,
-            "min-width" => ok!(MinWidth(ViewportLength::parse)),
-            "max-width" => ok!(MaxWidth(ViewportLength::parse)),
+            "min-width" => ok!(MinWidth(|i| ViewportLength::parse(self.context, i))),
+            "max-width" => ok!(MaxWidth(|i| ViewportLength::parse(self.context, i))),
             "width" => ok!(shorthand -> [MinWidth, MaxWidth]),
-            "min-height" => ok!(MinHeight(ViewportLength::parse)),
-            "max-height" => ok!(MaxHeight(ViewportLength::parse)),
+            "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(()),
         }
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -952,19 +952,19 @@ pub extern "C" fn Servo_ParseProperty(pr
         id
     } else {
         return RawServoDeclarationBlockStrong::null()
     };
     let value = unsafe { value.as_ref().unwrap().as_str_unchecked() };
 
     let url_data = unsafe { RefPtr::from_ptr_ref(&data) };
     let reporter = StdoutErrorReporter;
-    let context = ParserContext::new(Origin::Author, url_data, &reporter);
+    let context = ParserContext::new(Origin::Author, url_data, &reporter, Some(CssRuleType::Style));
 
-    match ParsedDeclaration::parse(id, &context, &mut Parser::new(value), false, CssRuleType::Style) {
+    match ParsedDeclaration::parse(id, &context, &mut Parser::new(value)) {
         Ok(parsed) => {
             let global_style_data = &*GLOBAL_STYLE_DATA;
             let mut block = PropertyDeclarationBlock::new();
             parsed.expand_push_into(&mut block, Importance::Normal);
             Arc::new(global_style_data.shared_lock.wrap(block)).into_strong()
         }
         Err(_) => RawServoDeclarationBlockStrong::null()
     }
@@ -974,17 +974,17 @@ pub extern "C" fn Servo_ParseProperty(pr
 pub extern "C" fn Servo_ParseEasing(easing: *const nsAString,
                                     data: *mut URLExtraData,
                                     output: nsTimingFunctionBorrowedMut)
                                     -> bool {
     use style::properties::longhands::transition_timing_function;
 
     let url_data = unsafe { RefPtr::from_ptr_ref(&data) };
     let reporter = StdoutErrorReporter;
-    let context = ParserContext::new(Origin::Author, url_data, &reporter);
+    let context = ParserContext::new(Origin::Author, url_data, &reporter, Some(CssRuleType::Style));
     let easing = unsafe { (*easing).to_string() };
     match transition_timing_function::single_value::parse(&context, &mut Parser::new(&easing)) {
         Ok(parsed_easing) => {
             *output = parsed_easing.into();
             true
         },
         Err(_) => false
     }
@@ -1168,18 +1168,21 @@ pub extern "C" fn Servo_MediaList_GetTex
         list.to_css(unsafe { result.as_mut().unwrap() }).unwrap();
     })
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_MediaList_SetText(list: RawServoMediaListBorrowed, text: *const nsACString) {
     let text = unsafe { text.as_ref().unwrap().as_str_unchecked() };
     let mut parser = Parser::new(&text);
-    write_locked_arc(list, |list: &mut MediaList| {
-        *list = parse_media_query_list(&mut parser);
+    let url_data = unsafe { dummy_url_data() };
+    let reporter = StdoutErrorReporter;
+    let context = ParserContext::new_for_cssom(url_data, &reporter, Some(CssRuleType::Media));
+     write_locked_arc(list, |list: &mut MediaList| {
+        *list = parse_media_query_list(&context, &mut parser);
     })
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_MediaList_GetLength(list: RawServoMediaListBorrowed) -> u32 {
     read_locked_arc(list, |list: &MediaList| list.media_queries.len() as u32)
 }
 
@@ -1195,26 +1198,32 @@ pub extern "C" fn Servo_MediaList_GetMed
         }
     })
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_MediaList_AppendMedium(list: RawServoMediaListBorrowed,
                                                new_medium: *const nsACString) {
     let new_medium = unsafe { new_medium.as_ref().unwrap().as_str_unchecked() };
+    let url_data = unsafe { dummy_url_data() };
+    let reporter = StdoutErrorReporter;
+    let context = ParserContext::new_for_cssom(url_data, &reporter, Some(CssRuleType::Media));
     write_locked_arc(list, |list: &mut MediaList| {
-        list.append_medium(new_medium);
+        list.append_medium(&context, new_medium);
     })
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_MediaList_DeleteMedium(list: RawServoMediaListBorrowed,
                                                old_medium: *const nsACString) -> bool {
     let old_medium = unsafe { old_medium.as_ref().unwrap().as_str_unchecked() };
-    write_locked_arc(list, |list: &mut MediaList| list.delete_medium(old_medium))
+    let url_data = unsafe { dummy_url_data() };
+    let reporter = StdoutErrorReporter;
+    let context = ParserContext::new_for_cssom(url_data, &reporter, Some(CssRuleType::Media));
+    write_locked_arc(list, |list: &mut MediaList| list.delete_medium(&context, old_medium))
 }
 
 macro_rules! get_longhand_from_id {
     ($id:expr, $retval:expr) => {
         match PropertyId::from_nscsspropertyid($id) {
             Ok(PropertyId::Longhand(long)) => long,
             _ => {
                 error!("stylo: unknown presentation property with id {:?}", $id);
@@ -1513,17 +1522,17 @@ pub extern "C" fn Servo_CSSSupports2(pro
 #[no_mangle]
 pub extern "C" fn Servo_CSSSupports(cond: *const nsACString) -> bool {
     let condition = unsafe { cond.as_ref().unwrap().as_str_unchecked() };
     let mut input = Parser::new(&condition);
     let cond = parse_condition_or_declaration(&mut input);
     if let Ok(cond) = cond {
         let url_data = unsafe { dummy_url_data() };
         let reporter = StdoutErrorReporter;
-        let context = ParserContext::new_for_cssom(url_data, &reporter);
+        let context = ParserContext::new_for_cssom(url_data, &reporter, Some(CssRuleType::Style));
         cond.eval(&context)
     } else {
         false
     }
 }
 
 /// Only safe to call on the main thread, with exclusive access to the element and
 /// its ancestors.
--- a/servo/tests/unit/style/parsing/animation.rs
+++ b/servo/tests/unit/style/parsing/animation.rs
@@ -4,17 +4,17 @@
 
 use cssparser::Parser;
 use media_queries::CSSErrorReporterTest;
 use parsing::parse;
 use servo_atoms::Atom;
 use style::parser::{Parse, ParserContext};
 use style::properties::longhands::animation_iteration_count::single_value::computed_value::T as AnimationIterationCount;
 use style::properties::longhands::animation_name;
-use style::stylesheets::Origin;
+use style::stylesheets::{CssRuleType, Origin};
 use style_traits::ToCss;
 
 #[test]
 fn test_animation_name() {
     use self::animation_name::single_value::SpecifiedValue as SingleValue;
     let other_name = Atom::from("other-name");
     assert_eq!(parse_longhand!(animation_name, "none"),
                animation_name::SpecifiedValue(vec![SingleValue(atom!(""))]));
--- a/servo/tests/unit/style/parsing/background.rs
+++ b/servo/tests/unit/style/parsing/background.rs
@@ -5,23 +5,23 @@
 use cssparser::Parser;
 use media_queries::CSSErrorReporterTest;
 use servo_url::ServoUrl;
 use style::parser::ParserContext;
 use style::properties::longhands::{background_attachment, background_clip, background_color, background_image};
 use style::properties::longhands::{background_origin, background_position_x, background_position_y, background_repeat};
 use style::properties::longhands::background_size;
 use style::properties::shorthands::background;
-use style::stylesheets::Origin;
+use style::stylesheets::{CssRuleType, Origin};
 
 #[test]
 fn background_shorthand_should_parse_all_available_properties_when_specified() {
     let url = ServoUrl::parse("http://localhost").unwrap();
     let reporter = CSSErrorReporterTest;
-    let context = ParserContext::new(Origin::Author, &url, &reporter);
+    let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
     let mut parser = Parser::new("url(\"http://servo/test.png\") top center / 200px 200px repeat-x fixed padding-box \
         content-box red");
     let result = background::parse_value(&context, &mut parser).unwrap();
 
     assert_eq!(result.background_image, parse_longhand!(background_image, "url(\"http://servo/test.png\")"));
     assert_eq!(result.background_position_x, parse_longhand!(background_position_x, "center"));
     assert_eq!(result.background_position_y, parse_longhand!(background_position_y, "top"));
     assert_eq!(result.background_size, parse_longhand!(background_size, "200px 200px"));
@@ -31,17 +31,17 @@ fn background_shorthand_should_parse_all
     assert_eq!(result.background_clip, parse_longhand!(background_clip, "content-box"));
     assert_eq!(result.background_color, parse_longhand!(background_color, "red"));
 }
 
 #[test]
 fn background_shorthand_should_parse_when_some_fields_set() {
     let url = ServoUrl::parse("http://localhost").unwrap();
     let reporter = CSSErrorReporterTest;
-    let context = ParserContext::new(Origin::Author, &url, &reporter);
+    let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
     let mut parser = Parser::new("14px 40px repeat-y");
     let result = background::parse_value(&context, &mut parser).unwrap();
 
     assert_eq!(result.background_position_x, parse_longhand!(background_position_x, "14px"));
     assert_eq!(result.background_position_y, parse_longhand!(background_position_y, "40px"));
     assert_eq!(result.background_repeat, parse_longhand!(background_repeat, "repeat-y"));
 
     let mut parser = Parser::new("url(\"http://servo/test.png\") repeat blue");
@@ -62,17 +62,17 @@ fn background_shorthand_should_parse_whe
 
     assert_eq!(result.background_image, parse_longhand!(background_image, "url(\"http://servo/test.png\")"));
 }
 
 #[test]
 fn background_shorthand_should_parse_comma_separated_declarations() {
     let url = ServoUrl::parse("http://localhost").unwrap();
     let reporter = CSSErrorReporterTest;
-    let context = ParserContext::new(Origin::Author, &url, &reporter);
+    let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
     let mut parser = Parser::new("url(\"http://servo/test.png\") top left no-repeat, url(\"http://servo/test.png\") \
         center / 100% 100% no-repeat, white");
     let result = background::parse_value(&context, &mut parser).unwrap();
 
     assert_eq!(result.background_image, parse_longhand!(background_image, "url(\"http://servo/test.png\"), \
         url(\"http://servo/test.png\"), none"));
     assert_eq!(result.background_position_x, parse_longhand!(background_position_x, "left, center, 0%"));
     assert_eq!(result.background_position_y, parse_longhand!(background_position_y, "top, center, 0%"));
@@ -84,17 +84,17 @@ fn background_shorthand_should_parse_com
     assert_eq!(result.background_attachment, parse_longhand!(background_attachment, "scroll, scroll, scroll"));
     assert_eq!(result.background_color, parse_longhand!(background_color, "white"));
 }
 
 #[test]
 fn background_shorthand_should_parse_position_and_size_correctly() {
     let url = ServoUrl::parse("http://localhost").unwrap();
     let reporter = CSSErrorReporterTest;
-    let context = ParserContext::new(Origin::Author, &url, &reporter);
+    let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
     let mut parser = Parser::new("7px 4px");
     let result = background::parse_value(&context, &mut parser).unwrap();
 
     assert_eq!(result.background_position_x, parse_longhand!(background_position_x, "7px"));
     assert_eq!(result.background_position_y, parse_longhand!(background_position_y, "4px"));
 
     let mut parser = Parser::new("7px 4px / 30px 20px");
     let result = background::parse_value(&context, &mut parser).unwrap();
@@ -109,17 +109,17 @@ fn background_shorthand_should_parse_pos
     let mut parser = Parser::new("repeat-x / 30px 20px");
     assert!(background::parse_value(&context, &mut parser).is_err());
 }
 
 #[test]
 fn background_shorthand_should_parse_origin_and_clip_correctly() {
     let url = ServoUrl::parse("http://localhost").unwrap();
     let reporter = CSSErrorReporterTest;
-    let context = ParserContext::new(Origin::Author, &url, &reporter);
+    let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
     let mut parser = Parser::new("padding-box content-box");
     let result = background::parse_value(&context, &mut parser).unwrap();
 
     assert_eq!(result.background_origin, parse_longhand!(background_origin, "padding-box"));
     assert_eq!(result.background_clip, parse_longhand!(background_clip, "content-box"));
 
     let mut parser = Parser::new("padding-box padding-box");
     let result = background::parse_value(&context, &mut parser).unwrap();
--- a/servo/tests/unit/style/parsing/basic_shape.rs
+++ b/servo/tests/unit/style/parsing/basic_shape.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/. */
 
 use cssparser::Parser;
 use media_queries::CSSErrorReporterTest;
 use parsing::parse;
 use style::parser::{Parse, ParserContext};
-use style::stylesheets::Origin;
+use style::stylesheets::{CssRuleType, Origin};
 use style::values::specified::basic_shape::*;
 use style_traits::ToCss;
 
 // Ensure that basic-shape sub-functions parse as both basic shapes
 // and their individual components
 macro_rules! assert_roundtrip_basicshape {
     ($fun:expr, $input:expr, $output:expr) => {
         assert_roundtrip_with_context!($fun, $input, $output);
--- a/servo/tests/unit/style/parsing/border.rs
+++ b/servo/tests/unit/style/parsing/border.rs
@@ -4,135 +4,135 @@
 
 use cssparser::Parser;
 use media_queries::CSSErrorReporterTest;
 use servo_url::ServoUrl;
 use style::parser::{ParserContext, Parse};
 use style::properties::longhands::{border_image_outset, border_image_repeat, border_image_slice};
 use style::properties::longhands::{border_image_source, border_image_width};
 use style::properties::shorthands::border_image;
-use style::stylesheets::Origin;
+use style::stylesheets::{CssRuleType, Origin};
 use style_traits::ToCss;
 
 #[test]
 fn border_image_shorthand_should_parse_when_all_properties_specified() {
     let url = ServoUrl::parse("http://localhost").unwrap();
     let reporter = CSSErrorReporterTest;
-    let context = ParserContext::new(Origin::Author, &url, &reporter);
+    let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
     let mut parser = Parser::new("linear-gradient(red, blue) 30 30% 45 fill / 20px 40px / 10px \
                                  round stretch");
     let result = border_image::parse_value(&context, &mut parser).unwrap();
 
     assert_eq!(result.border_image_source,
                parse_longhand!(border_image_source, "linear-gradient(red, blue)"));
     assert_eq!(result.border_image_slice, parse_longhand!(border_image_slice, "30 30% 45 fill"));
     assert_eq!(result.border_image_width, parse_longhand!(border_image_width, "20px 40px"));
     assert_eq!(result.border_image_outset, parse_longhand!(border_image_outset, "10px"));
     assert_eq!(result.border_image_repeat, parse_longhand!(border_image_repeat, "round stretch"));
 }
 
 #[test]
 fn border_image_shorthand_should_parse_without_width() {
     let url = ServoUrl::parse("http://localhost").unwrap();
     let reporter = CSSErrorReporterTest;
-    let context = ParserContext::new(Origin::Author, &url, &reporter);
+    let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
     let mut parser = Parser::new("linear-gradient(red, blue) 30 30% 45 fill / / 10px round stretch");
     let result = border_image::parse_value(&context, &mut parser).unwrap();
 
     assert_eq!(result.border_image_source,
                parse_longhand!(border_image_source, "linear-gradient(red, blue)"));
     assert_eq!(result.border_image_slice, parse_longhand!(border_image_slice, "30 30% 45 fill"));
     assert_eq!(result.border_image_outset, parse_longhand!(border_image_outset, "10px"));
     assert_eq!(result.border_image_repeat, parse_longhand!(border_image_repeat, "round stretch"));
     assert_eq!(result.border_image_width, border_image_width::get_initial_specified_value());
 }
 
 #[test]
 fn border_image_shorthand_should_parse_without_outset() {
     let url = ServoUrl::parse("http://localhost").unwrap();
     let reporter = CSSErrorReporterTest;
-    let context = ParserContext::new(Origin::Author, &url, &reporter);
+    let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
     let mut parser = Parser::new("linear-gradient(red, blue) 30 30% 45 fill / 20px 40px round");
     let result = border_image::parse_value(&context, &mut parser).unwrap();
 
     assert_eq!(result.border_image_source,
                parse_longhand!(border_image_source, "linear-gradient(red, blue)"));
     assert_eq!(result.border_image_slice, parse_longhand!(border_image_slice, "30 30% 45 fill"));
     assert_eq!(result.border_image_width, parse_longhand!(border_image_width, "20px 40px"));
     assert_eq!(result.border_image_repeat, parse_longhand!(border_image_repeat, "round"));
     assert_eq!(result.border_image_outset, border_image_outset::get_initial_specified_value());
 }
 
 #[test]
 fn border_image_shorthand_should_parse_without_width_or_outset() {
     let url = ServoUrl::parse("http://localhost").unwrap();
     let reporter = CSSErrorReporterTest;
-    let context = ParserContext::new(Origin::Author, &url, &reporter);
+    let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
     let mut parser = Parser::new("linear-gradient(red, blue) 30 30% 45 fill round");
     let result = border_image::parse_value(&context, &mut parser).unwrap();
 
     assert_eq!(result.border_image_source,
                parse_longhand!(border_image_source, "linear-gradient(red, blue)"));
     assert_eq!(result.border_image_slice, parse_longhand!(border_image_slice, "30 30% 45 fill"));
     assert_eq!(result.border_image_repeat, parse_longhand!(border_image_repeat, "round"));
     assert_eq!(result.border_image_width, border_image_width::get_initial_specified_value());
     assert_eq!(result.border_image_outset, border_image_outset::get_initial_specified_value());
 }
 
 #[test]
 fn border_image_shorthand_should_parse_with_just_source() {
     let url = ServoUrl::parse("http://localhost").unwrap();
     let reporter = CSSErrorReporterTest;
-    let context = ParserContext::new(Origin::Author, &url, &reporter);
+    let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
     let mut parser = Parser::new("linear-gradient(red, blue)");
     let result = border_image::parse_value(&context, &mut parser).unwrap();
 
     assert_eq!(result.border_image_source,
                parse_longhand!(border_image_source, "linear-gradient(red, blue)"));
     assert_eq!(result.border_image_slice, border_image_slice::get_initial_specified_value());
     assert_eq!(result.border_image_width, border_image_width::get_initial_specified_value());
     assert_eq!(result.border_image_outset, border_image_outset::get_initial_specified_value());
     assert_eq!(result.border_image_repeat, border_image_repeat::get_initial_specified_value());
 }
 
 #[test]
 fn border_image_outset_should_error_on_negative_length() {
     let url = ServoUrl::parse("http://localhost").unwrap();
     let reporter = CSSErrorReporterTest;
-    let context = ParserContext::new(Origin::Author, &url, &reporter);
+    let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
     let mut parser = Parser::new("-1em");
     let result = border_image_outset::parse(&context, &mut parser);
     assert_eq!(result, Err(()));
 }
 
 #[test]
 fn border_image_outset_should_error_on_negative_number() {
     let url = ServoUrl::parse("http://localhost").unwrap();
     let reporter = CSSErrorReporterTest;
-    let context = ParserContext::new(Origin::Author, &url, &reporter);
+    let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
     let mut parser = Parser::new("-15");
     let result = border_image_outset::parse(&context, &mut parser);
     assert_eq!(result, Err(()));
 }
 
 #[test]
 fn border_image_outset_should_return_number_on_plain_zero() {
     let url = ServoUrl::parse("http://localhost").unwrap();
     let reporter = CSSErrorReporterTest;
-    let context = ParserContext::new(Origin::Author, &url, &reporter);
+    let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
     let mut parser = Parser::new("0");
     let result = border_image_outset::parse(&context, &mut parser);
     assert_eq!(result.unwrap(), parse_longhand!(border_image_outset, "0"));
 }
 
 #[test]
 fn border_image_outset_should_return_length_on_length_zero() {
     let url = ServoUrl::parse("http://localhost").unwrap();
     let reporter = CSSErrorReporterTest;
-    let context = ParserContext::new(Origin::Author, &url, &reporter);
+    let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
     let mut parser = Parser::new("0em");
     let result = border_image_outset::parse(&context, &mut parser);
     assert_eq!(result.unwrap(), parse_longhand!(border_image_outset, "0em"));
 }
 
 #[test]
 fn test_border_style() {
     use style::values::specified::BorderStyle;
--- a/servo/tests/unit/style/parsing/box_.rs
+++ b/servo/tests/unit/style/parsing/box_.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/. */
 
 use cssparser::Parser;
 use media_queries::CSSErrorReporterTest;
 use parsing::parse;
 use style::parser::ParserContext;
-use style::stylesheets::Origin;
+use style::stylesheets::{CssRuleType, Origin};
 use style_traits::ToCss;
 
 #[test]
 fn test_will_change() {
     use style::properties::longhands::will_change;
 
     assert_roundtrip_with_context!(will_change::parse, "auto");
     assert_roundtrip_with_context!(will_change::parse, "scroll-position");
--- a/servo/tests/unit/style/parsing/column.rs
+++ b/servo/tests/unit/style/parsing/column.rs
@@ -1,44 +1,44 @@
 /* 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 cssparser::Parser;
 use media_queries::CSSErrorReporterTest;
 use servo_url::ServoUrl;
 use style::parser::ParserContext;
-use style::stylesheets::Origin;
+use style::stylesheets::{CssRuleType, Origin};
 use style_traits::ToCss;
 
 #[test]
 fn test_column_width() {
     use style::properties::longhands::column_width;
 
     assert_roundtrip_with_context!(column_width::parse, "auto");
     assert_roundtrip_with_context!(column_width::parse, "6px");
     assert_roundtrip_with_context!(column_width::parse, "2.5em");
     assert_roundtrip_with_context!(column_width::parse, "0.3vw");
 
     let url = ServoUrl::parse("http://localhost").unwrap();
     let reporter = CSSErrorReporterTest;
-    let context = ParserContext::new(Origin::Author, &url, &reporter);
+    let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
 
     let mut negative = Parser::new("-6px");
     assert!(column_width::parse(&context, &mut negative).is_err());
 }
 
 #[test]
 fn test_column_gap() {
     use style::properties::longhands::column_gap;
 
     assert_roundtrip_with_context!(column_gap::parse, "normal");
     assert_roundtrip_with_context!(column_gap::parse, "6px");
     assert_roundtrip_with_context!(column_gap::parse, "2.5em");
     assert_roundtrip_with_context!(column_gap::parse, "0.3vw");
 
     let url = ServoUrl::parse("http://localhost").unwrap();
     let reporter = CSSErrorReporterTest;
-    let context = ParserContext::new(Origin::Author, &url, &reporter);
+    let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
 
     let mut negative = Parser::new("-6px");
     assert!(column_gap::parse(&context, &mut negative).is_err());
 }
--- a/servo/tests/unit/style/parsing/containment.rs
+++ b/servo/tests/unit/style/parsing/containment.rs
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use cssparser::Parser;
 use media_queries::CSSErrorReporterTest;
 use style::parser::ParserContext;
-use style::stylesheets::Origin;
+use style::stylesheets::{CssRuleType, Origin};
 
 #[test]
 fn contain_longhand_should_parse_correctly() {
     use style::properties::longhands::contain;
     use style::properties::longhands::contain::SpecifiedValue;
 
     let none = parse_longhand!(contain, "none");
     assert_eq!(none, SpecifiedValue::empty());
--- a/servo/tests/unit/style/parsing/effects.rs
+++ b/servo/tests/unit/style/parsing/effects.rs
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use cssparser::Parser;
 use media_queries::CSSErrorReporterTest;
 use parsing::parse;
 use servo_url::ServoUrl;
 use style::parser::ParserContext;
 use style::properties::longhands::{self, perspective_origin, transform_origin};
-use style::stylesheets::Origin;
+use style::stylesheets::{CssRuleType, Origin};
 use style_traits::ToCss;
 
 #[test]
 fn test_clip() {
     use style::properties::longhands::clip;
 
     assert_roundtrip_with_context!(clip::parse, "auto");
     assert_roundtrip_with_context!(clip::parse, "rect(1px, 2px, 3px, 4px)");
@@ -34,17 +34,17 @@ fn test_clip() {
                                    "rect(auto auto auto auto)",
                                    "rect(auto, auto, auto, auto)");
 }
 
 #[test]
 fn test_longhands_parse_origin() {
     let url = ServoUrl::parse("http://localhost").unwrap();
     let reporter = CSSErrorReporterTest;
-    let context = ParserContext::new(Origin::Author, &url, &reporter);
+    let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
 
     let mut parser = Parser::new("1px some-rubbish");
     let parsed = longhands::parse_origin(&context, &mut parser);
     assert!(parsed.is_ok());
     assert_eq!(parser.is_exhausted(), false);
 
     let mut parser = Parser::new("1px 2px");
     let parsed = longhands::parse_origin(&context, &mut parser);
--- a/servo/tests/unit/style/parsing/font.rs
+++ b/servo/tests/unit/style/parsing/font.rs
@@ -4,17 +4,17 @@
 
 use cssparser::Parser;
 use media_queries::CSSErrorReporterTest;
 use servo_url::ServoUrl;
 use style::parser::ParserContext;
 use style::properties::longhands::{font_feature_settings, font_weight};
 use style::properties::longhands::font_feature_settings::computed_value;
 use style::properties::longhands::font_feature_settings::computed_value::FeatureTagValue;
-use style::stylesheets::Origin;
+use style::stylesheets::{CssRuleType, Origin};
 use style_traits::ToCss;
 
 #[test]
 fn font_feature_settings_should_parse_properly() {
     let normal = parse_longhand!(font_feature_settings, "normal");
     let normal_computed = computed_value::T::Normal;
     assert_eq!(normal, normal_computed);
 
@@ -49,17 +49,17 @@ fn font_feature_settings_should_parse_pr
     ]);
     assert_eq!(multiple, multiple_computed);
 }
 
 #[test]
 fn font_feature_settings_should_throw_on_bad_input() {
     let url = ServoUrl::parse("http://localhost").unwrap();
     let reporter = CSSErrorReporterTest;
-    let context = ParserContext::new(Origin::Author, &url, &reporter);
+    let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
 
     let mut empty = Parser::new("");
     assert!(font_feature_settings::parse(&context, &mut empty).is_err());
 
     let mut negative = Parser::new("\"abcd\" -1");
     assert!(font_feature_settings::parse(&context, &mut negative).is_err());
 
     let mut short_tag = Parser::new("\"abc\"");
@@ -100,17 +100,17 @@ fn font_language_override_should_parse_p
 }
 
 #[test]
 fn font_weight_keyword_should_preserve_keyword() {
     use style::properties::longhands::font_weight::SpecifiedValue;
 
     let url = ServoUrl::parse("http://localhost").unwrap();
     let reporter = CSSErrorReporterTest;
-    let context = ParserContext::new(Origin::Author, &url, &reporter);
+    let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
     let mut parser = Parser::new("normal");
     let result = font_weight::parse(&context, &mut parser);
     assert_eq!(result.unwrap(), SpecifiedValue::Normal);
 
     let mut parser = Parser::new("bold");
     let result = font_weight::parse(&context, &mut parser);
     assert_eq!(result.unwrap(), SpecifiedValue::Bold);
 }
--- a/servo/tests/unit/style/parsing/image.rs
+++ b/servo/tests/unit/style/parsing/image.rs
@@ -5,17 +5,17 @@
 use cssparser::Parser;
 use euclid::size::TypedSize2D;
 use media_queries::CSSErrorReporterTest;
 use std::f32::consts::PI;
 use style::font_metrics::ServoMetricsProvider;
 use style::media_queries::{Device, MediaType};
 use style::parser::ParserContext;
 use style::properties::ComputedValues;
-use style::stylesheets::Origin;
+use style::stylesheets::{CssRuleType, Origin};
 use style::values::computed;
 use style::values::computed::{Angle, Context, ToComputedValue};
 use style::values::specified;
 use style::values::specified::image::*;
 use style_traits::ToCss;
 
 #[test]
 fn test_linear_gradient() {
--- a/servo/tests/unit/style/parsing/inherited_box.rs
+++ b/servo/tests/unit/style/parsing/inherited_box.rs
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use cssparser::Parser;
 use media_queries::CSSErrorReporterTest;
 use style::parser::ParserContext;
-use style::stylesheets::Origin;
+use style::stylesheets::{CssRuleType, Origin};
 
 #[test]
 fn image_orientation_longhand_should_parse_properly() {
     use style::properties::longhands::image_orientation;
     use style::properties::longhands::image_orientation::SpecifiedValue;
     use style::values::specified::Angle;
 
     let from_image = parse_longhand!(image_orientation, "from-image");
--- a/servo/tests/unit/style/parsing/inherited_text.rs
+++ b/servo/tests/unit/style/parsing/inherited_text.rs
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use cssparser::Parser;
 use media_queries::CSSErrorReporterTest;
 use style::parser::ParserContext;
-use style::stylesheets::Origin;
+use style::stylesheets::{CssRuleType, Origin};
 
 #[test]
 fn negative_letter_spacing_should_parse_properly() {
     use style::properties::longhands::letter_spacing;
     use style::properties::longhands::letter_spacing::SpecifiedValue;
     use style::values::specified::length::{Length, NoCalcLength, FontRelativeLength};
 
     let negative_value = parse_longhand!(letter_spacing, "-0.5em");
@@ -107,17 +107,17 @@ fn webkit_text_stroke_shorthand_should_p
     use media_queries::CSSErrorReporterTest;
     use servo_url::ServoUrl;
     use style::properties::longhands::_webkit_text_stroke_color;
     use style::properties::longhands::_webkit_text_stroke_width;
     use style::properties::shorthands::_webkit_text_stroke;
 
     let url = ServoUrl::parse("http://localhost").unwrap();
     let reporter = CSSErrorReporterTest;
-    let context = ParserContext::new(Origin::Author, &url, &reporter);
+    let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
 
     let mut parser = Parser::new("thin red");
     let result = _webkit_text_stroke::parse_value(&context, &mut parser).unwrap();
     assert_eq!(result._webkit_text_stroke_color, parse_longhand!(_webkit_text_stroke_color, "red"));
     assert_eq!(result._webkit_text_stroke_width, parse_longhand!(_webkit_text_stroke_width, "thin"));
 
     // ensure its no longer sensitive to order
     let mut parser = Parser::new("red thin");
@@ -129,27 +129,27 @@ fn webkit_text_stroke_shorthand_should_p
 #[test]
 fn line_height_should_return_number_on_plain_zero() {
     use media_queries::CSSErrorReporterTest;
     use servo_url::ServoUrl;
     use style::properties::longhands::line_height;
 
     let url = ServoUrl::parse("http://localhost").unwrap();
     let reporter = CSSErrorReporterTest;
-    let context = ParserContext::new(Origin::Author, &url, &reporter);
+    let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
     let mut parser = Parser::new("0");
     let result = line_height::parse(&context, &mut parser);
     assert_eq!(result.unwrap(), parse_longhand!(line_height, "0"));
 }
 
 #[test]
 fn line_height_should_return_length_on_length_zero() {
     use media_queries::CSSErrorReporterTest;
     use servo_url::ServoUrl;
     use style::properties::longhands::line_height;
 
     let url = ServoUrl::parse("http://localhost").unwrap();
     let reporter = CSSErrorReporterTest;
-    let context = ParserContext::new(Origin::Author, &url, &reporter);
+    let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
     let mut parser = Parser::new("0px");
     let result = line_height::parse(&context, &mut parser);
     assert_eq!(result.unwrap(), parse_longhand!(line_height, "0px"));
 }
--- a/servo/tests/unit/style/parsing/length.rs
+++ b/servo/tests/unit/style/parsing/length.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/. */
 
 use cssparser::Parser;
 use media_queries::CSSErrorReporterTest;
 use parsing::parse;
 use style::parser::{Parse, ParserContext};
-use style::stylesheets::Origin;
+use style::stylesheets::{CssRuleType, Origin};
 use style::values::specified::length::Length;
 use style_traits::ToCss;
 
 #[test]
 fn test_calc() {
     assert!(parse(Length::parse, "calc(1px+ 2px)").is_err());
     assert!(parse(Length::parse, "calc( 1px + 2px )").is_ok());
     assert!(parse(Length::parse, "calc(1px + 2px )").is_ok());
--- a/servo/tests/unit/style/parsing/mask.rs
+++ b/servo/tests/unit/style/parsing/mask.rs
@@ -4,23 +4,23 @@
 
 use cssparser::Parser;
 use media_queries::CSSErrorReporterTest;
 use servo_url::ServoUrl;
 use style::parser::ParserContext;
 use style::properties::longhands::{mask_clip, mask_composite, mask_image, mask_mode};
 use style::properties::longhands::{mask_origin, mask_position_x, mask_position_y, mask_repeat, mask_size};
 use style::properties::shorthands::mask;
-use style::stylesheets::Origin;
+use style::stylesheets::{CssRuleType, Origin};
 
 #[test]
 fn mask_shorthand_should_parse_all_available_properties_when_specified() {
     let url = ServoUrl::parse("http://localhost").unwrap();
     let reporter = CSSErrorReporterTest;
-    let context = ParserContext::new(Origin::Author, &url, &reporter);
+    let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
     let mut parser = Parser::new("url(\"http://servo/test.png\") luminance 7px 4px / 70px 50px \
                                  repeat-x padding-box border-box subtract");
     let result = mask::parse_value(&context, &mut parser).unwrap();
 
     assert_eq!(result.mask_image, parse_longhand!(mask_image, "url(\"http://servo/test.png\")"));
     assert_eq!(result.mask_mode, parse_longhand!(mask_mode, "luminance"));
     assert_eq!(result.mask_position_x, parse_longhand!(mask_position_x, "7px"));
     assert_eq!(result.mask_position_y, parse_longhand!(mask_position_y, "4px"));
@@ -30,17 +30,17 @@ fn mask_shorthand_should_parse_all_avail
     assert_eq!(result.mask_clip, parse_longhand!(mask_clip, "border-box"));
     assert_eq!(result.mask_composite, parse_longhand!(mask_composite, "subtract"));
 }
 
 #[test]
 fn mask_shorthand_should_parse_when_some_fields_set() {
     let url = ServoUrl::parse("http://localhost").unwrap();
     let reporter = CSSErrorReporterTest;
-    let context = ParserContext::new(Origin::Author, &url, &reporter);
+    let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
     let mut parser = Parser::new("14px 40px repeat-y");
     let result = mask::parse_value(&context, &mut parser).unwrap();
 
     assert_eq!(result.mask_position_x, parse_longhand!(mask_position_x, "14px"));
     assert_eq!(result.mask_position_y, parse_longhand!(mask_position_y, "40px"));
     assert_eq!(result.mask_repeat, parse_longhand!(mask_repeat, "repeat-y"));
 
     let mut parser = Parser::new("url(\"http://servo/test.png\") repeat add");
@@ -60,17 +60,17 @@ fn mask_shorthand_should_parse_when_some
 
     assert_eq!(result.mask_image, parse_longhand!(mask_image, "url(\"http://servo/test.png\")"));
 }
 
 #[test]
 fn mask_shorthand_should_parse_position_and_size_correctly() {
     let url = ServoUrl::parse("http://localhost").unwrap();
     let reporter = CSSErrorReporterTest;
-    let context = ParserContext::new(Origin::Author, &url, &reporter);
+    let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
     let mut parser = Parser::new("7px 4px");
     let result = mask::parse_value(&context, &mut parser).unwrap();
 
     assert_eq!(result.mask_position_x, parse_longhand!(mask_position_x, "7px"));
     assert_eq!(result.mask_position_y, parse_longhand!(mask_position_y, "4px"));
 
     let mut parser = Parser::new("7px 4px / 30px 20px");
     let result = mask::parse_value(&context, &mut parser).unwrap();
@@ -85,17 +85,17 @@ fn mask_shorthand_should_parse_position_
     let mut parser = Parser::new("match-source repeat-x / 30px 20px");
     assert!(mask::parse_value(&context, &mut parser).is_err());
 }
 
 #[test]
 fn mask_shorthand_should_parse_origin_and_clip_correctly() {
     let url = ServoUrl::parse("http://localhost").unwrap();
     let reporter = CSSErrorReporterTest;
-    let context = ParserContext::new(Origin::Author, &url, &reporter);
+    let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
     let mut parser = Parser::new("padding-box content-box");
     let result = mask::parse_value(&context, &mut parser).unwrap();
 
     assert_eq!(result.mask_origin, parse_longhand!(mask_origin, "padding-box"));
     assert_eq!(result.mask_clip, parse_longhand!(mask_clip, "content-box"));
 
     let mut parser = Parser::new("padding-box padding-box");
     let result = mask::parse_value(&context, &mut parser).unwrap();
@@ -109,17 +109,17 @@ fn mask_shorthand_should_parse_origin_an
     assert_eq!(result.mask_origin, parse_longhand!(mask_origin, "padding-box"));
     assert_eq!(result.mask_clip, parse_longhand!(mask_clip, "padding-box"));
 }
 
 #[test]
 fn mask_shorthand_should_parse_mode_everywhere() {
     let url = ServoUrl::parse("http://localhost").unwrap();
     let reporter = CSSErrorReporterTest;
-    let context = ParserContext::new(Origin::Author, &url, &reporter);
+    let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
     let mut parser = Parser::new("luminance 7px 4px repeat-x padding-box");
     assert!(mask::parse_value(&context, &mut parser).is_ok());
 
     let mut parser = Parser::new("alpha");
     assert!(mask::parse_value(&context, &mut parser).is_ok());
 }
 
 #[test]
@@ -150,17 +150,17 @@ fn mask_repeat_should_parse_shorthand_co
 }
 
 #[test]
 fn mask_repeat_should_parse_longhand_correctly() {
     use style::properties::longhands::mask_repeat::single_value::{RepeatKeyword, SpecifiedValue};
 
     let url = ServoUrl::parse("http://localhost").unwrap();
     let reporter = CSSErrorReporterTest;
-    let context = ParserContext::new(Origin::Author, &url, &reporter);
+    let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
 
     // repeat-x is not available in longhand form.
     let mut parser = Parser::new("repeat-x no-repeat");
     assert!(mask_repeat::parse(&context, &mut parser).is_err());
 
     let mut parser = Parser::new("no-repeat repeat-x");
     assert!(mask_repeat::parse(&context, &mut parser).is_err());
 
--- a/servo/tests/unit/style/parsing/mod.rs
+++ b/servo/tests/unit/style/parsing/mod.rs
@@ -2,36 +2,36 @@
  * 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/. */
 
 //! Tests for parsing and serialization of values/properties
 
 use cssparser::Parser;
 use media_queries::CSSErrorReporterTest;
 use style::parser::ParserContext;
-use style::stylesheets::Origin;
+use style::stylesheets::{CssRuleType, Origin};
 
 fn parse<T, F: Fn(&ParserContext, &mut Parser) -> Result<T, ()>>(f: F, s: &str) -> Result<T, ()> {
     let url = ::servo_url::ServoUrl::parse("http://localhost").unwrap();
     let reporter = CSSErrorReporterTest;
-    let context = ParserContext::new(Origin::Author, &url, &reporter);
+    let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
     let mut parser = Parser::new(s);
     f(&context, &mut parser)
 }
 
 // This is a macro so that the file/line information
 // is preserved in the panic
 macro_rules! assert_roundtrip_with_context {
     ($fun:expr, $string:expr) => {
         assert_roundtrip_with_context!($fun, $string, $string);
     };
     ($fun:expr,$input:expr, $output:expr) => {
         let url = ::servo_url::ServoUrl::parse("http://localhost").unwrap();
         let reporter = CSSErrorReporterTest;
-        let context = ParserContext::new(Origin::Author, &url, &reporter);
+        let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
         let mut parser = Parser::new($input);
         let parsed = $fun(&context, &mut parser)
                      .expect(&format!("Failed to parse {}", $input));
         let serialized = ToCss::to_css_string(&parsed);
         assert_eq!(serialized, $output);
 
         let mut parser = Parser::new(&serialized);
         let re_parsed = $fun(&context, &mut parser)
@@ -59,29 +59,29 @@ macro_rules! assert_roundtrip {
         assert_eq!(serialized, re_serialized);
     }
 }
 
 macro_rules! assert_parser_exhausted {
     ($name:ident, $string:expr, $should_exhausted:expr) => {{
         let url = ::servo_url::ServoUrl::parse("http://localhost").unwrap();
         let reporter = CSSErrorReporterTest;
-        let context = ParserContext::new(Origin::Author, &url, &reporter);
+        let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
         let mut parser = Parser::new($string);
         let parsed = $name::parse(&context, &mut parser);
         assert_eq!(parsed.is_ok(), true);
         assert_eq!(parser.is_exhausted(), $should_exhausted);
     }}
 }
 
 macro_rules! parse_longhand {
     ($name:ident, $s:expr) => {{
         let url = ::servo_url::ServoUrl::parse("http://localhost").unwrap();
         let reporter = CSSErrorReporterTest;
-        let context = ParserContext::new(Origin::Author, &url, &reporter);
+        let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
         $name::parse(&context, &mut Parser::new($s)).unwrap()
     }};
 }
 
 mod animation;
 mod background;
 mod basic_shape;
 mod border;
--- a/servo/tests/unit/style/parsing/outline.rs
+++ b/servo/tests/unit/style/parsing/outline.rs
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use cssparser::Parser;
 use media_queries::CSSErrorReporterTest;
 use style::parser::ParserContext;
-use style::stylesheets::Origin;
+use style::stylesheets::{CssRuleType, Origin};
 use style_traits::ToCss;
 
 #[test]
 fn test_outline_style() {
     use style::properties::longhands::outline_style;
 
     assert_roundtrip_with_context!(outline_style::parse, r#"auto"#);
     assert_roundtrip_with_context!(outline_style::parse, r#"none"#);
@@ -24,15 +24,15 @@ fn test_outline_style() {
     assert_roundtrip_with_context!(outline_style::parse, r#"outset"#);
 
     {
         // The outline-style property accepts the same values as border-style,
         // except that 'hidden' is not a legal outline style.
 
         let url = ::servo_url::ServoUrl::parse("http://localhost").unwrap();
         let reporter = CSSErrorReporterTest;
-        let context = ParserContext::new(Origin::Author, &url, &reporter);
+        let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
         let mut parser = Parser::new(r#"hidden"#);
         let parsed = outline_style::parse(&context, &mut parser);
         assert!(parsed.is_err());
     };
 
 }
--- a/servo/tests/unit/style/parsing/position.rs
+++ b/servo/tests/unit/style/parsing/position.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/. */
 
 use cssparser::Parser;
 use media_queries::CSSErrorReporterTest;
 use parsing::parse;
 use style::parser::{Parse, ParserContext};
-use style::stylesheets::Origin;
+use style::stylesheets::{CssRuleType, Origin};
 use style::values::specified::position::*;
 use style_traits::ToCss;
 
 #[test]
 fn test_position() {
     // Serialization is not actually specced
     // though these are the values expected by basic-shape
     // https://github.com/w3c/csswg-drafts/issues/368
--- a/servo/tests/unit/style/parsing/selectors.rs
+++ b/servo/tests/unit/style/parsing/selectors.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/. */
 
 use cssparser::{Parser, ToCss};
 use media_queries::CSSErrorReporterTest;
 use selectors::parser::SelectorList;
 use style::parser::ParserContext;
 use style::selector_parser::{SelectorImpl, SelectorParser};
-use style::stylesheets::{Origin, Namespaces};
+use style::stylesheets::{CssRuleType, Origin, Namespaces};
 
 fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SelectorList<SelectorImpl>, ()> {
     let mut ns = Namespaces::default();
     ns.prefixes.insert("svg".into(), ns!(svg));
     let parser = SelectorParser {
         stylesheet_origin: Origin::UserAgent,
         namespaces: &ns,
     };
--- a/servo/tests/unit/style/parsing/text.rs
+++ b/servo/tests/unit/style/parsing/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/. */
 
 use cssparser::Parser;
 use media_queries::CSSErrorReporterTest;
 use parsing::parse;
 use style::parser::ParserContext;
-use style::stylesheets::Origin;
+use style::stylesheets::{CssRuleType, Origin};
 use style_traits::ToCss;
 
 #[test]
 fn initial_letter_should_be_parsed_correctly() {
     use style::properties::longhands::initial_letter;
 
     assert_roundtrip_with_context!(initial_letter::parse, "1.5");
     assert_roundtrip_with_context!(initial_letter::parse, "1.5 3");
--- a/servo/tests/unit/style/parsing/text_overflow.rs
+++ b/servo/tests/unit/style/parsing/text_overflow.rs
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use cssparser::Parser;
 use media_queries::CSSErrorReporterTest;
 use style::parser::ParserContext;
-use style::stylesheets::Origin;
+use style::stylesheets::{CssRuleType, Origin};
 use style_traits::ToCss;
 
 #[test]
 fn test_text_overflow() {
     use style::properties::longhands::text_overflow;
 
     assert_roundtrip_with_context!(text_overflow::parse, r#"clip"#);
     assert_roundtrip_with_context!(text_overflow::parse, r#"ellipsis"#);
--- a/servo/tests/unit/style/parsing/transition_timing_function.rs
+++ b/servo/tests/unit/style/parsing/transition_timing_function.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/. */
 
 use cssparser::Parser;
 use media_queries::CSSErrorReporterTest;
 use parsing::parse;
 use style::parser::ParserContext;
 use style::properties::longhands::transition_timing_function;
-use style::stylesheets::Origin;
+use style::stylesheets::{CssRuleType, Origin};
 use style_traits::ToCss;
 
 #[test]
 fn test_cubic_bezier() {
     assert_roundtrip_with_context!(transition_timing_function::parse, "cubic-bezier(0, 0, 0, 0)");
     assert_roundtrip_with_context!(transition_timing_function::parse, "cubic-bezier(0.25, 0, 0.5, 0)");
     assert_roundtrip_with_context!(transition_timing_function::parse, "cubic-bezier(1, 1, 1, 1)");
 
--- a/servo/tests/unit/style/parsing/ui.rs
+++ b/servo/tests/unit/style/parsing/ui.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/. */
 
 use cssparser::{Color, Parser, RGBA};
 use media_queries::CSSErrorReporterTest;
 use servo_url::ServoUrl;
 use style::parser::ParserContext;
-use style::stylesheets::Origin;
+use style::stylesheets::{CssRuleType, Origin};
 use style::values::{Auto, Either};
 use style::values::specified::CSSColor;
 use style_traits::ToCss;
 
 #[test]
 fn test_moz_user_select() {
     use style::properties::longhands::_moz_user_select;
 
@@ -23,17 +23,17 @@ fn test_moz_user_select() {
     assert_roundtrip_with_context!(_moz_user_select::parse, "toggle");
     assert_roundtrip_with_context!(_moz_user_select::parse, "tri-state");
     assert_roundtrip_with_context!(_moz_user_select::parse, "-moz-all");
     assert_roundtrip_with_context!(_moz_user_select::parse, "-moz-none");
     assert_roundtrip_with_context!(_moz_user_select::parse, "-moz-text");
 
     let url = ServoUrl::parse("http://localhost").unwrap();
     let reporter = CSSErrorReporterTest;
-    let context = ParserContext::new(Origin::Author, &url, &reporter);
+    let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
 
     let mut negative = Parser::new("potato");
     assert!(_moz_user_select::parse(&context, &mut negative).is_err());
 }
 
 #[test]
 fn test_caret_color() {
     use style::properties::longhands::caret_color;
--- a/servo/tests/unit/style/properties/background.rs
+++ b/servo/tests/unit/style/properties/background.rs
@@ -1,20 +1,20 @@
 /* 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 cssparser::Parser;
 use media_queries::CSSErrorReporterTest;
 use style::parser::ParserContext;
 use style::properties::longhands::background_size;
-use style::stylesheets::Origin;
+use style::stylesheets::{CssRuleType, Origin};
 
 #[test]
 fn background_size_should_reject_negative_values() {
     let url = ::servo_url::ServoUrl::parse("http://localhost").unwrap();
     let reporter = CSSErrorReporterTest;
-    let context = ParserContext::new(Origin::Author, &url, &reporter);
+    let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
 
     let parse_result = background_size::parse(&context, &mut Parser::new("-40% -40%"));
 
     assert_eq!(parse_result.is_err(), true);
 }
--- a/servo/tests/unit/style/properties/serialization.rs
+++ b/servo/tests/unit/style/properties/serialization.rs
@@ -16,19 +16,19 @@ use style::values::specified::{BorderSty
 use style::values::specified::{LengthOrPercentage, LengthOrPercentageOrAuto, LengthOrPercentageOrAutoOrContent};
 use style::values::specified::url::SpecifiedUrl;
 use style_traits::ToCss;
 use stylesheets::block_from;
 
 fn parse_declaration_block(css_properties: &str) -> PropertyDeclarationBlock {
     let url = ServoUrl::parse("http://localhost").unwrap();
     let reporter = CSSErrorReporterTest;
-    let context = ParserContext::new(Origin::Author, &url, &reporter);
+    let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
     let mut parser = Parser::new(css_properties);
-    parse_property_declaration_list(&context, &mut parser, CssRuleType::Style)
+    parse_property_declaration_list(&context, &mut parser)
 }
 
 #[test]
 fn property_declaration_block_should_serialize_correctly() {
     use style::properties::longhands::overflow_x::SpecifiedValue as OverflowXValue;
     use style::properties::longhands::overflow_y::SpecifiedValue as OverflowYContainer;
 
     let declarations = vec![
@@ -971,17 +971,17 @@ mod shorthand_serialization {
             use media_queries::CSSErrorReporterTest;
             use style::parser::ParserContext;
             use style::properties::longhands::transform;
             use style::stylesheets::Origin;
 
             let mut s = String::new();
             let url = ::servo_url::ServoUrl::parse("http://localhost").unwrap();
             let reporter = CSSErrorReporterTest;
-            let context = ParserContext::new(Origin::Author, &url, &reporter);
+            let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
 
             let parsed = transform::parse(&context, &mut Parser::new("none")).unwrap();
             let try_serialize = parsed.to_css(&mut s);
 
             assert_eq!(try_serialize.is_ok(), true);
             assert_eq!(s, "none");
         }
 
@@ -1035,17 +1035,17 @@ mod shorthand_serialization {
             use media_queries::CSSErrorReporterTest;
             use style::parser::ParserContext;
             use style::properties::longhands::quotes;
             use style::stylesheets::Origin;
 
             let mut s = String::new();
             let url = ::servo_url::ServoUrl::parse("http://localhost").unwrap();
             let reporter = CSSErrorReporterTest;
-            let context = ParserContext::new(Origin::Author, &url, &reporter);
+            let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
 
             let parsed = quotes::parse(&context, &mut Parser::new("none")).unwrap();
             let try_serialize = parsed.to_css(&mut s);
 
             assert_eq!(try_serialize.is_ok(), true);
             assert_eq!(s, "none");
         }
     }
--- a/servo/tests/unit/style/value.rs
+++ b/servo/tests/unit/style/value.rs
@@ -1,31 +1,37 @@
 /* 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 app_units::Au;
 use cssparser::Parser;
+use media_queries::CSSErrorReporterTest;
+use style::parser::ParserContext;
+use style::stylesheets::{CssRuleType, Origin};
 use style::values::HasViewportPercentage;
 use style::values::specified::{AbsoluteLength, ViewportPercentageLength, NoCalcLength};
 use style::values::specified::length::{CalcLengthOrPercentage, CalcUnit};
 
 #[test]
 fn length_has_viewport_percentage() {
     let l = NoCalcLength::ViewportPercentage(ViewportPercentageLength::Vw(100.));
     assert!(l.has_viewport_percentage());
     let l = NoCalcLength::Absolute(AbsoluteLength::Px(Au(100).to_f32_px()));
     assert!(!l.has_viewport_percentage());
 }
 
 #[test]
 fn calc_top_level_number_with_unit() {
     fn parse(text: &str, unit: CalcUnit) -> Result<CalcLengthOrPercentage, ()> {
+        let url = ::servo_url::ServoUrl::parse("http://localhost").unwrap();
+        let reporter = CSSErrorReporterTest;
+        let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style));
         let mut parser = Parser::new(text);
-        CalcLengthOrPercentage::parse(&mut parser, unit)
+        CalcLengthOrPercentage::parse(&context, &mut parser, unit)
     }
     assert_eq!(parse("1", CalcUnit::Length), Err(()));
     assert_eq!(parse("1", CalcUnit::LengthOrPercentage), Err(()));
     assert_eq!(parse("1", CalcUnit::Angle), Err(()));
     assert_eq!(parse("1", CalcUnit::Time), Err(()));
     assert_eq!(parse("1px  + 1", CalcUnit::Length), Err(()));
     assert_eq!(parse("1em  + 1", CalcUnit::Length), Err(()));
     assert_eq!(parse("1px  + 1", CalcUnit::LengthOrPercentage), Err(()));
--- a/servo/tests/unit/style/viewport.rs
+++ b/servo/tests/unit/style/viewport.rs
@@ -5,17 +5,17 @@
 use cssparser::Parser;
 use euclid::size::TypedSize2D;
 use media_queries::CSSErrorReporterTest;
 use servo_config::prefs::{PREFS, PrefValue};
 use servo_url::ServoUrl;
 use style::media_queries::{Device, MediaType};
 use style::parser::{Parse, ParserContext};
 use style::shared_lock::SharedRwLock;
-use style::stylesheets::{Stylesheet, Origin};
+use style::stylesheets::{CssRuleType, Stylesheet, Origin};
 use style::values::specified::LengthOrPercentageOrAuto::{self, Auto};
 use style::values::specified::NoCalcLength::{self, ViewportPercentage};
 use style::values::specified::ViewportPercentageLength::Vw;
 use style::viewport::*;
 use style_traits::PinchZoomFactor;
 use style_traits::viewport::*;
 
 macro_rules! stylesheet {
@@ -285,17 +285,17 @@ fn multiple_stylesheets_cascading() {
     assert_decl_eq!(&declarations[1], User, MinHeight: viewport_length!(200., px), !important);
     assert_decl_eq!(&declarations[2], Author, Zoom: Zoom::Number(3.), !important);
 }
 
 #[test]
 fn constrain_viewport() {
     let url = ServoUrl::parse("http://localhost").unwrap();
     let reporter = CSSErrorReporterTest;
-    let context = ParserContext::new(Origin::Author, &url, &reporter);
+    let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Viewport));
 
     macro_rules! from_css {
         ($css:expr) => {
             &ViewportRule::parse(&context, &mut Parser::new($css)).unwrap()
         }
     }
 
     let initial_viewport = TypedSize2D::new(800., 600.);