diff --git a/servo/components/style/gecko/media_queries.rs b/servo/components/style/gecko/media_queries.rs index b422d45a3279..ffa3c95ed4fa 100644 --- a/servo/components/style/gecko/media_queries.rs +++ b/servo/components/style/gecko/media_queries.rs @@ -9,7 +9,7 @@ use cssparser::{CssStringWriter, Parser, Token}; 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::{nsCSSKeyword, nsCSSProps_KTableEntry, 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; @@ -138,7 +138,7 @@ impl ToCss for Expression { if let Some(ref val) = self.value { dest.write_str(": ")?; - val.to_css(dest)?; + val.to_css(dest, self)?; } dest.write_str(")") @@ -232,7 +232,7 @@ pub enum MediaExpressionValue { Resolution(Resolution), /// An enumerated value, defined by the variant keyword table in the /// feature's `mData` member. - Enumerated(u32), + Enumerated(i16), /// An identifier. /// /// TODO(emilio): Maybe atomize? @@ -273,10 +273,8 @@ impl MediaExpressionValue { Some(MediaExpressionValue::Resolution(Resolution::Dpi(css_value.float_unchecked()))) } nsMediaFeature_ValueType::eEnumerated => { - debug_assert!(css_value.mUnit == nsCSSUnit::eCSSUnit_Enumerated); - let value = css_value.integer_unchecked(); - debug_assert!(value >= 0); - Some(MediaExpressionValue::Enumerated(value as u32)) + let value = css_value.integer_unchecked() as i16; + Some(MediaExpressionValue::Enumerated(value)) } nsMediaFeature_ValueType::eIdent => { debug_assert!(css_value.mUnit == nsCSSUnit::eCSSUnit_Ident); @@ -298,8 +296,8 @@ impl MediaExpressionValue { } } -impl ToCss for MediaExpressionValue { - fn to_css(&self, dest: &mut W) -> fmt::Result +impl MediaExpressionValue { + fn to_css(&self, dest: &mut W, for_expr: &Expression) -> fmt::Result where W: fmt::Write, { match *self { @@ -316,9 +314,27 @@ impl ToCss for MediaExpressionValue { MediaExpressionValue::Ident(ref ident) => { CssStringWriter::new(dest).write_str(ident) } - MediaExpressionValue::Enumerated(..) => { - // TODO(emilio): Use the CSS keyword table. - unimplemented!() + MediaExpressionValue::Enumerated(value) => unsafe { + use std::{slice, str}; + use std::os::raw::c_char; + + // NB: All the keywords on nsMediaFeatures are static, + // well-formed utf-8. + let mut length = 0; + + let (keyword, _value) = + find_in_table(*for_expr.feature.mData.mKeywordTable.as_ref(), + |_kw, val| val == value) + .expect("Value not found in the keyword table?"); + + let buffer: *const c_char = + bindings::Gecko_CSSKeywordString(keyword, &mut length); + let buffer = + slice::from_raw_parts(buffer as *const u8, length as usize); + + let string = str::from_utf8_unchecked(buffer); + + dest.write_str(string) } } } @@ -350,6 +366,27 @@ fn find_feature(mut f: F) -> Option<&'static nsMediaFeature> None } +unsafe fn find_in_table(mut current_entry: *const nsCSSProps_KTableEntry, + mut f: F) + -> Option<(nsCSSKeyword, i16)> + where F: FnMut(nsCSSKeyword, i16) -> bool +{ + loop { + let value = (*current_entry).mValue; + let keyword = (*current_entry).mKeyword; + + if value == -1 { + return None; // End of the table. + } + + if f(keyword, value) { + return Some((keyword, value)); + } + + current_entry = current_entry.offset(1); + } +} + impl Expression { /// Trivially construct a new expression. fn new(feature: &'static nsMediaFeature, @@ -459,9 +496,25 @@ impl Expression { MediaExpressionValue::Resolution(Resolution::parse(input)?) } nsMediaFeature_ValueType::eEnumerated => { - // TODO(emilio): Use Gecko's CSS keyword table to parse - // this. - return Err(()) + let keyword = input.expect_ident()?; + let keyword = unsafe { + bindings::Gecko_LookupCSSKeyword(keyword.as_bytes().as_ptr(), + keyword.len() as u32) + }; + + let first_table_entry: *const nsCSSProps_KTableEntry = unsafe { + *feature.mData.mKeywordTable.as_ref() + }; + + let value = + match unsafe { find_in_table(first_table_entry, |kw, _| kw == keyword) } { + Some((_kw, value)) => { + value + } + None => return Err(()), + }; + + MediaExpressionValue::Enumerated(value) } nsMediaFeature_ValueType::eIdent => { MediaExpressionValue::Ident(input.expect_ident()?.into_owned()) @@ -560,9 +613,9 @@ impl Expression { debug_assert!(self.feature.mRangeType != nsMediaFeature_RangeType::eMinMaxAllowed); return one == other; } - (&Enumerated(..), &Enumerated(..)) => { - // TODO(emilio) - unimplemented!(); + (&Enumerated(one), &Enumerated(other)) => { + debug_assert!(self.feature.mRangeType != nsMediaFeature_RangeType::eMinMaxAllowed); + return one == other; } _ => unreachable!(), }; diff --git a/servo/components/style/gecko_bindings/bindings.rs b/servo/components/style/gecko_bindings/bindings.rs index 9b428005d242..acef8183eee2 100644 --- a/servo/components/style/gecko_bindings/bindings.rs +++ b/servo/components/style/gecko_bindings/bindings.rs @@ -1108,6 +1108,14 @@ extern "C" { extern "C" { pub fn Gecko_GetMediaFeatures() -> *const nsMediaFeature; } +extern "C" { + pub fn Gecko_LookupCSSKeyword(string: *const u8, len: u32) + -> nsCSSKeyword; +} +extern "C" { + pub fn Gecko_CSSKeywordString(keyword: nsCSSKeyword, len: *mut u32) + -> *const ::std::os::raw::c_char; +} extern "C" { pub fn Gecko_CSSFontFaceRule_Create() -> *mut nsCSSFontFaceRule; } diff --git a/servo/components/style/traversal.rs b/servo/components/style/traversal.rs index f15c45b6d7a8..3a5c649f9fcb 100644 --- a/servo/components/style/traversal.rs +++ b/servo/components/style/traversal.rs @@ -4,8 +4,6 @@ //! Traversing the DOM tree; the bloom filter. -#![deny(missing_docs)] - use atomic_refcell::{AtomicRefCell, AtomicRefMut}; use context::{SharedStyleContext, StyleContext, ThreadLocalStyleContext}; use data::{ElementData, ElementStyles, StoredRestyleHint};