style: Introduce nsCSSKeywordAndBoolTableEntry.

The values in the boolean context depend on its feature.  For examples, in the
case of prefers-reduced-motion 'no-preference' means false and 'reduced' mean
true in the boolean context, whereas in the case of prefers-contrast
'no-preference' means false and other two values, 'high' and 'low' means true
in the boolean context.  To support it we introduce a child struct of
nsCSSKTableEntry that has an additional field representing the value in the
boolean context and use it when we have no specified value in the media feature
(i.e. in the boolean context).

Bug: 1365045
Reviewed-by: heycam
MozReview-Commit-ID: 79HiW8l5ous
This commit is contained in:
Hiroyuki Ikezoe 2018-07-24 16:50:47 +09:00 committed by Emilio Cobos Álvarez
parent d1733aa502
commit c63a9a37a9
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C

View file

@ -16,6 +16,7 @@ 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};
@ -374,6 +375,9 @@ pub enum MediaExpressionValue {
/// 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),
}
@ -420,6 +424,10 @@ impl MediaExpressionValue {
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 {
@ -457,25 +465,43 @@ impl MediaExpressionValue {
MediaExpressionValue::Resolution(ref r) => r.to_css(dest),
MediaExpressionValue::Ident(ref ident) => serialize_atom_identifier(ident, dest),
MediaExpressionValue::Enumerated(value) => unsafe {
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?");
MediaExpressionValue::keyword_to_css(keyword, dest)
}
}
}
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;
// 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?");
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)
},
}
}
}
@ -496,23 +522,51 @@ where
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);
@ -556,24 +610,21 @@ fn parse_feature_value<'i, 't>(
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()))
},
@ -582,6 +633,30 @@ fn parse_feature_value<'i, 't>(
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,
@ -834,6 +909,16 @@ impl MediaFeatureExpression {
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,
};
},
@ -880,6 +965,13 @@ impl MediaFeatureExpression {
);
return one == other;
},
(&BoolEnumerated(one), &BoolEnumerated(other)) => {
debug_assert_ne!(
self.feature.mRangeType,
nsMediaFeature_RangeType::eMinMaxAllowed
);
return one == other;
},
_ => unreachable!(),
};