/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
/// Whether to allow an `or` condition or not during parsing. #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToCss)] enum AllowOr {
Yes,
No,
}
/// Trait for query elements that parse a series of conditions separated by /// AND or OR operators, or prefixed with NOT. /// /// This is used by both QueryCondition and StyleQuery as they support similar /// syntax for combining multiple conditions with a boolean operator. trait OperationParser: Sized { /// https://drafts.csswg.org/mediaqueries-5/#typedef-media-condition or /// https://drafts.csswg.org/mediaqueries-5/#typedef-media-condition-without-or /// (depending on `allow_or`). fn parse_internal<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
feature_type: FeatureType,
allow_or: AllowOr,
) -> Result<Self, ParseError<'i>> { let location = input.current_source_location();
if input.try_parse(|i| i.expect_ident_matching("not")).is_ok() { let inner_condition = Self::parse_in_parens(context, input, feature_type)?; return Ok(Self::new_not(Box::new(inner_condition)));
}
let first_condition = Self::parse_in_parens(context, input, feature_type)?; let operator = match input.try_parse(Operator::parse) {
Ok(op) => op,
Err(..) => return Ok(first_condition),
};
// Parse a condition in parentheses, or `<general-enclosed>`. fn parse_in_parens<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
feature_type: FeatureType,
) -> Result<Self, ParseError<'i>>;
// Helpers to create the appropriate enum variant of the implementing type: // Create a Not result that encapsulates the `inner` condition. fn new_not(inner: Box<Self>) -> Self;
// Create an Operation result with the given list of `conditions` using `operator`. fn new_operation(conditions: Box<[Self]>, operator: Operator) -> Self;
}
/// https://drafts.csswg.org/css-conditional-5/#typedef-style-query #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)] pubenum StyleQuery { /// A negation of a condition.
Not(Box<StyleQuery>), /// A set of joint operations.
Operation(Box<[StyleQuery]>, Operator), /// A condition wrapped in parenthesis.
InParens(Box<StyleQuery>), /// A feature query (`--foo: bar` or just `--foo`).
Feature(StyleFeature), /// An unknown "general-enclosed" term.
GeneralEnclosed(String),
}
impl ToCss for StyleQuery { fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where
W: fmt::Write,
{ match *self {
StyleQuery::Not(ref c) => {
dest.write_str("not ")?;
c.maybe_parenthesized(dest)
},
StyleQuery::Operation(ref list, op) => { letmut iter = list.iter(); let item = iter.next().unwrap();
item.maybe_parenthesized(dest)?; for item in iter {
dest.write_char(' ')?;
op.to_css(dest)?;
dest.write_char(' ')?;
item.maybe_parenthesized(dest)?;
}
Ok(())
},
StyleQuery::InParens(ref c) => match &**c {
StyleQuery::Feature(_) | StyleQuery::InParens(_) => {
dest.write_char('(')?;
c.to_css(dest)?;
dest.write_char(')')
},
_ => c.to_css(dest),
},
StyleQuery::Feature(ref f) => f.to_css(dest),
StyleQuery::GeneralEnclosed(ref s) => dest.write_str(&s),
}
}
}
impl StyleQuery { // Helper for to_css when handling values within boolean operators: // GeneralEnclosed includes its parens in the string, so we don't need to // wrap the value with an additional set here. fn maybe_parenthesized<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where
W: fmt::Write,
{ iflet StyleQuery::GeneralEnclosed(ref s) = self {
dest.write_str(&s)
} else {
dest.write_char('(')?; self.to_css(dest)?;
dest.write_char(')')
}
}
let inner = Self::parse_internal(context, input, feature_type, AllowOr::Yes)?;
Ok(Self::InParens(Box::new(inner)))
}
fn parse_in_parenthesis_block<'i>(
context: &ParserContext,
input: &mut Parser<'i, '_>,
) -> Result<Self, ParseError<'i>> { // Base case. Make sure to preserve this error as it's more generally // relevant. let feature_error = match input.try_parse(|input| StyleFeature::parse(context, input)) {
Ok(feature) => return Ok(Self::Feature(feature)),
Err(e) => e,
};
fn try_parse_block<'i, T, F>(
context: &ParserContext,
input: &mut Parser<'i, '_>,
start: SourcePosition,
parse: F,
) -> Option<T> where
F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i>>,
{ let nested = input.try_parse(|input| input.parse_nested_block(parse)); match nested {
Ok(nested) => Some(nested),
Err(e) => { // We're about to swallow the error in a `<general-enclosed>` // condition, so report it while we can. let loc = e.location; let error = ContextualParseError::InvalidMediaRule(input.slice_from(start), e);
context.log_css_error(loc, error);
None
},
}
}
/// A style query feature: /// https://drafts.csswg.org/css-conditional-5/#typedef-style-feature #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)] pubenum StyleFeature { /// A property name and optional value to match.
Plain(StyleFeaturePlain), /// A style query range expression.
Range(QueryStyleRange),
}
/// A style feature consisting of a custom property name and (optionally) value. #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)] pubstruct StyleFeaturePlain {
name: custom_properties::Name, #[ignore_malloc_size_of = "StyleFeatureValue has an Arc variant"]
value: StyleFeatureValue,
}
fn matches(
&self,
ctx: &computed::Context,
attribute_tracker: &mut AttributeTracker,
) -> KleeneValue { // FIXME(emilio): Confirm this is the right style to query. let stylist = ctx
.builder
.stylist
.expect("container queries should have a stylist around"); let registration = stylist.get_custom_property_registration(&self.name); let current_value = ctx
.inherited_custom_properties()
.get(registration, &self.name);
KleeneValue::from(matchself.value {
StyleFeatureValue::Value(Some(ref v)) => { if ctx.container_info.is_none() { // If no container, custom props are guaranteed-unknown. false
} elseif v.has_references() { // If there are --var() references in the query value, // try to substitute them before comparing to current. Self::substitute_and_compare(
v,
registration,
stylist,
ctx,
attribute_tracker,
current_value,
)
} else {
custom_properties::compute_variable_value(&v, registration, ctx).as_ref()
== current_value
}
},
StyleFeatureValue::Value(None) => current_value.is_some(),
StyleFeatureValue::Keyword(kw) => { match kw {
CSSWideKeyword::Unset => current_value.is_none(),
CSSWideKeyword::Initial => { iflet Some(initial) = ®istration.initial_value { let v = custom_properties::compute_variable_value(
&initial,
registration,
ctx,
);
v.as_ref() == current_value
} else {
current_value.is_none()
}
},
CSSWideKeyword::Inherit => { iflet Some(inherited) = ctx
.container_info
.as_ref()
.expect("queries should provide container info")
.inherited_style()
{
inherited.custom_properties().get(registration, &self.name)
== current_value
} else { false
}
}, // Cascade-dependent keywords, such as revert and revert-layer, // are invalid as values in a style feature, and cause the // container style query to be false. // https://drafts.csswg.org/css-conditional-5/#evaluate-a-style-range
CSSWideKeyword::Revert
| CSSWideKeyword::RevertLayer
| CSSWideKeyword::RevertRule => false,
}
},
})
}
}
/// A boolean value for a pref query. #[derive(
Clone,
Debug,
MallocSizeOf,
PartialEq,
Eq,
Parse,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToShmem,
)] #[repr(u8)] #[allow(missing_docs)] pubenum BoolValue { False, True,
}
/// Simple values we support for -moz-pref(). We don't want to deal with calc() and other /// shenanigans for now. #[derive(
Clone,
Debug,
Eq,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToShmem,
)] #[repr(u8)] pubenum MozPrefFeatureValue<I> { /// No pref value, implicitly bool, but also used to represent missing prefs. #[css(skip)]
None, /// A bool value.
Boolean(BoolValue), /// An integer value, useful for int prefs.
Integer(I), /// A string pref value.
String(crate::values::AtomString),
}
type SpecifiedMozPrefFeatureValue = MozPrefFeatureValue<crate::values::specified::Integer>; /// The computed -moz-pref() value. pubtype ComputedMozPrefFeatureValue = MozPrefFeatureValue<crate::values::computed::Integer>;
impl ToCss for MozPrefFeature { fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where
W: fmt::Write,
{ self.name.to_css(dest)?; if !matches!(self.value, MozPrefFeatureValue::None) {
dest.write_str(", ")?; self.value.to_css(dest)?;
}
Ok(())
}
}
/// Represents a condition. #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)] pubenum QueryCondition { /// A simple feature expression, implicitly parenthesized.
Feature(QueryFeatureExpression), /// A custom media query reference in a boolean context, implicitly parenthesized.
Custom(DashedIdent), /// A negation of a condition.
Not(Box<QueryCondition>), /// A set of joint operations.
Operation(Box<[QueryCondition]>, Operator), /// A condition wrapped in parenthesis.
InParens(Box<QueryCondition>), /// A <style> query.
Style(StyleQuery), /// A -moz-pref() query.
MozPref(MozPrefFeature), /// [ <function-token> <any-value>? ) ] | [ ( <any-value>? ) ]
GeneralEnclosed(String, UrlExtraData),
}
impl ToCss for QueryCondition { fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where
W: fmt::Write,
{ match *self { // NOTE(emilio): QueryFeatureExpression already includes the // parenthesis.
QueryCondition::Feature(ref f) => f.to_css(dest),
QueryCondition::Custom(ref name) => {
dest.write_char('(')?;
name.to_css(dest)?;
dest.write_char(')')
},
QueryCondition::Not(ref c) => {
dest.write_str("not ")?;
c.to_css(dest)
},
QueryCondition::InParens(ref c) => {
dest.write_char('(')?;
c.to_css(dest)?;
dest.write_char(')')
},
QueryCondition::Style(ref c) => {
dest.write_str("style(")?;
c.to_css(dest)?;
dest.write_char(')')
},
QueryCondition::MozPref(ref c) => {
dest.write_str("-moz-pref(")?;
c.to_css(dest)?;
dest.write_char(')')
},
QueryCondition::Operation(ref list, op) => { letmut iter = list.iter();
iter.next().unwrap().to_css(dest)?; for item in iter {
dest.write_char(' ')?;
op.to_css(dest)?;
dest.write_char(' ')?;
item.to_css(dest)?;
}
Ok(())
},
QueryCondition::GeneralEnclosed(ref s, _) => dest.write_str(&s),
}
}
}
/// Returns the union of all flags in the expression. This is useful for /// container queries. pubfn cumulative_flags(&self) -> FeatureFlags { letmut result = FeatureFlags::empty(); self.visit(&mut |condition| { ifletSelf::Style(..) = condition {
result.insert(FeatureFlags::STYLE);
} ifletSelf::Feature(ref f) = condition {
result.insert(f.feature_flags())
}
});
result
}
/// Parse a single condition, disallowing `or` expressions. /// /// To be used from the legacy query syntax. pubfn parse_disallow_or<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
feature_type: FeatureType,
) -> Result<Self, ParseError<'i>> { Self::parse_internal(context, input, feature_type, AllowOr::No)
}
fn parse_in_parenthesis_block<'i>(
context: &ParserContext,
input: &mut Parser<'i, '_>,
feature_type: FeatureType,
) -> Result<Self, ParseError<'i>> { // Base case. Make sure to preserve this error as it's more generally // relevant. let feature_error = match input.try_parse(|input| {
QueryFeatureExpression::parse_in_parenthesis_block(context, input, feature_type)
}) {
Ok(expr) => return Ok(Self::Feature(expr)),
Err(e) => e,
}; if static_prefs::pref!("layout.css.custom-media.enabled") { iflet Ok(custom) = input.try_parse(|input| DashedIdent::parse(context, input)) { return Ok(Self::Custom(custom));
}
} iflet Ok(inner) = Self::parse(context, input, feature_type) { return Ok(Self::InParens(Box::new(inner)));
}
Err(feature_error)
}
fn try_parse_block<'i, T, F>(
context: &ParserContext,
input: &mut Parser<'i, '_>,
start: SourcePosition,
parse: F,
) -> Option<T> where
F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i>>,
{ let nested = input.try_parse(|input| input.parse_nested_block(parse)); match nested {
Ok(nested) => Some(nested),
Err(e) => { // We're about to swallow the error in a `<general-enclosed>` // condition, so report it while we can. let loc = e.location; let error = ContextualParseError::InvalidMediaRule(input.slice_from(start), e);
context.log_css_error(loc, error);
None
},
}
}
/// Whether this condition matches the device and quirks mode. /// https://drafts.csswg.org/mediaqueries/#evaluating /// https://drafts.csswg.org/mediaqueries/#typedef-general-enclosed /// Kleene 3-valued logic is adopted here due to the introduction of /// <general-enclosed>. pubfn matches(
&self,
context: &computed::Context,
custom: &mut CustomMediaEvaluator,
attribute_tracker: &mut AttributeTracker,
) -> KleeneValue { match *self { Self::Custom(ref f) => custom.matches(f, context), Self::Feature(ref f) => f.matches(context), Self::GeneralEnclosed(ref str, ref url_data) => { self.matches_general(&str, url_data, context, custom, attribute_tracker)
}, Self::InParens(ref c) => c.matches(context, custom, attribute_tracker), Self::Not(ref c) => !c.matches(context, custom, attribute_tracker), Self::Style(ref c) => c.matches(context, attribute_tracker), Self::MozPref(ref c) => c.matches(context), Self::Operation(ref conditions, op) => {
debug_assert!(!conditions.is_empty(), "We never create an empty op"); match op {
Operator::And => KleeneValue::any_false(conditions.iter(), |c| {
c.matches(context, custom, attribute_tracker)
}),
Operator::Or => KleeneValue::any(conditions.iter(), |c| {
c.matches(context, custom, attribute_tracker)
}),
}
},
}
}
/// For a condition that was parsed as GeneralEnclosed, try applying custom-property /// substitution and re-parse the result. fn matches_general(
&self,
css_text: &str,
url_data: &UrlExtraData,
context: &computed::Context,
custom: &mut CustomMediaEvaluator,
attribute_tracker: &mut AttributeTracker,
) -> KleeneValue { // This only applies (currently, at least) to container queries. if !context.in_container_query { return KleeneValue::Unknown;
}
let stylist = context
.builder
.stylist
.expect("container query should provide a Stylist");
// Parse the text as a custom-property value to identify references. letmut input = ParserInput::new(css_text); let value = match custom_properties::SpecifiedValue::parse(
&mut Parser::new(&mut input),
None, // TODO: what Namespaces should we pass here?
url_data,
) {
Ok(val) => val,
Err(_) => return KleeneValue::Unknown,
};
// If no references, we're not going to end up with a new result, just bail out. if !value.has_references() { return KleeneValue::Unknown;
}
// Substitute var() functions if possible. let substitution_functions = custom_properties::ComputedSubstitutionFunctions::new(
Some(context.inherited_custom_properties().clone()),
None,
); let custom_properties::SubstitutionResult { css, attr_taint } = match custom_properties::substitute(
&value,
&substitution_functions,
stylist,
context,
attribute_tracker,
) {
Ok(sub) => sub,
Err(_) => return KleeneValue::Unknown,
};
// Re-parse the result as a query-condition, and evaluate it. let parser_context = ParserContext::new(
Origin::Author,
url_data,
Some(CssRuleType::Container),
ParsingMode::DEFAULT,
QuirksMode::NoQuirks, /* namespaces = */ Default::default(), /* error_reporter = */ None, /* use_counters = */ None,
attr_taint,
); letmut input = ParserInput::new(&css); let result = matchSelf::parse(
&parser_context,
&mut Parser::new(&mut input),
FeatureType::Container,
) {
Ok(Self::GeneralEnclosed(..)) => { // If the result is still GeneralEnclosed, the query is unknown.
KleeneValue::Unknown
},
Ok(query) => query.matches(context, custom, attribute_tracker),
Err(_) => KleeneValue::Unknown,
};
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.