mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-13 05:15:45 +00:00
Bug 1499386 - Implement @supports selector() syntax. r=heycam
This implements the selector(<complex-selector>) syntax for @supports. See https://github.com/w3c/csswg-drafts/issues/3207 for explainer and discussion. Probably would should wait for that to be sorted out to land this, or maybe we should put it behind a pref to get the code landed and change our implementation if the discussion there leads to a change. Differential Revision: https://phabricator.services.mozilla.com/D8864 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
4dfe346a4b
commit
81d70d88d1
3
layout/reftests/bugs/1499386-ref.html
Normal file
3
layout/reftests/bugs/1499386-ref.html
Normal file
@ -0,0 +1,3 @@
|
||||
<style>
|
||||
:root { background: green }
|
||||
</style>
|
6
layout/reftests/bugs/1499386.html
Normal file
6
layout/reftests/bugs/1499386.html
Normal file
@ -0,0 +1,6 @@
|
||||
<!doctype html>
|
||||
<style>
|
||||
@supports selector(div) {
|
||||
:root { background: green }
|
||||
}
|
||||
</style>
|
@ -2087,3 +2087,5 @@ fuzzy(0-1,0-625) == 1466638-1.html 1466638-1-ref.html
|
||||
test-pref(layout.css.contain.enabled,true) == 1483946.html 1483946-ref.html
|
||||
test-pref(layout.css.visited_links_enabled,false) == 1488155.html 1488155-ref.html
|
||||
== 1492660-1.html 1492660-1-ref.html
|
||||
pref(layout.css.supports-selector.enabled,true) == 1499386.html 1499386-ref.html
|
||||
pref(layout.css.supports-selector.enabled,false) != 1499386.html 1499386-ref.html
|
||||
|
@ -712,6 +712,18 @@ VARCACHE_PREF(
|
||||
bool, false
|
||||
)
|
||||
|
||||
#ifdef NIGHTLY_BUILD
|
||||
# define PREF_VALUE true
|
||||
#else
|
||||
# define PREF_VALUE false
|
||||
#endif
|
||||
VARCACHE_PREF(
|
||||
"layout.css.supports-selector.enabled",
|
||||
layout_css_supports_selector_enabled,
|
||||
bool, PREF_VALUE
|
||||
)
|
||||
#undef PREF_VALUE
|
||||
|
||||
// Pref to control whether @-moz-document url-prefix() is parsed in content
|
||||
// pages. Only effective when layout.css.moz-document.content.enabled is false.
|
||||
#ifdef EARLY_BETA_OR_EARLIER
|
||||
|
@ -259,8 +259,13 @@ where
|
||||
Impl: SelectorImpl,
|
||||
{
|
||||
let location = input.current_source_location();
|
||||
let selector = Selector::parse(parser, input)?;
|
||||
// Ensure they're actually all compound selectors.
|
||||
let selector = parse_selector(parser, input)?;
|
||||
|
||||
// Ensure they're actually all compound selectors without pseudo-elements.
|
||||
if selector.has_pseudo_element() {
|
||||
return Err(location.new_custom_error(SelectorParseErrorKind::PseudoElementInComplexSelector));
|
||||
}
|
||||
|
||||
if selector.iter_raw_match_order().any(|s| s.is_combinator()) {
|
||||
return Err(location.new_custom_error(SelectorParseErrorKind::NonCompoundSelector));
|
||||
}
|
||||
@ -1397,6 +1402,7 @@ where
|
||||
|
||||
impl<Impl: SelectorImpl> Selector<Impl> {
|
||||
/// Parse a selector, without any pseudo-element.
|
||||
#[inline]
|
||||
pub fn parse<'i, 't, P>(
|
||||
parser: &P,
|
||||
input: &mut CssParser<'i, 't>,
|
||||
@ -1404,12 +1410,7 @@ impl<Impl: SelectorImpl> Selector<Impl> {
|
||||
where
|
||||
P: Parser<'i, Impl = Impl>,
|
||||
{
|
||||
let selector = parse_selector(parser, input)?;
|
||||
if selector.has_pseudo_element() {
|
||||
let e = SelectorParseErrorKind::PseudoElementInComplexSelector;
|
||||
return Err(input.new_custom_error(e));
|
||||
}
|
||||
Ok(selector)
|
||||
parse_selector(parser, input)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -277,7 +277,6 @@ impl CssRule {
|
||||
|
||||
// nested rules are in the body state
|
||||
let mut rule_parser = TopLevelRuleParser {
|
||||
stylesheet_origin: parent_stylesheet_contents.origin,
|
||||
context,
|
||||
shared_lock: &shared_lock,
|
||||
loader,
|
||||
|
@ -19,7 +19,7 @@ use servo_arc::Arc;
|
||||
use shared_lock::{Locked, SharedRwLock};
|
||||
use str::starts_with_ignore_ascii_case;
|
||||
use style_traits::{ParseError, StyleParseErrorKind};
|
||||
use stylesheets::{CssRule, CssRuleType, CssRules, Origin, RulesMutateError, StylesheetLoader};
|
||||
use stylesheets::{CssRule, CssRuleType, CssRules, RulesMutateError, StylesheetLoader};
|
||||
use stylesheets::{DocumentRule, FontFeatureValuesRule, KeyframesRule, MediaRule};
|
||||
use stylesheets::{NamespaceRule, PageRule, StyleRule, SupportsRule, ViewportRule};
|
||||
use stylesheets::document_rule::DocumentCondition;
|
||||
@ -41,8 +41,6 @@ pub struct InsertRuleContext<'a> {
|
||||
|
||||
/// The parser for the top-level rules in a stylesheet.
|
||||
pub struct TopLevelRuleParser<'a> {
|
||||
/// The origin of the stylesheet we're parsing.
|
||||
pub stylesheet_origin: Origin,
|
||||
/// A reference to the lock we need to use to create rules.
|
||||
pub shared_lock: &'a SharedRwLock,
|
||||
/// A reference to a stylesheet loader if applicable, for `@import` rules.
|
||||
@ -69,7 +67,6 @@ pub struct TopLevelRuleParser<'a> {
|
||||
impl<'b> TopLevelRuleParser<'b> {
|
||||
fn nested<'a: 'b>(&'a self) -> NestedRuleParser<'a, 'b> {
|
||||
NestedRuleParser {
|
||||
stylesheet_origin: self.stylesheet_origin,
|
||||
shared_lock: self.shared_lock,
|
||||
context: &self.context,
|
||||
namespaces: &self.namespaces,
|
||||
@ -325,7 +322,6 @@ impl<'a, 'i> QualifiedRuleParser<'i> for TopLevelRuleParser<'a> {
|
||||
|
||||
#[derive(Clone)] // shallow, relatively cheap .clone
|
||||
struct NestedRuleParser<'a, 'b: 'a> {
|
||||
stylesheet_origin: Origin,
|
||||
shared_lock: &'a SharedRwLock,
|
||||
context: &'a ParserContext<'b>,
|
||||
namespaces: &'a Namespaces,
|
||||
@ -340,7 +336,6 @@ impl<'a, 'b> NestedRuleParser<'a, 'b> {
|
||||
let context = ParserContext::new_with_rule_type(self.context, rule_type, self.namespaces);
|
||||
|
||||
let nested_parser = NestedRuleParser {
|
||||
stylesheet_origin: self.stylesheet_origin,
|
||||
shared_lock: self.shared_lock,
|
||||
context: &context,
|
||||
namespaces: self.namespaces,
|
||||
@ -501,7 +496,7 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
|
||||
self.namespaces,
|
||||
);
|
||||
|
||||
let enabled = condition.eval(&eval_context);
|
||||
let enabled = condition.eval(&eval_context, self.namespaces);
|
||||
Ok(CssRule::Supports(Arc::new(self.shared_lock.wrap(
|
||||
SupportsRule {
|
||||
condition,
|
||||
@ -577,7 +572,7 @@ impl<'a, 'b, 'i> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'b> {
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Self::Prelude, ParseError<'i>> {
|
||||
let selector_parser = SelectorParser {
|
||||
stylesheet_origin: self.stylesheet_origin,
|
||||
stylesheet_origin: self.context.stylesheet_origin,
|
||||
namespaces: self.namespaces,
|
||||
url_data: Some(self.context.url_data),
|
||||
};
|
||||
|
@ -374,7 +374,6 @@ impl Stylesheet {
|
||||
);
|
||||
|
||||
let rule_parser = TopLevelRuleParser {
|
||||
stylesheet_origin: origin,
|
||||
shared_lock,
|
||||
loader: stylesheet_loader,
|
||||
context,
|
||||
|
@ -12,6 +12,8 @@ use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
|
||||
use parser::ParserContext;
|
||||
use properties::{PropertyDeclaration, PropertyId, SourcePropertyDeclaration};
|
||||
use selectors::parser::SelectorParseErrorKind;
|
||||
use selector_parser::{SelectorImpl, SelectorParser};
|
||||
use selectors::parser::Selector;
|
||||
use servo_arc::Arc;
|
||||
use shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked};
|
||||
use shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
|
||||
@ -19,8 +21,8 @@ use std::ffi::{CStr, CString};
|
||||
use std::fmt::{self, Write};
|
||||
use std::str;
|
||||
use str::CssStringWriter;
|
||||
use style_traits::{CssWriter, ParseError, ToCss};
|
||||
use stylesheets::{CssRuleType, CssRules};
|
||||
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
|
||||
use stylesheets::{CssRuleType, CssRules, Namespaces};
|
||||
|
||||
/// An [`@supports`][supports] rule.
|
||||
///
|
||||
@ -87,6 +89,8 @@ pub enum SupportsCondition {
|
||||
Or(Vec<SupportsCondition>),
|
||||
/// `property-ident: value` (value can be any tokens)
|
||||
Declaration(Declaration),
|
||||
/// A `selector()` function.
|
||||
Selector(RawSelector),
|
||||
/// `-moz-bool-pref("pref-name")`
|
||||
/// Since we need to pass it through FFI to get the pref value,
|
||||
/// we store it as CString directly.
|
||||
@ -99,8 +103,8 @@ impl SupportsCondition {
|
||||
/// Parse a condition
|
||||
///
|
||||
/// <https://drafts.csswg.org/css-conditional/#supports_condition>
|
||||
pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<SupportsCondition, ParseError<'i>> {
|
||||
if let Ok(_) = input.try(|i| i.expect_ident_matching("not")) {
|
||||
pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
|
||||
if input.try(|i| i.expect_ident_matching("not")).is_ok() {
|
||||
let inner = SupportsCondition::parse_in_parens(input)?;
|
||||
return Ok(SupportsCondition::Not(Box::new(inner)));
|
||||
}
|
||||
@ -109,10 +113,8 @@ impl SupportsCondition {
|
||||
|
||||
let location = input.current_source_location();
|
||||
let (keyword, wrapper) = match input.next() {
|
||||
Err(_) => {
|
||||
// End of input
|
||||
return Ok(in_parens);
|
||||
},
|
||||
// End of input
|
||||
Err(..) => return Ok(in_parens),
|
||||
Ok(&Token::Ident(ref ident)) => {
|
||||
match_ignore_ascii_case! { &ident,
|
||||
"and" => ("and", SupportsCondition::And as fn(_) -> _),
|
||||
@ -132,17 +134,48 @@ impl SupportsCondition {
|
||||
.is_err()
|
||||
{
|
||||
// Did not find the expected keyword.
|
||||
// If we found some other token,
|
||||
// it will be rejected by `Parser::parse_entirely` somewhere up the stack.
|
||||
// If we found some other token, it will be rejected by
|
||||
// `Parser::parse_entirely` somewhere up the stack.
|
||||
return Ok(wrapper(conditions));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses a functional supports condition.
|
||||
fn parse_functional<'i, 't>(
|
||||
function: &str,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
match_ignore_ascii_case!{ function,
|
||||
// Although this is an internal syntax, it is not necessary
|
||||
// to check parsing context as far as we accept any
|
||||
// unexpected token as future syntax, and evaluate it to
|
||||
// false when not in chrome / ua sheet.
|
||||
// See https://drafts.csswg.org/css-conditional-3/#general_enclosed
|
||||
"-moz-bool-pref" => {
|
||||
let name = {
|
||||
let name = input.expect_string()?;
|
||||
CString::new(name.as_bytes())
|
||||
}.map_err(|_| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))?;
|
||||
Ok(SupportsCondition::MozBoolPref(name))
|
||||
}
|
||||
"selector" => {
|
||||
let pos = input.position();
|
||||
consume_any_value(input)?;
|
||||
Ok(SupportsCondition::Selector(RawSelector(
|
||||
input.slice_from(pos).to_owned()
|
||||
)))
|
||||
}
|
||||
_ => {
|
||||
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://drafts.csswg.org/css-conditional-3/#supports_condition_in_parens>
|
||||
fn parse_in_parens<'i, 't>(
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<SupportsCondition, ParseError<'i>> {
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
// Whitespace is normally taken care of in `Parser::next`,
|
||||
// but we want to not include it in `pos` for the SupportsCondition::FutureSyntax cases.
|
||||
while input.try(Parser::expect_whitespace).is_ok() {}
|
||||
@ -151,46 +184,45 @@ impl SupportsCondition {
|
||||
// FIXME: remove clone() when lifetimes are non-lexical
|
||||
match input.next()?.clone() {
|
||||
Token::ParenthesisBlock => {
|
||||
let nested = input
|
||||
.try(|input| input.parse_nested_block(|i| parse_condition_or_declaration(i)));
|
||||
let nested = input.try(|input| {
|
||||
input.parse_nested_block(parse_condition_or_declaration)
|
||||
});
|
||||
if nested.is_ok() {
|
||||
return nested;
|
||||
}
|
||||
},
|
||||
Token::Function(ident) => {
|
||||
// Although this is an internal syntax, it is not necessary to check
|
||||
// parsing context as far as we accept any unexpected token as future
|
||||
// syntax, and evaluate it to false when not in chrome / ua sheet.
|
||||
// See https://drafts.csswg.org/css-conditional-3/#general_enclosed
|
||||
if ident.eq_ignore_ascii_case("-moz-bool-pref") {
|
||||
if let Ok(name) = input.try(|i| {
|
||||
i.parse_nested_block(|i| {
|
||||
i.expect_string()
|
||||
.map(|s| s.to_string())
|
||||
.map_err(CssParseError::<()>::from)
|
||||
}).and_then(|s| CString::new(s).map_err(|_| location.new_custom_error(())))
|
||||
}) {
|
||||
return Ok(SupportsCondition::MozBoolPref(name));
|
||||
}
|
||||
let nested = input.try(|input| {
|
||||
input.parse_nested_block(|input| {
|
||||
SupportsCondition::parse_functional(&ident, input)
|
||||
})
|
||||
});
|
||||
if nested.is_ok() {
|
||||
return nested;
|
||||
}
|
||||
},
|
||||
t => return Err(location.new_unexpected_token_error(t)),
|
||||
}
|
||||
input.parse_nested_block(|i| consume_any_value(i))?;
|
||||
input.parse_nested_block(consume_any_value)?;
|
||||
Ok(SupportsCondition::FutureSyntax(
|
||||
input.slice_from(pos).to_owned(),
|
||||
))
|
||||
}
|
||||
|
||||
/// Evaluate a supports condition
|
||||
pub fn eval(&self, cx: &ParserContext) -> bool {
|
||||
pub fn eval(
|
||||
&self,
|
||||
cx: &ParserContext,
|
||||
namespaces: &Namespaces,
|
||||
) -> bool {
|
||||
match *self {
|
||||
SupportsCondition::Not(ref cond) => !cond.eval(cx),
|
||||
SupportsCondition::Parenthesized(ref cond) => cond.eval(cx),
|
||||
SupportsCondition::And(ref vec) => vec.iter().all(|c| c.eval(cx)),
|
||||
SupportsCondition::Or(ref vec) => vec.iter().any(|c| c.eval(cx)),
|
||||
SupportsCondition::Not(ref cond) => !cond.eval(cx, namespaces),
|
||||
SupportsCondition::Parenthesized(ref cond) => cond.eval(cx, namespaces),
|
||||
SupportsCondition::And(ref vec) => vec.iter().all(|c| c.eval(cx, namespaces)),
|
||||
SupportsCondition::Or(ref vec) => vec.iter().any(|c| c.eval(cx, namespaces)),
|
||||
SupportsCondition::Declaration(ref decl) => decl.eval(cx),
|
||||
SupportsCondition::MozBoolPref(ref name) => eval_moz_bool_pref(name, cx),
|
||||
SupportsCondition::Selector(ref selector) => selector.eval(cx, namespaces),
|
||||
SupportsCondition::FutureSyntax(_) => false,
|
||||
}
|
||||
}
|
||||
@ -265,6 +297,11 @@ impl ToCss for SupportsCondition {
|
||||
decl.to_css(dest)?;
|
||||
dest.write_str(")")
|
||||
},
|
||||
SupportsCondition::Selector(ref selector) => {
|
||||
dest.write_str("selector(")?;
|
||||
selector.to_css(dest)?;
|
||||
dest.write_str(")")
|
||||
}
|
||||
SupportsCondition::MozBoolPref(ref name) => {
|
||||
dest.write_str("-moz-bool-pref(")?;
|
||||
let name =
|
||||
@ -277,6 +314,68 @@ impl ToCss for SupportsCondition {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
/// A possibly-invalid CSS selector.
|
||||
pub struct RawSelector(pub String);
|
||||
|
||||
impl ToCss for RawSelector {
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
dest.write_str(&self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl RawSelector {
|
||||
/// Tries to evaluate a `selector()` function.
|
||||
pub fn eval(
|
||||
&self,
|
||||
context: &ParserContext,
|
||||
namespaces: &Namespaces,
|
||||
) -> bool {
|
||||
#[cfg(feature = "gecko")]
|
||||
{
|
||||
if unsafe { !::gecko_bindings::structs::StaticPrefs_sVarCache_layout_css_supports_selector_enabled } {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
let mut input = ParserInput::new(&self.0);
|
||||
let mut input = Parser::new(&mut input);
|
||||
input.parse_entirely(|input| -> Result<(), CssParseError<()>> {
|
||||
let parser = SelectorParser {
|
||||
namespaces,
|
||||
stylesheet_origin: context.stylesheet_origin,
|
||||
url_data: Some(context.url_data),
|
||||
};
|
||||
|
||||
let selector = Selector::<SelectorImpl>::parse(&parser, input)
|
||||
.map_err(|_| input.new_custom_error(()))?;
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
{
|
||||
use selectors::parser::Component;
|
||||
use selector_parser::PseudoElement;
|
||||
|
||||
let has_any_unknown_webkit_pseudo =
|
||||
selector.has_pseudo_element() &&
|
||||
selector.iter_raw_match_order().any(|component| {
|
||||
matches!(
|
||||
*component,
|
||||
Component::PseudoElement(PseudoElement::UnknownWebkit(..))
|
||||
)
|
||||
});
|
||||
if has_any_unknown_webkit_pseudo {
|
||||
return Err(input.new_custom_error(()));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}).is_ok()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
/// A possibly-invalid property declaration
|
||||
pub struct Declaration(pub String);
|
||||
@ -313,21 +412,20 @@ impl Declaration {
|
||||
|
||||
let mut input = ParserInput::new(&self.0);
|
||||
let mut input = Parser::new(&mut input);
|
||||
input
|
||||
.parse_entirely(|input| -> Result<(), CssParseError<()>> {
|
||||
let prop = input.expect_ident_cloned().unwrap();
|
||||
input.expect_colon().unwrap();
|
||||
input.parse_entirely(|input| -> Result<(), CssParseError<()>> {
|
||||
let prop = input.expect_ident_cloned().unwrap();
|
||||
input.expect_colon().unwrap();
|
||||
|
||||
let id =
|
||||
PropertyId::parse(&prop, context).map_err(|_| input.new_custom_error(()))?;
|
||||
let id =
|
||||
PropertyId::parse(&prop, context).map_err(|_| input.new_custom_error(()))?;
|
||||
|
||||
let mut declarations = SourcePropertyDeclaration::new();
|
||||
input.parse_until_before(Delimiter::Bang, |input| {
|
||||
PropertyDeclaration::parse_into(&mut declarations, id, &context, input)
|
||||
.map_err(|_| input.new_custom_error(()))
|
||||
})?;
|
||||
let _ = input.try(parse_important);
|
||||
Ok(())
|
||||
}).is_ok()
|
||||
let mut declarations = SourcePropertyDeclaration::new();
|
||||
input.parse_until_before(Delimiter::Bang, |input| {
|
||||
PropertyDeclaration::parse_into(&mut declarations, id, &context, input)
|
||||
.map_err(|_| input.new_custom_error(()))
|
||||
})?;
|
||||
let _ = input.try(parse_important);
|
||||
Ok(())
|
||||
}).is_ok()
|
||||
}
|
||||
}
|
||||
|
@ -4499,24 +4499,26 @@ pub extern "C" fn Servo_CSSSupports(cond: *const nsACString) -> bool {
|
||||
let condition = unsafe { cond.as_ref().unwrap().as_str_unchecked() };
|
||||
let mut input = ParserInput::new(&condition);
|
||||
let mut input = Parser::new(&mut input);
|
||||
let cond = input.parse_entirely(|i| parse_condition_or_declaration(i));
|
||||
if let Ok(cond) = cond {
|
||||
let url_data = unsafe { dummy_url_data() };
|
||||
// NOTE(emilio): The supports API is not associated to any stylesheet,
|
||||
// so the fact that there is no namespace map here is fine.
|
||||
let context = ParserContext::new_for_cssom(
|
||||
url_data,
|
||||
Some(CssRuleType::Style),
|
||||
ParsingMode::DEFAULT,
|
||||
QuirksMode::NoQuirks,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
let cond = match input.parse_entirely(parse_condition_or_declaration) {
|
||||
Ok(c) => c,
|
||||
Err(..) => return false,
|
||||
};
|
||||
|
||||
cond.eval(&context)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
let url_data = unsafe { dummy_url_data() };
|
||||
|
||||
// NOTE(emilio): The supports API is not associated to any stylesheet,
|
||||
// so the fact that there is no namespace map here is fine.
|
||||
let context = ParserContext::new_for_cssom(
|
||||
url_data,
|
||||
Some(CssRuleType::Style),
|
||||
ParsingMode::DEFAULT,
|
||||
QuirksMode::NoQuirks,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
|
||||
let namespaces = Default::default();
|
||||
cond.eval(&context, &namespaces)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
@ -0,0 +1,18 @@
|
||||
<!doctype html>
|
||||
<title>CSS Conditional Test: @supports selector() with pseudo-elements.</title>
|
||||
<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
|
||||
<link rel="author" href="https://mozilla.org" title="Mozilla">
|
||||
<link rel="help" href="https://drafts.csswg.org/css-conditional/#at-supports">
|
||||
<link rel="match" href="at-supports-001-ref.html">
|
||||
<style>
|
||||
div {
|
||||
background-color:red;
|
||||
height:100px;
|
||||
width:100px;
|
||||
}
|
||||
@supports selector(::before) {
|
||||
div { background: green };
|
||||
}
|
||||
</style>
|
||||
<p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
|
||||
<div></div>
|
@ -0,0 +1,18 @@
|
||||
<!doctype html>
|
||||
<title>CSS Conditional Test: @supports selector() with -webkit- unknown pseudo-elements and negation.</title>
|
||||
<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
|
||||
<link rel="author" href="https://mozilla.org" title="Mozilla">
|
||||
<link rel="help" href="https://drafts.csswg.org/css-conditional/#at-supports">
|
||||
<link rel="match" href="at-supports-001-ref.html">
|
||||
<style>
|
||||
div {
|
||||
background-color:red;
|
||||
height:100px;
|
||||
width:100px;
|
||||
}
|
||||
@supports not selector(::-webkit-unknown-pseudo) {
|
||||
div { background: green };
|
||||
}
|
||||
</style>
|
||||
<p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
|
||||
<div></div>
|
@ -0,0 +1,18 @@
|
||||
<!doctype html>
|
||||
<title>CSS Conditional Test: @supports selector() with multiple selectors doesn't work.</title>
|
||||
<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
|
||||
<link rel="author" href="https://mozilla.org" title="Mozilla">
|
||||
<link rel="help" href="https://drafts.csswg.org/css-conditional/#at-supports">
|
||||
<link rel="match" href="at-supports-001-ref.html">
|
||||
<style>
|
||||
div {
|
||||
background-color: green;
|
||||
height: 100px;
|
||||
width: 100px;
|
||||
}
|
||||
@supports selector(div, div) {
|
||||
div { background: red };
|
||||
}
|
||||
</style>
|
||||
<p>Test passes if there is a <strong>filled green square</strong> and <strong>no red</strong>.</p>
|
||||
<div></div>
|
@ -35,4 +35,12 @@
|
||||
assert_equals(CSS.supports("width", "blah"), false, "CSS.supports: two argument form fails for invalid value");
|
||||
assert_equals(CSS.supports("--foo", "blah"), true, "CSS.supports: two argument form succeeds for custom property");
|
||||
}, "CSS.supports, two argument form");
|
||||
test(function () {
|
||||
assert_equals(CSS.supports("selector(div)"), true, "CSS.supports: selector() function accepts a selector");
|
||||
assert_equals(CSS.supports("selector(div, div)"), false, "CSS.supports: selector() function doesn't accept a selector list");
|
||||
assert_equals(CSS.supports("selector(::-webkit-unknown-pseudo-element)"), false, "CSS.supports: selector() function rejects unknown webkit pseudo-elements.");
|
||||
assert_equals(CSS.supports("selector(::before)"), true, "CSS.supports: selector() function accepts known pseudo-elements");
|
||||
assert_equals(CSS.supports("selector(div + .c)"), true, "CSS.supports: selector() with simple combinators");
|
||||
assert_equals(CSS.supports("selector(div | .c)"), false, "CSS.supports: selector() with unknown combinators");
|
||||
}, "CSS.supports, selector function");
|
||||
</script>
|
||||
|
Loading…
Reference in New Issue
Block a user