--- a/servo/components/style/gecko/media_queries.rs
+++ b/servo/components/style/gecko/media_queries.rs
@@ -4,17 +4,17 @@
//! Gecko's media-query device and expression representation.
use app_units::Au;
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;
use media_queries::MediaType;
use parser::ParserContext;
use properties::ComputedValues;
use std::ascii::AsciiExt;
use std::fmt::{self, Write};
@@ -133,17 +133,17 @@ impl ToCss for Expression {
nsMediaExpression_Range::eEqual => {},
}
// NB: CssStringWriter not needed, feature names are under control.
write!(dest, "{}", Atom::from(unsafe { *self.feature.mName }))?;
if let Some(ref val) = self.value {
dest.write_str(": ")?;
- val.to_css(dest)?;
+ val.to_css(dest, self)?;
}
dest.write_str(")")
}
}
impl PartialEq for Expression {
fn eq(&self, other: &Expression) -> bool {
@@ -227,17 +227,17 @@ pub enum MediaExpressionValue {
BoolInteger(bool),
/// Two integers separated by '/', with optional whitespace on either side
/// of the '/'.
IntRatio(u32, u32),
/// A resolution.
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?
Ident(String),
}
impl MediaExpressionValue {
fn from_css_value(for_expr: &Expression, css_value: &nsCSSValue) -> Option<Self> {
@@ -268,20 +268,18 @@ impl MediaExpressionValue {
debug_assert!(i == 0 || i == 1);
Some(MediaExpressionValue::BoolInteger(i == 1))
}
nsMediaFeature_ValueType::eResolution => {
debug_assert!(css_value.mUnit == nsCSSUnit::eCSSUnit_Inch);
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);
let string = unsafe {
string_from_ns_string_buffer(*css_value.mValue.mString.as_ref())
};
Some(MediaExpressionValue::Ident(string))
}
@@ -293,37 +291,55 @@ impl MediaExpressionValue {
debug_assert!(first >= 0 && second >= 0);
Some(MediaExpressionValue::IntRatio(first as u32, second as u32))
}
}
}
}
-impl ToCss for MediaExpressionValue {
- fn to_css<W>(&self, dest: &mut W) -> fmt::Result
+impl MediaExpressionValue {
+ fn to_css<W>(&self, dest: &mut W, for_expr: &Expression) -> fmt::Result
where W: fmt::Write,
{
match *self {
MediaExpressionValue::Length(ref l) => l.to_css(dest),
MediaExpressionValue::Integer(v) => write!(dest, "{}", v),
MediaExpressionValue::Float(v) => write!(dest, "{}", v),
MediaExpressionValue::BoolInteger(v) => {
dest.write_str(if v { "1" } else { "0" })
},
MediaExpressionValue::IntRatio(a, b) => {
write!(dest, "{}/{}", a, b)
},
MediaExpressionValue::Resolution(ref r) => r.to_css(dest),
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::os::raw::c_char;
+ use std::{slice, str};
+
+ // 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)
}
}
}
}
fn starts_with_ignore_ascii_case(string: &str, prefix: &str) -> bool {
string.len() > prefix.len() &&
string[0..prefix.len()].eq_ignore_ascii_case(prefix)
@@ -345,16 +361,37 @@ fn find_feature<F>(mut f: F) -> Option<&
}
features = features.offset(1);
}
}
None
}
+unsafe fn find_in_table<F>(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,
value: Option<MediaExpressionValue>,
range: nsMediaExpression_Range) -> Self {
Expression {
feature: feature,
value: value,
@@ -454,19 +491,35 @@ impl Expression {
return Err(())
}
MediaExpressionValue::IntRatio(a as u32, b as u32)
}
nsMediaFeature_ValueType::eResolution => {
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())
}
};
Ok(Expression::new(feature, Some(value), range))
})
@@ -555,19 +608,19 @@ impl Expression {
};
one.to_dpi().partial_cmp(&actual_dpi).unwrap()
}
(&Ident(ref one), &Ident(ref other)) => {
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!(),
};
cmp == Ordering::Equal || match self.range {
nsMediaExpression_Range::eMin => cmp == Ordering::Less,
nsMediaExpression_Range::eEqual => false,
nsMediaExpression_Range::eMax => cmp == Ordering::Greater,