Auto merge of #16450 - emilio:keyword-media-queries, r=heycam

stylo: Support keyword-valued media queries

From [Bug 1356074](https://bugzil.la/1356074)

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/16450)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2017-04-13 23:09:34 -05:00 committed by GitHub
commit 6f7db7c571
3 changed files with 79 additions and 20 deletions

View file

@ -9,7 +9,7 @@ use cssparser::{CssStringWriter, Parser, Token};
use euclid::Size2D; use euclid::Size2D;
use font_metrics::get_metrics_provider_for_product; use font_metrics::get_metrics_provider_for_product;
use gecko_bindings::bindings; 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::{nsMediaExpression_Range, nsMediaFeature};
use gecko_bindings::structs::{nsMediaFeature_ValueType, nsMediaFeature_RangeType, nsMediaFeature_RequirementFlags}; use gecko_bindings::structs::{nsMediaFeature_ValueType, nsMediaFeature_RangeType, nsMediaFeature_RequirementFlags};
use gecko_bindings::structs::RawGeckoPresContextOwned; use gecko_bindings::structs::RawGeckoPresContextOwned;
@ -138,7 +138,7 @@ impl ToCss for Expression {
if let Some(ref val) = self.value { if let Some(ref val) = self.value {
dest.write_str(": ")?; dest.write_str(": ")?;
val.to_css(dest)?; val.to_css(dest, self)?;
} }
dest.write_str(")") dest.write_str(")")
@ -232,7 +232,7 @@ pub enum MediaExpressionValue {
Resolution(Resolution), Resolution(Resolution),
/// An enumerated value, defined by the variant keyword table in the /// An enumerated value, defined by the variant keyword table in the
/// feature's `mData` member. /// feature's `mData` member.
Enumerated(u32), Enumerated(i16),
/// An identifier. /// An identifier.
/// ///
/// TODO(emilio): Maybe atomize? /// TODO(emilio): Maybe atomize?
@ -273,10 +273,8 @@ impl MediaExpressionValue {
Some(MediaExpressionValue::Resolution(Resolution::Dpi(css_value.float_unchecked()))) Some(MediaExpressionValue::Resolution(Resolution::Dpi(css_value.float_unchecked())))
} }
nsMediaFeature_ValueType::eEnumerated => { nsMediaFeature_ValueType::eEnumerated => {
debug_assert!(css_value.mUnit == nsCSSUnit::eCSSUnit_Enumerated); let value = css_value.integer_unchecked() as i16;
let value = css_value.integer_unchecked(); Some(MediaExpressionValue::Enumerated(value))
debug_assert!(value >= 0);
Some(MediaExpressionValue::Enumerated(value as u32))
} }
nsMediaFeature_ValueType::eIdent => { nsMediaFeature_ValueType::eIdent => {
debug_assert!(css_value.mUnit == nsCSSUnit::eCSSUnit_Ident); debug_assert!(css_value.mUnit == nsCSSUnit::eCSSUnit_Ident);
@ -298,8 +296,8 @@ impl MediaExpressionValue {
} }
} }
impl ToCss for MediaExpressionValue { impl MediaExpressionValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result fn to_css<W>(&self, dest: &mut W, for_expr: &Expression) -> fmt::Result
where W: fmt::Write, where W: fmt::Write,
{ {
match *self { match *self {
@ -316,9 +314,27 @@ impl ToCss for MediaExpressionValue {
MediaExpressionValue::Ident(ref ident) => { MediaExpressionValue::Ident(ref ident) => {
CssStringWriter::new(dest).write_str(ident) CssStringWriter::new(dest).write_str(ident)
} }
MediaExpressionValue::Enumerated(..) => { MediaExpressionValue::Enumerated(value) => unsafe {
// TODO(emilio): Use the CSS keyword table. use std::{slice, str};
unimplemented!() 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<F>(mut f: F) -> Option<&'static nsMediaFeature>
None 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 { impl Expression {
/// Trivially construct a new expression. /// Trivially construct a new expression.
fn new(feature: &'static nsMediaFeature, fn new(feature: &'static nsMediaFeature,
@ -459,9 +496,25 @@ impl Expression {
MediaExpressionValue::Resolution(Resolution::parse(input)?) MediaExpressionValue::Resolution(Resolution::parse(input)?)
} }
nsMediaFeature_ValueType::eEnumerated => { nsMediaFeature_ValueType::eEnumerated => {
// TODO(emilio): Use Gecko's CSS keyword table to parse let keyword = input.expect_ident()?;
// this. let keyword = unsafe {
return Err(()) 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 => { nsMediaFeature_ValueType::eIdent => {
MediaExpressionValue::Ident(input.expect_ident()?.into_owned()) MediaExpressionValue::Ident(input.expect_ident()?.into_owned())
@ -560,9 +613,9 @@ impl Expression {
debug_assert!(self.feature.mRangeType != nsMediaFeature_RangeType::eMinMaxAllowed); debug_assert!(self.feature.mRangeType != nsMediaFeature_RangeType::eMinMaxAllowed);
return one == other; return one == other;
} }
(&Enumerated(..), &Enumerated(..)) => { (&Enumerated(one), &Enumerated(other)) => {
// TODO(emilio) debug_assert!(self.feature.mRangeType != nsMediaFeature_RangeType::eMinMaxAllowed);
unimplemented!(); return one == other;
} }
_ => unreachable!(), _ => unreachable!(),
}; };

View file

@ -1108,6 +1108,14 @@ extern "C" {
extern "C" { extern "C" {
pub fn Gecko_GetMediaFeatures() -> *const nsMediaFeature; 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" { extern "C" {
pub fn Gecko_CSSFontFaceRule_Create() -> *mut nsCSSFontFaceRule; pub fn Gecko_CSSFontFaceRule_Create() -> *mut nsCSSFontFaceRule;
} }

View file

@ -4,8 +4,6 @@
//! Traversing the DOM tree; the bloom filter. //! Traversing the DOM tree; the bloom filter.
#![deny(missing_docs)]
use atomic_refcell::{AtomicRefCell, AtomicRefMut}; use atomic_refcell::{AtomicRefCell, AtomicRefMut};
use context::{SharedStyleContext, StyleContext, ThreadLocalStyleContext}; use context::{SharedStyleContext, StyleContext, ThreadLocalStyleContext};
use data::{ElementData, ElementStyles, StoredRestyleHint}; use data::{ElementData, ElementStyles, StoredRestyleHint};