Bug 1329088 - SVG length parsing mode. r=emilio draft
authorJ. Ryan Stinnett <jryans@gmail.com>
Wed, 12 Apr 2017 17:40:48 +0800
changeset 562649 b7402ee4d5cb7840410428a593fe3d83341e4a64
parent 562648 4e9f177f0a836b97d8c4667524b946f3f53a274d
child 562650 e37825470b1c4a19b9c39b843cbb275c6c874223
push id54069
push userbmo:jryans@gmail.com
push dateFri, 14 Apr 2017 04:32:08 +0000
reviewersemilio
bugs1329088
milestone55.0a1
Bug 1329088 - SVG length parsing mode. r=emilio SVG allows non-zero lengths to be accepted and assumes they are in px. This adds this length parsing mode to Servo. MozReview-Commit-ID: Kxd3x64r9Ye
servo/components/style/keyframes.rs
servo/components/style/parser.rs
servo/components/style/properties/declaration_block.rs
servo/components/style/properties/properties.mako.rs
servo/components/style/stylesheets.rs
servo/components/style/values/specified/length.rs
servo/ports/geckolib/glue.rs
servo/tests/unit/style/parsing/length.rs
servo/tests/unit/style/parsing/mod.rs
servo/tests/unit/style/properties/mod.rs
servo/tests/unit/style/viewport.rs
--- a/servo/components/style/keyframes.rs
+++ b/servo/components/style/keyframes.rs
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Keyframes: https://drafts.csswg.org/css-animations/#keyframes
 
 #![deny(missing_docs)]
 
 use cssparser::{AtRuleParser, Parser, QualifiedRuleParser, RuleListParser};
 use cssparser::{DeclarationListParser, DeclarationParser, parse_one_rule};
-use parser::{ParserContext, log_css_error};
+use parser::{LengthMode, ParserContext, log_css_error};
 use properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock, PropertyId};
 use properties::{PropertyDeclarationId, LonghandId, ParsedDeclaration};
 use properties::LonghandIdSet;
 use properties::animated_properties::TransitionProperty;
 use properties::longhands::transition_timing_function::single_value::SpecifiedValue as SpecifiedTimingFunction;
 use shared_lock::{SharedRwLock, SharedRwLockReadGuard, Locked, ToCssWithGuard};
 use std::fmt;
 use std::sync::Arc;
@@ -124,17 +124,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,
-                                         Some(CssRuleType::Keyframe));
+                                         Some(CssRuleType::Keyframe),
+                                         LengthMode::Default);
         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)
     }
--- a/servo/components/style/parser.rs
+++ b/servo/components/style/parser.rs
@@ -6,60 +6,77 @@
 
 #![deny(missing_docs)]
 
 use cssparser::{Parser, SourcePosition, UnicodeRange};
 use error_reporting::ParseErrorReporter;
 use style_traits::OneOrMoreCommaSeparated;
 use stylesheets::{CssRuleType, Origin, UrlExtraData};
 
+/// The mode to use when parsing lengths.
+#[derive(PartialEq, Eq, Copy, Clone)]
+pub enum LengthMode {
+    /// In CSS, lengths must have units, except for zero values, where the unit can be omitted.
+    /// https://www.w3.org/TR/css3-values/#lengths
+    Default,
+    /// In SVG, a coordinate or length value without a unit identifier (e.g., "25") is assumed to be in user units (px).
+    /// https://www.w3.org/TR/SVG/coords.html#Units
+    SVG,
+}
+
 /// 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>,
+    /// The mode to use when parsing lengths.
+    pub length_mode: LengthMode,
 }
 
 impl<'a> ParserContext<'a> {
     /// Create a parser context.
     pub fn new(stylesheet_origin: Origin,
                url_data: &'a UrlExtraData,
                error_reporter: &'a ParseErrorReporter,
-               rule_type: Option<CssRuleType>)
+               rule_type: Option<CssRuleType>,
+               length_mode: LengthMode)
                -> ParserContext<'a> {
         ParserContext {
             stylesheet_origin: stylesheet_origin,
             url_data: url_data,
             error_reporter: error_reporter,
             rule_type: rule_type,
+            length_mode: length_mode,
         }
     }
 
     /// Create a parser context for on-the-fly parsing in CSSOM
     pub fn new_for_cssom(url_data: &'a UrlExtraData,
                          error_reporter: &'a ParseErrorReporter,
-                         rule_type: Option<CssRuleType>)
+                         rule_type: Option<CssRuleType>,
+                         length_mode: LengthMode)
                          -> ParserContext<'a> {
