Bug 1565562: Media Query - Enable single <number> and <number>/<number> for <aspect-ratio>. r=emilio
authorJoel Olsson <joel_1st@hotmail.com>
Thu, 29 Aug 2019 10:48:52 +0000
changeset 551117 f95ee35992771a820e9ca9ccd372fd5111587b17
parent 551116 2fa81d89b660198144d8c2f98c7f5043351d2766
child 551118 e36be9c42ef1f555859514c71f03471498817e01
push id11865
push userbtara@mozilla.com
push dateMon, 02 Sep 2019 08:54:37 +0000
treeherdermozilla-beta@37f59c4671b3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
bugs1565562
milestone70.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
Bug 1565562: Media Query - Enable single <number> and <number>/<number> for <aspect-ratio>. r=emilio Differential Revision: https://phabricator.services.mozilla.com/D41557
layout/style/test/test_css_parse_error_smoketest.html
layout/style/test/test_media_queries.html
modules/libpref/init/StaticPrefList.yaml
servo/components/style/gecko/media_features.rs
servo/components/style/media_queries/media_feature.rs
servo/components/style/media_queries/media_feature_expression.rs
testing/web-platform/meta/css/mediaqueries/aspect-ratio-005.html.ini
testing/web-platform/meta/css/mediaqueries/aspect-ratio-006.html.ini
testing/web-platform/tests/css/mediaqueries/aspect-ratio-001.html
testing/web-platform/tests/css/mediaqueries/aspect-ratio-003.html
testing/web-platform/tests/css/mediaqueries/aspect-ratio-005.html
testing/web-platform/tests/css/mediaqueries/aspect-ratio-006.html
testing/web-platform/tests/css/mediaqueries/test_media_queries.html
--- a/layout/style/test/test_css_parse_error_smoketest.html
+++ b/layout/style/test/test_css_parse_error_smoketest.html
@@ -98,17 +98,20 @@
       error: "Found invalid value for media feature.",
     }, {
       css: "@media (min-resolution: 2) {}",
       error: "Found invalid value for media feature.",
     }, {
       css: "@media (min-monochrome: 1.1) {}",
       error: "Found invalid value for media feature.",
     }, {
-      css: "@media (min-aspect-ratio: 1) {}",
+      css: "@media (min-aspect-ratio: 1 invalid) {}",
+      error: "Found invalid value for media feature.",
+    }, {
+      css: "@media (min-aspect-ratio: 1 / invalid) {}",
       error: "Found invalid value for media feature.",
     }, {
       css: "@media (orientation: invalid-orientation-value) {}",
       error: "Found invalid value for media feature.",
     }, {
       css: "a, .b, #c { unknown: invalid; }",
       error: "Unknown property ‘unknown’.  Declaration dropped.",
       cssSelectors: "a, .b, #c"
--- a/layout/style/test/test_media_queries.html
+++ b/layout/style/test/test_media_queries.html
@@ -506,27 +506,46 @@ function run() {
 
   features = [ "max-aspect-ratio", "device-aspect-ratio" ];
   for (i in features) {
     feature = features[i];
     expression_should_be_parseable(feature + ": 1/1");
     expression_should_be_parseable(feature + ": 1  /1");
     expression_should_be_parseable(feature + ": 1  / \t\n1");
     expression_should_be_parseable(feature + ": 1/\r1");
-    expression_should_not_be_parseable(feature + ": 1");
-    expression_should_not_be_parseable(feature + ": 0.5");
-    expression_should_not_be_parseable(feature + ": 1.0/1");
-    expression_should_not_be_parseable(feature + ": 1/1.0");
-    expression_should_not_be_parseable(feature + ": 1.0/1.0");
+    if (SpecialPowers.getBoolPref('layout.css.aspect-ratio-number.enabled')) {
+      expression_should_be_parseable(feature + ": 1");
+      expression_should_be_parseable(feature + ": 0.5");
+      expression_should_be_parseable(feature + ": 1.0/1");
+      expression_should_be_parseable(feature + ": 1/1.0");
+      expression_should_be_parseable(feature + ": 1.0/1.0");
+      expression_should_be_parseable(feature + ": 1.5/1.2");
+      expression_should_be_parseable(feature + ": 1.5");
+      expression_should_be_parseable(feature + ": calc(1.2 * 1.3)");
+      expression_should_be_parseable(feature + ": 1.1/calc(2.2 * 2.3)");
+      expression_should_be_parseable(feature + ": calc(1.2 * 1.3)/2.2");
+      expression_should_be_parseable(feature + ": calc(1.2 * 1.3)/calc(2.2 * 2.3)");
+    } else {
+      expression_should_not_be_parseable(feature + ": 1");
+      expression_should_not_be_parseable(feature + ": 0.5");
+      expression_should_not_be_parseable(feature + ": 1.0/1");
+      expression_should_not_be_parseable(feature + ": 1/1.0");
+      expression_should_not_be_parseable(feature + ": 1.0/1.0");
+    }
     expression_should_not_be_parseable(feature + ": 0/1");
     expression_should_not_be_parseable(feature + ": 1/0");
     expression_should_not_be_parseable(feature + ": 0/0");
     expression_should_not_be_parseable(feature + ": -1/1");
     expression_should_not_be_parseable(feature + ": 1/-1");
     expression_should_not_be_parseable(feature + ": -1/-1");
+    expression_should_not_be_parseable(feature + ": -1/-1");
+    expression_should_not_be_parseable(feature + ": -1/-1");
+    expression_should_not_be_parseable(feature + ": invalid");
+    expression_should_not_be_parseable(feature + ": 1 / invalid");
+    expression_should_not_be_parseable(feature + ": 1 invalid");
   }
 
   var is_monochrome = query_applies("all and (min-monochrome: 1)");
   test_serialization("all and (min-monochrome: 1)", true, is_monochrome);
   var is_color = query_applies("all and (min-color: 1)");
   test_serialization("all and (min-color: 1)", true, is_color);
   isnot(is_monochrome, is_color, "should be either monochrome or color");
 
--- a/modules/libpref/init/StaticPrefList.yaml
+++ b/modules/libpref/init/StaticPrefList.yaml
@@ -4388,16 +4388,24 @@
   value: 4096
   mirror: always
 
 - name: layout.animation.prerender.absolute-limit-y
   type: RelaxedAtomicUint32
   value: 4096
   mirror: always
 
+# Allow <number> and <number>/<number> both for <aspect-ratio>
+# https://github.com/w3c/csswg-drafts/issues/3757
+- name: layout.css.aspect-ratio-number.enabled
+  type: RelaxedAtomicBool
+  value: false
+  mirror: always
+  rust: true
+
 # Is the codepath for using cached scrollbar styles enabled?
 - name: layout.css.cached-scrollbar-styles.enabled
   type: bool
   value: true
   mirror: always
 
 # Is path() supported in clip-path?
 - name: layout.css.clip-path-path.enabled
--- a/servo/components/style/gecko/media_features.rs
+++ b/servo/components/style/gecko/media_features.rs
@@ -99,17 +99,17 @@ where
     F: FnOnce(&Device) -> Size2D<Au>,
 {
     let query_value = match query_value {
         Some(v) => v,
         None => return true,
     };
 
     let size = get_size(device);
-    let value = AspectRatio(size.width.0 as u32, size.height.0 as u32);
+    let value = AspectRatio(size.width.0 as f32, size.height.0 as f32);
     RangeOrOperator::evaluate_with_query_value(range_or_operator, query_value, value)
 }
 
 /// https://drafts.csswg.org/mediaqueries-4/#aspect-ratio
 fn eval_aspect_ratio(
     device: &Device,
     query_value: Option<AspectRatio>,
     range_or_operator: Option<RangeOrOperator>,
@@ -554,17 +554,17 @@ lazy_static! {
             atom!("height"),
             AllowsRanges::Yes,
             Evaluator::Length(eval_height),
             ParsingRequirements::empty(),
         ),
         feature!(
             atom!("aspect-ratio"),
             AllowsRanges::Yes,
-            Evaluator::IntRatio(eval_aspect_ratio),
+            Evaluator::NumberRatio(eval_aspect_ratio),
             ParsingRequirements::empty(),
         ),
         feature!(
             atom!("orientation"),
             AllowsRanges::No,
             keyword_evaluator!(eval_orientation, Orientation),
             ParsingRequirements::empty(),
         ),
@@ -578,17 +578,17 @@ lazy_static! {
             atom!("device-height"),
             AllowsRanges::Yes,
             Evaluator::Length(eval_device_height),
             ParsingRequirements::empty(),
         ),
         feature!(
             atom!("device-aspect-ratio"),
             AllowsRanges::Yes,
-            Evaluator::IntRatio(eval_device_aspect_ratio),
+            Evaluator::NumberRatio(eval_device_aspect_ratio),
             ParsingRequirements::empty(),
         ),
         feature!(
             atom!("-moz-device-orientation"),
             AllowsRanges::No,
             keyword_evaluator!(eval_device_orientation, Orientation),
             ParsingRequirements::empty(),
         ),
--- a/servo/components/style/media_queries/media_feature.rs
+++ b/servo/components/style/media_queries/media_feature.rs
@@ -39,18 +39,18 @@ pub type KeywordParser = for<'a, 'i, 't>
 ///
 /// This determines the kind of values that get parsed, too.
 #[allow(missing_docs)]
 pub enum Evaluator {
     Length(MediaFeatureEvaluator<CSSPixelLength>),
     Integer(MediaFeatureEvaluator<u32>),
     Float(MediaFeatureEvaluator<f32>),
     BoolInteger(MediaFeatureEvaluator<bool>),
-    /// An integer ratio, such as the one from device-pixel-ratio.
-    IntRatio(MediaFeatureEvaluator<AspectRatio>),
+    /// A non-negative number ratio, such as the one from device-pixel-ratio.
+    NumberRatio(MediaFeatureEvaluator<AspectRatio>),
     /// A resolution.
     Resolution(MediaFeatureEvaluator<Resolution>),
     /// A keyword value.
     Enumerated {
         /// The parser to get a discriminant given a string.
         parser: KeywordParser,
         /// The serializer to get a string from a discriminant.
         ///
--- a/servo/components/style/media_queries/media_feature_expression.rs
+++ b/servo/components/style/media_queries/media_feature_expression.rs
@@ -11,44 +11,44 @@ use super::Device;
 use crate::context::QuirksMode;
 #[cfg(feature = "gecko")]
 use crate::gecko::media_features::MEDIA_FEATURES;
 use crate::parser::{Parse, ParserContext};
 #[cfg(feature = "servo")]
 use crate::servo::media_queries::MEDIA_FEATURES;
 use crate::str::{starts_with_ignore_ascii_case, string_as_ascii_lowercase};
 use crate::values::computed::{self, ToComputedValue};
-use crate::values::specified::{Integer, Length, Number, Resolution};
+use crate::values::specified::{Integer, NonNegativeNumber, Length, Number, Resolution};
 use crate::values::{serialize_atom_identifier, CSSFloat};
 use crate::{Atom, Zero};
 use cssparser::{Parser, Token};
 use std::cmp::{Ordering, PartialOrd};
 use std::fmt::{self, Write};
 use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
 
 /// An aspect ratio, with a numerator and denominator.
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]
-pub struct AspectRatio(pub u32, pub u32);
+#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToShmem)]
+pub struct AspectRatio(pub CSSFloat, pub CSSFloat);
 
 impl ToCss for AspectRatio {
     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
     where
         W: fmt::Write,
     {
         self.0.to_css(dest)?;
         dest.write_char('/')?;
         self.1.to_css(dest)
     }
 }
 
 impl PartialOrd for AspectRatio {
     fn partial_cmp(&self, other: &AspectRatio) -> Option<Ordering> {
-        u64::partial_cmp(
-            &(self.0 as u64 * other.1 as u64),
-            &(self.1 as u64 * other.0 as u64),
+        f64::partial_cmp(
+            &(self.0 as f64 * other.1 as f64),
+            &(self.1 as f64 * other.0 as f64),
         )
     }
 }
 
 /// The kind of matching that should be performed on a media feature value.
 #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]
 pub enum Range {
     /// At least the specified value.
@@ -424,18 +424,18 @@ impl MediaFeatureExpression {
                     })
                 });
                 eval(device, computed, self.range_or_operator)
             },
             Evaluator::Integer(eval) => {
                 eval(device, expect!(Integer).cloned(), self.range_or_operator)
             },
             Evaluator::Float(eval) => eval(device, expect!(Float).cloned(), self.range_or_operator),
-            Evaluator::IntRatio(eval) => {
-                eval(device, expect!(IntRatio).cloned(), self.range_or_operator)
+            Evaluator::NumberRatio(eval) => {
+                eval(device, expect!(NumberRatio).cloned(), self.range_or_operator)
             },
             Evaluator::Resolution(eval) => {
                 let computed = expect!(Resolution).map(|specified| {
                     computed::Context::for_media_query_evaluation(device, quirks_mode, |context| {
                         specified.to_computed_value(context)
                     })
                 });
                 eval(device, computed, self.range_or_operator)
@@ -451,34 +451,34 @@ impl MediaFeatureExpression {
             ),
         }
     }
 }
 
 /// A value found or expected in a media expression.
 ///
 /// FIXME(emilio): How should calc() serialize in the Number / Integer /
-/// BoolInteger / IntRatio case, as computed or as specified value?
+/// BoolInteger / NumberRatio case, as computed or as specified value?
 ///
 /// If the first, this would need to store the relevant values.
 ///
 /// See: https://github.com/w3c/csswg-drafts/issues/1968
 #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
 pub enum MediaExpressionValue {
     /// A length.
     Length(Length),
     /// A (non-negative) integer.
     Integer(u32),
     /// A floating point value.
     Float(CSSFloat),
     /// A boolean value, specified as an integer (i.e., either 0 or 1).
     BoolInteger(bool),
-    /// Two integers separated by '/', with optional whitespace on either side
-    /// of the '/'.
-    IntRatio(AspectRatio),
+    /// A single non-negative number or two non-negative numbers separated by '/',
+    /// with optional whitespace on either side of the '/'.
+    NumberRatio(AspectRatio),
     /// A resolution.
     Resolution(Resolution),
     /// An enumerated value, defined by the variant keyword table in the
     /// feature's `mData` member.
     Enumerated(KeywordDiscriminant),
     /// An identifier.
     Ident(Atom),
 }
@@ -488,17 +488,17 @@ impl MediaExpressionValue {
     where
         W: fmt::Write,
     {
         match *self {
             MediaExpressionValue::Length(ref l) => l.to_css(dest),
             MediaExpressionValue::Integer(v) => v.to_css(dest),
             MediaExpressionValue::Float(v) => v.to_css(dest),
             MediaExpressionValue::BoolInteger(v) => dest.write_str(if v { "1" } else { "0" }),
-            MediaExpressionValue::IntRatio(ratio) => ratio.to_css(dest),
+            MediaExpressionValue::NumberRatio(ratio) => ratio.to_css(dest),
             MediaExpressionValue::Resolution(ref r) => r.to_css(dest),
             MediaExpressionValue::Ident(ref ident) => serialize_atom_identifier(ident, dest),
             MediaExpressionValue::Enumerated(value) => match for_expr.feature().evaluator {
                 Evaluator::Enumerated { serializer, .. } => dest.write_str(&*serializer(value)),
                 _ => unreachable!(),
             },
         }
     }
@@ -524,21 +524,30 @@ impl MediaExpressionValue {
                     return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
                 }
                 MediaExpressionValue::BoolInteger(value == 1)
             },
             Evaluator::Float(..) => {
                 let number = Number::parse(context, input)?;
                 MediaExpressionValue::Float(number.get())
             },
-            Evaluator::IntRatio(..) => {
-                let a = Integer::parse_positive(context, input)?;
-                input.expect_delim('/')?;
-                let b = Integer::parse_positive(context, input)?;
-                MediaExpressionValue::IntRatio(AspectRatio(a.value() as u32, b.value() as u32))
+            Evaluator::NumberRatio(..) => {
+                if static_prefs::pref!("layout.css.aspect-ratio-number.enabled") {
+                    let a = NonNegativeNumber::parse(context, input)?.0.get();
+                    let b = match input.try_parse(|input| input.expect_delim('/')) {
+                        Ok(()) => NonNegativeNumber::parse(context, input)?.0.get(),
+                        _ => 1.0,
+                    };
+                    MediaExpressionValue::NumberRatio(AspectRatio(a, b))
+                } else {
+                    let a = Integer::parse_positive(context, input)?;
+                    input.expect_delim('/')?;
+                    let b = Integer::parse_positive(context, input)?;
+                    MediaExpressionValue::NumberRatio(AspectRatio(a.value() as CSSFloat, b.value() as CSSFloat))
+                }
             },
             Evaluator::Resolution(..) => {
                 MediaExpressionValue::Resolution(Resolution::parse(context, input)?)
             },
             Evaluator::Enumerated { parser, .. } => {
                 MediaExpressionValue::Enumerated(parser(context, input)?)
             },
             Evaluator::Ident(..) => {
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/mediaqueries/aspect-ratio-005.html.ini
@@ -0,0 +1,2 @@
+[aspect-ratio-005.html]
+  prefs: [layout.css.aspect-ratio-number.enabled:true]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/mediaqueries/aspect-ratio-006.html.ini
@@ -0,0 +1,2 @@
+[aspect-ratio-006.html]
+  prefs: [layout.css.aspect-ratio-number.enabled:true]
--- a/testing/web-platform/tests/css/mediaqueries/aspect-ratio-001.html
+++ b/testing/web-platform/tests/css/mediaqueries/aspect-ratio-001.html
@@ -2,17 +2,18 @@
 <meta charset="utf-8">
 <title>CSS Media Queries Test: min-aspect-ratio - 59/79 ('aspect-ratio' property with prefix 'min')</title>
 <link rel="author" title="Intel" href="http://www.intel.com/">
 <link name="author" title="Xin Liu" href="mailto:xinx.liu@intel.com">
 <link rel="help" title="4.6. aspect-ratio" href="http://www.w3.org/TR/css3-mediaqueries/#aspect-ratio">
 <link rel="match" href="../reference/ref-filled-green-100px-square.xht">
 <meta name="flags" content="">
 <meta name="assert" content="The 'aspect-ratio' property with prefix 'min' set '59/79' means that the minimum of ratio is '59/79',
-only and only if the value of the 'width' to the value of the 'height' is greater than value of 'min-aspect-ratio', the style sheet will be applied.">
+only and only if the value of the 'width' to the value of the 'height' is greater than value of 'min-aspect-ratio', the style sheet will be applied.
+The test runner will run this test in a 800/600 viewport (https://github.com/web-platform-tests/wpt/pull/12695).">
 <style>
   div {
     background-color: red;
     height: 100px;
     width: 100px;
   }
   @media screen and (min-aspect-ratio: 59/79) {
     div {
--- a/testing/web-platform/tests/css/mediaqueries/aspect-ratio-003.html
+++ b/testing/web-platform/tests/css/mediaqueries/aspect-ratio-003.html
@@ -1,17 +1,19 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>CSS Media Queries Test: max-aspect-ratio - 1280/720 ('aspect-ratio' property with prefix 'max')</title>
 <link rel="author" title="Intel" href="http://www.intel.com/">
 <link name="author" title="Xin Liu" href="mailto:xinx.liu@intel.com">
 <link rel="help" title="4.6. aspect-ratio" href="http://www.w3.org/TR/css3-mediaqueries/#aspect-ratio">
 <link rel="match" href="../reference/ref-filled-green-100px-square.xht">
 <meta name="flags" content="">
-<meta name="assert" content="The 'aspect-ratio' property with prefix 'max' set '1280/720' means that the maximum of ratio is '1280/720', only and only if the value of the 'width' to the value of the 'height' is lower than value of 'max-aspect-ratio', the style sheet will be applied.">
+<meta name="assert" content="The 'aspect-ratio' property with prefix 'max' set '1280/720' means that the maximum of ratio is '1280/720',
+only and only if the value of the 'width' to the value of the 'height' is lower than value of 'max-aspect-ratio', the style sheet will be applied.
+The test runner will run this test in a 800/600 viewport (https://github.com/web-platform-tests/wpt/pull/12695)">
 <style>
   div {
     background-color: red;
     height: 100px;
     width: 100px;
   }
   @media screen and (max-aspect-ratio: 1280/720) {
     div {
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/mediaqueries/aspect-ratio-005.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Media Queries Test: max-aspect-ratio - 1280.1/720.01 ('aspect-ratio' property with prefix 'max')</title>
+<link rel="author" title="Joel Olsson" href="mailto:joel_1st@hotmail.com">
+<link rel="help" title="aspect-ratio" href="https://drafts.csswg.org/mediaqueries-4/#aspect-ratio">
+<link rel="match" href="../reference/ref-filled-green-100px-square.xht">
+<meta name="flags" content="">
+<meta name="assert" content="The 'aspect-ratio' property with prefix 'max' set 1280.1/720.01. Only and only if the value of the 'width'
+to the value of the 'height' is lower than value of 'max-aspect-ratio', the style sheet will be applied.
+The test runner will run this test in a 800/600 viewport (https://github.com/web-platform-tests/wpt/pull/12695)">
+<style>
+  div {
+    background-color: red;
+    height: 100px;
+    width: 100px;
+  }
+  @media screen and (max-aspect-ratio: 1280.1/720.01) {
+    div {
+      background-color: green;
+    }
+  }
+</style>
+<body>
+  <p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+  <div></div>
+</body>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/mediaqueries/aspect-ratio-006.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Media Queries Test: min-aspect-ratio - 0.7 ('aspect-ratio' property with prefix 'min')</title>
+<link rel="author" title="Joel Olsson" href="mailto:joel_1st@hotmail.com">
+<link rel="help" title="aspect-ratio" href="https://drafts.csswg.org/mediaqueries-4/#aspect-ratio">
+<link rel="match" href="../reference/ref-filled-green-100px-square.xht">
+<meta name="flags" content="">
+<meta name="assert" content="The 'aspect-ratio' property with prefix 'min' set '0.7' means that the minimum of ratio is '0.7',
+only and only if the value of the 'width' to the value of the 'height' is greater than value of 'min-aspect-ratio', the style sheet will be applied.
+The test runner will run this test in a 800/600 viewport (https://github.com/web-platform-tests/wpt/pull/12695)">
+<style>
+  div {
+    background-color: red;
+    height: 100px;
+    width: 100px;
+  }
+  @media screen and (min-aspect-ratio: 0.2) {
+    div {
+      background-color: green;
+    }
+  }
+</style>
+<body>
+  <p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+  <div></div>
+</body>
--- a/testing/web-platform/tests/css/mediaqueries/test_media_queries.html
+++ b/testing/web-platform/tests/css/mediaqueries/test_media_queries.html
@@ -313,16 +313,21 @@ function run() {
       expression_should_not_be_parseable(feature + ": 1/1.0");
       expression_should_not_be_parseable(feature + ": 1.0/1.0");
       expression_should_not_be_parseable(feature + ": 0/1");
       expression_should_not_be_parseable(feature + ": 1/0");
       expression_should_not_be_parseable(feature + ": 0/0");
       expression_should_not_be_parseable(feature + ": -1/1");
       expression_should_not_be_parseable(feature + ": 1/-1");
       expression_should_not_be_parseable(feature + ": -1/-1");
+      expression_should_not_be_parseable(feature + ": -1/-1");
+      expression_should_not_be_parseable(feature + ": -1/-1");
+      expression_should_not_be_parseable(feature + ": invalid");
+      expression_should_not_be_parseable(feature + ": 1 / invalid");
+      expression_should_not_be_parseable(feature + ": 1 invalid");
     }
 
     var is_monochrome = query_applies("all and (min-monochrome: 1)");
     var is_color = query_applies("all and (min-color: 1)");
     test(function() {
       assert_not_equals(is_monochrome, is_color, "should be either monochrome or color");
     }, "monochrome_and_color");