--- a/layout/style/nsMediaFeatures.h
+++ b/layout/style/nsMediaFeatures.h
@@ -12,41 +12,64 @@
#include <stdint.h>
class nsAtom;
class nsIDocument;
struct nsCSSKTableEntry;
class nsCSSValue;
class nsStaticAtom;
+struct nsCSSKeywordAndBoolTableEntry : public nsCSSKTableEntry {
+ constexpr nsCSSKeywordAndBoolTableEntry(nsCSSKeyword aKeyword, int16_t aValue)
+ : nsCSSKTableEntry(aKeyword, aValue)
+ , mValueInBooleanContext(true)
+ {
+ }
+
+ template<typename T,
+ typename = typename std::enable_if<std::is_enum<T>::value>::type>
+ constexpr nsCSSKeywordAndBoolTableEntry(
+ nsCSSKeyword aKeyword,
+ T aValue,
+ bool aValueInBooleanContext)
+ : nsCSSKTableEntry(aKeyword, aValue)
+ , mValueInBooleanContext(aValueInBooleanContext)
+ {
+ }
+
+ bool mValueInBooleanContext;
+};
+
struct nsMediaFeature;
typedef void (*nsMediaFeatureValueGetter)(nsIDocument* aDocument,
const nsMediaFeature* aFeature,
nsCSSValue& aResult);
struct nsMediaFeature
{
nsStaticAtom** mName; // extra indirection to point to nsGkAtoms members
enum RangeType { eMinMaxAllowed, eMinMaxNotAllowed };
RangeType mRangeType;
enum ValueType {
// All value types allow eCSSUnit_Null to indicate that no value
// was given (in addition to the types listed below).
- eLength, // values are eCSSUnit_Pixel
- eInteger, // values are eCSSUnit_Integer
- eFloat, // values are eCSSUnit_Number
- eBoolInteger,// values are eCSSUnit_Integer (0, -0, or 1 only)
- eIntRatio, // values are eCSSUnit_Array of two eCSSUnit_Integer
- eResolution, // values are in eCSSUnit_Inch (for dpi),
- // eCSSUnit_Pixel (for dppx), or
- // eCSSUnit_Centimeter (for dpcm)
- eEnumerated, // values are eCSSUnit_Enumerated (uses keyword table)
- eIdent // values are eCSSUnit_Ident
+ eLength, // values are eCSSUnit_Pixel
+ eInteger, // values are eCSSUnit_Integer
+ eFloat, // values are eCSSUnit_Number
+ eBoolInteger, // values are eCSSUnit_Integer (0, -0, or 1 only)
+ eIntRatio, // values are eCSSUnit_Array of two eCSSUnit_Integer
+ eResolution, // values are in eCSSUnit_Inch (for dpi),
+ // eCSSUnit_Pixel (for dppx), or
+ // eCSSUnit_Centimeter (for dpcm)
+ eEnumerated, // values are eCSSUnit_Enumerated (uses keyword table)
+ eBoolEnumerated, // values are eCSSUnit_Enumerated (uses keyword and boolean
+ // pair table)
+ eIdent // values are eCSSUnit_Ident
// Note that a number of pieces of code (both for parsing and
// for matching of valueless expressions) assume that all numeric
// value types cannot be negative. The parsing code also does
// not allow zeros in eIntRatio types.
};
ValueType mValueType;
enum RequirementFlags : uint8_t {
@@ -68,16 +91,19 @@ struct nsMediaFeature
union {
// In static arrays, it's the first member that's initialized. We
// need that to be void* so we can initialize both other types.
// This member should never be accessed by name.
const void* mInitializer_;
// If mValueType == eEnumerated: const int32_t*: keyword table in
// the same format as the keyword tables in nsCSSProps.
const nsCSSKTableEntry* mKeywordTable;
+ // If mValueType == eBoolEnumerated: similar to the above but having a
+ // boolean field representing the value in Boolean context.
+ const nsCSSKeywordAndBoolTableEntry* mKeywordAndBoolTable;
// If mGetter == GetSystemMetric (which implies mValueType ==
// eBoolInteger): nsAtom * const *, for the system metric.
nsAtom * const * mMetric;
} mData;
// A function that returns the current value for this feature for a
// given presentation. If it returns eCSSUnit_Null, the feature is
// not present.
--- a/servo/components/style/gecko/media_queries.rs
+++ b/servo/components/style/gecko/media_queries.rs
@@ -11,16 +11,17 @@ use cssparser::{Parser, RGBA, Token};
use euclid::Size2D;
use euclid::TypedScale;
use gecko::values::{convert_nscolor_to_rgba, convert_rgba_to_nscolor};
use gecko_bindings::bindings;
use gecko_bindings::structs;
use gecko_bindings::structs::{nsCSSKTableEntry, nsCSSKeyword, nsCSSUnit, nsCSSValue};
use gecko_bindings::structs::{nsMediaFeature, nsMediaFeature_RangeType};
use gecko_bindings::structs::{nsMediaFeature_ValueType, nsPresContext};
+use gecko_bindings::structs::nsCSSKeywordAndBoolTableEntry;
use gecko_bindings::structs::RawGeckoPresContextOwned;
use media_queries::MediaType;
use parser::{Parse, ParserContext};
use properties::ComputedValues;
use servo_arc::Arc;
use std::fmt::{self, Write};
use std::sync::atomic::{AtomicBool, AtomicIsize, AtomicUsize, Ordering};
use str::{starts_with_ignore_ascii_case, string_as_ascii_lowercase};
@@ -369,16 +370,19 @@ pub enum MediaExpressionValue {
/// 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(i16),
+ /// Similar to the above Enumerated but the variant keyword table has an
+ /// additional field what the keyword value means in the Boolean Context.
+ BoolEnumerated(i16),
/// An identifier.
Ident(Atom),
}
impl MediaExpressionValue {
fn from_css_value(
for_expr: &MediaFeatureExpression,
css_value: &nsCSSValue,
@@ -415,16 +419,20 @@ impl MediaExpressionValue {
Some(MediaExpressionValue::Resolution(Resolution::Dppx(
css_value.float_unchecked(),
)))
},
nsMediaFeature_ValueType::eEnumerated => {
let value = css_value.integer_unchecked() as i16;
Some(MediaExpressionValue::Enumerated(value))
},
+ nsMediaFeature_ValueType::eBoolEnumerated => {
+ let value = css_value.integer_unchecked() as i16;
+ Some(MediaExpressionValue::BoolEnumerated(value))
+ },
nsMediaFeature_ValueType::eIdent => {
debug_assert_eq!(css_value.mUnit, nsCSSUnit::eCSSUnit_AtomIdent);
Some(MediaExpressionValue::Ident(unsafe {
Atom::from_raw(*css_value.mValue.mAtom.as_ref())
}))
},
nsMediaFeature_ValueType::eIntRatio => {
let array = unsafe { css_value.array_unchecked() };
@@ -452,35 +460,53 @@ impl MediaExpressionValue {
MediaExpressionValue::IntRatio(a, b) => {
a.to_css(dest)?;
dest.write_char('/')?;
b.to_css(dest)
},
MediaExpressionValue::Resolution(ref r) => r.to_css(dest),
MediaExpressionValue::Ident(ref ident) => serialize_atom_identifier(ident, dest),
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(
+ let keyword = find_in_table(
*for_expr.feature.mData.mKeywordTable.as_ref(),
|_kw, val| val == value,
+ |e| e.keyword(),
+ ).expect("Value not found in the keyword table?");
+
+ MediaExpressionValue::keyword_to_css(keyword, dest)
+ },
+ MediaExpressionValue::BoolEnumerated(value) => unsafe {
+ let keyword = find_in_table(
+ *for_expr.feature.mData.mKeywordAndBoolTable.as_ref(),
+ |_kw, val| val == value,
+ |e| e.keyword(),
).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);
+ MediaExpressionValue::keyword_to_css(keyword, dest)
+ }
+ }
+ }
- let string = str::from_utf8_unchecked(buffer);
+ fn keyword_to_css<W>(keyword: nsCSSKeyword, dest: &mut CssWriter<W>) -> fmt::Result
+ where
+ W: fmt::Write,
+ {
+ use std::{slice, str};
+ use std::os::raw::c_char;
- dest.write_str(string)
- },
+ // NB: All the keywords on nsMediaFeatures are static,
+ // well-formed utf-8.
+ let mut length = 0;
+ unsafe {
+ 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 find_feature<F>(mut f: F) -> Option<&'static nsMediaFeature>
where
F: FnMut(&'static nsMediaFeature) -> bool,
{
@@ -491,33 +517,61 @@ where
return Some(&*features);
}
features = features.offset(1);
}
}
None
}
-unsafe fn find_in_table<F>(
- mut current_entry: *const nsCSSKTableEntry,
- mut f: F,
-) -> Option<(nsCSSKeyword, i16)>
+trait TableEntry {
+ fn value(&self) -> i16;
+ fn keyword(&self) -> nsCSSKeyword;
+}
+
+impl TableEntry for nsCSSKTableEntry {
+ fn value(&self) -> i16 {
+ self.mValue
+ }
+ fn keyword(&self) -> nsCSSKeyword {
+ self.mKeyword
+ }
+}
+
+impl TableEntry for nsCSSKeywordAndBoolTableEntry {
+ fn value(&self) -> i16 {
+ self._base.mValue
+ }
+ fn keyword(&self) -> nsCSSKeyword {
+ self._base.mKeyword
+ }
+}
+
+unsafe fn find_in_table<T, R, FindFunc, ResultFunc>(
+ current_entry: *const T,
+ find: FindFunc,
+ result_func: ResultFunc,
+) -> Option<R>
where
- F: FnMut(nsCSSKeyword, i16) -> bool,
+ T: TableEntry,
+ FindFunc: Fn(nsCSSKeyword, i16) -> bool,
+ ResultFunc: Fn(&T) -> R,
{
+ let mut current_entry = current_entry;
+
loop {
- let value = (*current_entry).mValue;
- let keyword = (*current_entry).mKeyword;
+ let value = (*current_entry).value();
+ let keyword = (*current_entry).keyword();
if value == -1 {
return None; // End of the table.
}
- if f(keyword, value) {
- return Some((keyword, value));
+ if find(keyword, value) {
+ return Some(result_func(&*current_entry));
}
current_entry = current_entry.offset(1);
}
}
fn parse_feature_value<'i, 't>(
feature: &nsMediaFeature,
@@ -551,42 +605,63 @@ fn parse_feature_value<'i, 't>(
input.expect_delim('/')?;
let b = Integer::parse_positive(context, input)?;
MediaExpressionValue::IntRatio(a.value() as u32, b.value() as u32)
},
nsMediaFeature_ValueType::eResolution => {
MediaExpressionValue::Resolution(Resolution::parse(context, input)?)
},
nsMediaFeature_ValueType::eEnumerated => {
- let location = input.current_source_location();
- 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 nsCSSKTableEntry =
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(location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- },
- };
+ let value = parse_keyword(input, first_table_entry)?;
MediaExpressionValue::Enumerated(value)
},
+ nsMediaFeature_ValueType::eBoolEnumerated => {
+ let first_table_entry: *const nsCSSKeywordAndBoolTableEntry =
+ unsafe { *feature.mData.mKeywordAndBoolTable.as_ref() };
+
+ let value = parse_keyword(input, first_table_entry)?;
+
+ MediaExpressionValue::BoolEnumerated(value)
+ },
nsMediaFeature_ValueType::eIdent => {
MediaExpressionValue::Ident(Atom::from(input.expect_ident()?.as_ref()))
},
};
Ok(value)
}
+/// Parse a keyword and returns the corresponding i16 value.
+fn parse_keyword<'i, 't, T>(
+ input: &mut Parser<'i, 't>,
+ first_table_entry: *const T,
+) -> Result<i16, ParseError<'i>>
+where
+ T: TableEntry,
+{
+ let location = input.current_source_location();
+ let keyword = input.expect_ident()?;
+ let keyword = unsafe {
+ bindings::Gecko_LookupCSSKeyword(keyword.as_bytes().as_ptr(), keyword.len() as u32)
+ };
+
+ let value = unsafe {
+ find_in_table(first_table_entry, |kw, _| kw == keyword, |e| e.value())
+ };
+
+ match value {
+ Some(value) => Ok(value),
+ None => Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
+ }
+}
+
/// Consumes an operation or a colon, or returns an error.
fn consume_operation_or_colon(
input: &mut Parser,
) -> Result<Option<Operator>, ()> {
let first_delim = {
let next_token = match input.next() {
Ok(t) => t,
Err(..) => return Err(()),
@@ -829,16 +904,26 @@ impl MediaFeatureExpression {
return match *actual_value {
BoolInteger(v) => v,
Integer(v) => v != 0,
Length(ref l) => computed::Context::for_media_query_evaluation(
device,
quirks_mode,
|context| l.to_computed_value(&context).px() != 0.,
),
+ BoolEnumerated(value) => {
+ let value = unsafe {
+ find_in_table(
+ *self.feature.mData.mKeywordAndBoolTable.as_ref(),
+ |_kw, val| val == value,
+ |e| e.mValueInBooleanContext,
+ )
+ };
+ value.expect("Value not found in the keyword table?")
+ },
_ => true,
};
},
};
// FIXME(emilio): Handle the possible floating point errors?
let cmp = match (actual_value, required_value) {
(&Length(ref one), &Length(ref other)) => {
@@ -875,16 +960,23 @@ impl MediaFeatureExpression {
},
(&Enumerated(one), &Enumerated(other)) => {
debug_assert_ne!(
self.feature.mRangeType,
nsMediaFeature_RangeType::eMinMaxAllowed
);
return one == other;
},
+ (&BoolEnumerated(one), &BoolEnumerated(other)) => {
+ debug_assert_ne!(
+ self.feature.mRangeType,
+ nsMediaFeature_RangeType::eMinMaxAllowed
+ );
+ return one == other;
+ },
_ => unreachable!(),
};
let range_or_op = match self.range_or_operator {
Some(r) => r,
None => return cmp == Ordering::Equal,
};