-        Self::new(Origin::Author, url_data, error_reporter, rule_type)
+        Self::new(Origin::Author, url_data, error_reporter, rule_type, length_mode)
     }
 
     /// 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)
+                  rule_type,
+                  context.length_mode)
     }
 
     /// 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.")
     }
 }
 
--- a/servo/components/style/properties/declaration_block.rs
+++ b/servo/components/style/properties/declaration_block.rs
@@ -4,17 +4,17 @@
 
 //! A property declaration block.
 
 #![deny(missing_docs)]
 
 use cssparser::{DeclarationListParser, parse_important};
 use cssparser::{Parser, AtRuleParser, DeclarationParser, Delimiter};
 use error_reporting::ParseErrorReporter;
-use parser::{ParserContext, log_css_error};
+use parser::{LengthMode, ParserContext, log_css_error};
 use std::fmt;
 use style_traits::ToCss;
 use stylesheets::{CssRuleType, Origin, UrlExtraData};
 use super::*;
 #[cfg(feature = "gecko")] use properties::animated_properties::AnimationValueMap;
 
 /// A declaration [importance][importance].
 ///
@@ -607,31 +607,39 @@ 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, Some(CssRuleType::Style));
+    let context = ParserContext::new(Origin::Author,
+                                     url_data,
+                                     error_reporter,
+                                     Some(CssRuleType::Style),
+                                     LengthMode::Default);
     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, Some(CssRuleType::Style));
+    let context = ParserContext::new(Origin::Author,
+                                     url_data,
+                                     error_reporter,
+                                     Some(CssRuleType::Style),
+                                     LengthMode::Default);
     Parser::new(input).parse_entirely(|parser| {
         ParsedDeclaration::parse(id, &context, parser)
             .map_err(|_| ())
     })
 }
 
 /// A struct to parse property declarations.
 struct PropertyDeclarationParser<'a, 'b: 'a> {
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -22,17 +22,17 @@ use error_reporting::ParseErrorReporter;
 #[cfg(feature = "servo")] use euclid::side_offsets::SideOffsets2D;
 use computed_values;
 use font_metrics::FontMetricsProvider;
 #[cfg(feature = "gecko")] use gecko_bindings::bindings;
 #[cfg(feature = "gecko")] use gecko_bindings::structs::{self, nsCSSPropertyID};
 #[cfg(feature = "servo")] use logical_geometry::{LogicalMargin, PhysicalSide};
 use logical_geometry::WritingMode;
 use media_queries::Device;
-use parser::{Parse, ParserContext};
+use parser::{LengthMode, Parse, ParserContext};
 use properties::animated_properties::TransitionProperty;
 #[cfg(feature = "servo")] use servo_config::prefs::PREFS;
 use shared_lock::StylesheetGuards;
 use style_traits::ToCss;
 use stylesheets::{CssRuleType, Origin, UrlExtraData};
 #[cfg(feature = "servo")] use values::Either;
 use values::{HasViewportPercentage, computed};
 use cascade_info::CascadeInfo;
@@ -325,17 +325,21 @@ 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, None);
+                    let context = ParserContext::new(Origin::Author,
+                                                     url_data,
+                                                     error_reporter,
+                                                     None,
+                                                     LengthMode::Default);
                     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:
--- a/servo/components/style/stylesheets.rs
+++ b/servo/components/style/stylesheets.rs
@@ -18,17 +18,17 @@ use font_face::parse_font_face_block;
 pub use gecko::rules::FontFaceRule;
 #[cfg(feature = "gecko")]
 use gecko_bindings::structs::URLExtraData;
 #[cfg(feature = "gecko")]
 use gecko_bindings::sugar::refptr::RefPtr;
 use keyframes::{Keyframe, parse_keyframe_list};
 use media_queries::{Device, MediaList, parse_media_query_list};
 use parking_lot::RwLock;
-use parser::{Parse, ParserContext, log_css_error};
+use parser::{LengthMode, Parse, ParserContext, log_css_error};
 use properties::{PropertyDeclarationBlock, parse_property_declaration_list};
 use selector_parser::{SelectorImpl, SelectorParser};
 use selectors::parser::SelectorList;
 #[cfg(feature = "servo")]
 use servo_config::prefs::PREFS;
 #[cfg(not(feature = "gecko"))]
 use servo_url::ServoUrl;
 use shared_lock::{SharedRwLock, Locked, ToCssWithGuard, SharedRwLockReadGuard};
