Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Hiroyuki Ikezoe authored and emilio committed Aug 7, 2018
1 parent d1733aa commit c63a9a3
Showing 1 changed file with 126 additions and 34 deletions.
160 changes: 126 additions & 34 deletions components/style/gecko/media_queries.rs
Expand Up @@ -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};
Expand Down Expand Up @@ -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),
}
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
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?");

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)
},
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 string = str::from_utf8_unchecked(buffer);
MediaExpressionValue::keyword_to_css(keyword, dest)
}
}
}

dest.write_str(string)
},
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;
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)
}
}
}
Expand All @@ -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);
Expand Down Expand Up @@ -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()))
},
Expand All @@ -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,
Expand Down Expand Up @@ -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,
};
},
Expand Down Expand Up @@ -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!(),
};

Expand Down

0 comments on commit c63a9a3

Please sign in to comment.