Bug 1331213: Allow parsing media query expressions without colons. r?heycam draft
authorEmilio Cobos Álvarez <emilio@crisal.io>
Sun, 15 Jan 2017 21:07:28 +0100
changeset 461261 9384b11082049358489ef35407219c23ae158357
parent 461260 191cf9d56945ad8e1172f09712f0927f97adcc8a
child 461262 5b0561a4cf2e26a4cac47662d1695c9406c8574f
child 461272 631ac9c4b288b84086eb191b0a7ba6b44f80c4cf
push id41620
push userbmo:emilio+bugs@crisal.io
push dateMon, 16 Jan 2017 09:15:13 +0000
reviewersheycam
bugs1331213
milestone53.0a1
Bug 1331213: Allow parsing media query expressions without colons. r?heycam This is used for stuff like @media (monochrome), etc. MozReview-Commit-ID: ANhZLXDURDj Signed-off-by: Emilio Cobos Álvarez <emilio@crisal.io>
servo/components/style/gecko/media_queries.rs
--- a/servo/components/style/gecko/media_queries.rs
+++ b/servo/components/style/gecko/media_queries.rs
@@ -93,35 +93,39 @@ impl Device {
 unsafe impl Sync for Device {}
 unsafe impl Send for Device {}
 
 /// A expression for gecko contains a reference to the media feature, the value
 /// the media query contained, and the range to evaluate.
 #[derive(Debug, Clone)]
 pub struct Expression {
     feature: &'static nsMediaFeature,
-    value: MediaExpressionValue,
+    value: Option<MediaExpressionValue>,
     range: nsMediaExpression_Range
 }
 
 impl ToCss for Expression {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result
         where W: fmt::Write,
     {
         dest.write_str("(")?;
         match self.range {
             nsMediaExpression_Range::eMin => dest.write_str("min-")?,
             nsMediaExpression_Range::eMax => dest.write_str("max-")?,
             nsMediaExpression_Range::eEqual => {},
         }
-        // NB: CSSStringWriter not needed, features are under control.
+
+        // NB: CSSStringWriter not needed, feature names are under control.
         write!(dest, "{}", Atom::from(unsafe { *self.feature.mName }))?;
-        dest.write_str(": ")?;
 
-        self.value.to_css(dest)?;
+        if let Some(ref val) = self.value {
+            dest.write_str(": ")?;
+            val.to_css(dest)?;
+        }
+
         dest.write_str(")")
     }
 }
 
 /// A resolution.
 #[derive(Debug, Clone)]
 pub enum Resolution {
     /// Dots per inch.
@@ -208,17 +212,16 @@ impl ToCss for MediaExpressionValue {
     }
 }
 
 fn starts_with_ignore_ascii_case(string: &str, prefix: &str) -> bool {
     string.len() > prefix.len() &&
       string[0..prefix.len()].eq_ignore_ascii_case(prefix)
 }
 
-#[allow(warnings)]
 fn find_feature<F>(mut f: F) -> Option<&'static nsMediaFeature>
     where F: FnMut(&'static nsMediaFeature) -> bool,
 {
     // FIXME(emilio): With build-time bindgen, we would be able to use
     // structs::nsMediaFeatures_features. That would unfortunately break MSVC
     // builds, or require one bindings file per platform.
     //
     // I'm not into any of those, so meanwhile let's use a FFI function.
@@ -233,36 +236,34 @@ fn find_feature<F>(mut f: F) -> Option<&
     }
 
     None
 }
 
 impl Expression {
     /// Trivially construct a new expression.
     fn new(feature: &'static nsMediaFeature,
-           value: MediaExpressionValue,
+           value: Option<MediaExpressionValue>,
            range: nsMediaExpression_Range) -> Self {
         Expression {
             feature: feature,
             value: value,
             range: range,
         }
     }
 
     /// Parse a media expression of the form:
     ///
     /// ```
     /// (media-feature: media-value)
     /// ```
-    #[allow(warnings)]
     pub fn parse(input: &mut Parser) -> Result<Self, ()> {
         try!(input.expect_parenthesis_block());
         input.parse_nested_block(|input| {
             let ident = try!(input.expect_ident());
-            try!(input.expect_colon());
 
             let mut flags = 0;
             let mut feature_name = &*ident;
 
             // TODO(emilio): this is under a pref in Gecko.
             if starts_with_ignore_ascii_case(feature_name, "-webkit-") {
                 feature_name = &feature_name[8..];
                 flags |= nsMediaFeature_RequirementFlags::eHasWebkitPrefix as u8;
@@ -289,16 +290,27 @@ impl Expression {
                 return Err(());
             }
 
             if range != nsMediaExpression_Range::eEqual &&
                 feature.mRangeType != nsMediaFeature_RangeType::eMinMaxAllowed {
                 return Err(());
             }
 
+            // If there's no colon, this is a media query of the form
+            // '(<feature>)', that is, there's no value specified.
+            //
+            // FIXME(emilio): We need to check for range operators too here when
+            // we support them, see:
+            //
+            // https://drafts.csswg.org/mediaqueries/#mq-ranges
+            if input.try(|i| i.expect_colon()).is_err() {
+                return Ok(Expression::new(feature, None, range));
+            }
+
             let value = match feature.mValueType {
                 nsMediaFeature_ValueType::eLength => {
                     MediaExpressionValue::Length(
                         specified::Length::parse_non_negative(input)?)
                 },
                 nsMediaFeature_ValueType::eInteger => {
                     let i = input.expect_integer()?;
                     if i < 0 {
@@ -331,16 +343,17 @@ impl Expression {
                     MediaExpressionValue::IntRatio(a as u32, b as u32)
                 }
                 nsMediaFeature_ValueType::eResolution => {
                     MediaExpressionValue::Resolution(Resolution::parse(input)?)
                 }
                 nsMediaFeature_ValueType::eEnumerated => {
                     let index = unsafe {
                         let _table = feature.mData.mKeywordTable.as_ref();
+                        // TODO
                         0
                     };
                     MediaExpressionValue::Enumerated(index)
                 }
                 nsMediaFeature_ValueType::eIdent => {
                     MediaExpressionValue::Ident(input.expect_ident()?.into())
                 }
             };