@@ -416,17 +416,18 @@ impl CssRule {
                  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,
-                                         None);
+                                         None,
+                                         LengthMode::Default);
         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,
@@ -687,17 +688,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, None),
+            context: ParserContext::new(origin, url_data, error_reporter, None, LengthMode::Default),
             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() {
--- a/servo/components/style/values/specified/length.rs
+++ b/servo/components/style/values/specified/length.rs
@@ -5,17 +5,17 @@
 //! [Length values][length].
 //!
 //! [length]: https://drafts.csswg.org/css-values/#lengths
 
 use app_units::Au;
 use cssparser::{Parser, Token};
 use euclid::size::Size2D;
 use font_metrics::FontMetricsQueryResult;
-use parser::{Parse, ParserContext};
+use parser::{LengthMode, 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};
@@ -547,16 +547,22 @@ impl Length {
 
     #[inline]
     fn parse_internal(context: &ParserContext, input: &mut Parser, num_context: AllowedNumericType)
                       -> Result<Length, ()> {
         match try!(input.next()) {
             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::Number(ref value) if value.value != 0. => {
+                if context.length_mode != LengthMode::SVG {
+                    return Err(())
+                }
+                Ok(Length::NoCalc(NoCalcLength::Absolute(AbsoluteLength::Px(value.value))))
+            },
             Token::Function(ref name) if name.eq_ignore_ascii_case("calc") =>
                 input.parse_nested_block(|input| {
                     CalcLengthOrPercentage::parse_length(context, input, num_context)
                 }),
             _ => Err(())
         }
     }
 
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -996,17 +996,18 @@ 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, Some(CssRuleType::Style));
+    let context = ParserContext::new(Origin::Author, url_data, &reporter,
+                                     Some(CssRuleType::Style), LengthMode::Default);
 
     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()
         }
@@ -1018,17 +1019,18 @@ 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, Some(CssRuleType::Style));
+    let context = ParserContext::new(Origin::Author, url_data, &reporter,
+                                     Some(CssRuleType::Style), LengthMode::Default);
     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
     }
@@ -1239,17 +1241,17 @@ pub extern "C" fn Servo_MediaList_GetTex
 }
 
 #[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);
     let url_data = unsafe { dummy_url_data() };
     let reporter = StdoutErrorReporter;
-    let context = ParserContext::new_for_cssom(url_data, &reporter, Some(CssRuleType::Media));
+    let context = ParserContext::new_for_cssom(url_data, &reporter, Some(CssRuleType::Media), LengthMode::Default);
      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)
@@ -1269,29 +1271,29 @@ 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));
+    let context = ParserContext::new_for_cssom(url_data, &reporter, Some(CssRuleType::Media), LengthMode::Default);
     write_locked_arc(list, |list: &mut MediaList| {
         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() };
     let url_data = unsafe { dummy_url_data() };
     let reporter = StdoutErrorReporter;
-    let context = ParserContext::new_for_cssom(url_data, &reporter, Some(CssRuleType::Media));
+    let context = ParserContext::new_for_cssom(url_data, &reporter, Some(CssRuleType::Media), LengthMode::Default);
     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,
             _ => {
@@ -1632,17 +1634,18 @@ pub extern "C" fn Servo_DeclarationBlock
     use style::properties::longhands::background_image::SpecifiedValue as BackgroundImage;
     use style::properties::longhands::background_image::single_value::SpecifiedValue as SingleBackgroundImage;
     use style::values::specified::image::Image;
     use style::values::specified::url::SpecifiedUrl;
 
     let url_data = unsafe { RefPtr::from_ptr_ref(&raw_extra_data) };
     let string = unsafe { (*value).to_string() };
     let error_reporter = StdoutErrorReporter;
-    let context = ParserContext::new(Origin::Author, url_data, &error_reporter, Some(CssRuleType::Style));
+    let context = ParserContext::new(Origin::Author, url_data, &error_reporter,
+                                     Some(CssRuleType::Style), LengthMode::Default);
     if let Ok(url) = SpecifiedUrl::parse_from_string(string.into(), &context) {
         let decl = PropertyDeclaration::BackgroundImage(BackgroundImage(
             vec![SingleBackgroundImage(
                 Some(Image::Url(url))
             )]
         ));
         write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
             decls.push(decl, Importance::Normal);
@@ -1681,17 +1684,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, Some(CssRuleType::Style));
+        let context = ParserContext::new_for_cssom(url_data, &reporter, Some(CssRuleType::Style), LengthMode::Default);
         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/length.rs
+++ b/servo/tests/unit/style/parsing/length.rs
@@ -1,18 +1,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use cssparser::Parser;
 use media_queries::CSSErrorReporterTest;
 use parsing::parse;
-use style::parser::{Parse, ParserContext};
+use style::parser::{LengthMode, Parse, ParserContext};
 use style::stylesheets::{CssRuleType, Origin};
-use style::values::specified::length::Length;
+use style::values::specified::length::{AbsoluteLength, Length, NoCalcLength};
 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());
     assert!(parse(Length::parse, "calc( 1px + 2px)").is_ok());
@@ -23,8 +23,24 @@ fn test_length_literals() {
     assert_roundtrip_with_context!(Length::parse, "0.33px", "0.33px");
     assert_roundtrip_with_context!(Length::parse, "0.33in", "0.33in");
     assert_roundtrip_with_context!(Length::parse, "0.33cm", "0.33cm");
     assert_roundtrip_with_context!(Length::parse, "0.33mm", "0.33mm");
     assert_roundtrip_with_context!(Length::parse, "0.33q", "0.33q");
     assert_roundtrip_with_context!(Length::parse, "0.33pt", "0.33pt");
     assert_roundtrip_with_context!(Length::parse, "0.33pc", "0.33pc");
 }
+
+#[test]
+fn test_length_modes() {
+    // In default length mode, non-zero lengths must have a unit.
+    assert!(parse(Length::parse, "1").is_err());
+
+    // In SVG length mode, non-zero lengths are assumed to be px.
+    let url = ::servo_url::ServoUrl::parse("http://localhost").unwrap();
+    let reporter = CSSErrorReporterTest;
+    let context = ParserContext::new(Origin::Author, &url, &reporter,
+                                     Some(CssRuleType::Style), LengthMode::SVG);
+    let mut parser = Parser::new("1");
+    let result = Length::parse(&context, &mut parser);
+    assert!(result.is_ok());
+    assert_eq!(result.unwrap(), Length::NoCalc(NoCalcLength::Absolute(AbsoluteLength::Px(1.))));
+}
\ No newline at end of file
--- a/servo/tests/unit/style/parsing/mod.rs
+++ b/servo/tests/unit/style/parsing/mod.rs
@@ -1,23 +1,23 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Tests for parsing and serialization of values/properties
 
 use cssparser::Parser;
 use media_queries::CSSErrorReporterTest;
-use style::parser::ParserContext;
+use style::parser::{LengthMode, ParserContext};
 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, Some(CssRuleType::Style));
+    let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style), LengthMode::Default);
     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) => {
--- a/servo/tests/unit/style/properties/mod.rs
+++ b/servo/tests/unit/style/properties/mod.rs
@@ -1,21 +1,21 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use cssparser::Parser;
 use media_queries::CSSErrorReporterTest;
-use style::parser::ParserContext;
+use style::parser::{LengthMode, ParserContext};
 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, Some(CssRuleType::Style));
+    let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Style), LengthMode::Default);
     let mut parser = Parser::new(s);
     f(&context, &mut parser)
 }
 
 macro_rules! assert_roundtrip_with_context {
     ($fun:expr, $string:expr) => {
         assert_roundtrip_with_context!($fun, $string, $string);
     };
--- a/servo/tests/unit/style/viewport.rs
+++ b/servo/tests/unit/style/viewport.rs
@@ -4,17 +4,17 @@
 
 use cssparser::Parser;
 use euclid::size::TypedSize2D;
 use media_queries::CSSErrorReporterTest;
 use servo_config::prefs::{PREFS, PrefValue};
 use servo_url::ServoUrl;
 use std::sync::Arc;
 use style::media_queries::{Device, MediaList, MediaType};
-use style::parser::{Parse, ParserContext};
+use style::parser::{LengthMode, Parse, ParserContext};
 use style::shared_lock::SharedRwLock;
 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::*;
@@ -286,17 +286,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, Some(CssRuleType::Viewport));
+    let context = ParserContext::new(Origin::Author, &url, &reporter, Some(CssRuleType::Viewport), LengthMode::Default);
 
     macro_rules! from_css {
         ($css:expr) => {
             &ViewportRule::parse(&context, &mut Parser::new($css)).unwrap()
         }
     }
 
     let initial_viewport = TypedSize2D::new(800., 600.);