Omit serializing the universal selector when possible.

If omitting the universal selector in the serialization is possible, we
should do it so we obtain a shorter serialization (to match the behavior
asserted in cssom/serialize-namespaced-type-selectors.html).

For example, if someone writes *|*::before and there is no default
namespace, we should serialize to ::before; however, if there is a
default namespace, we should serialize to *|*::before.
(This is the test case "Universal selector in any namespace followed by
pseudo element).

This matches the behavior implemented by WebKit; that one case in
particular isn't implemented by Gecko, but other cases where the
universal selector should be elided are implemented by Gecko but were not
previously by Servo.
This commit is contained in:
Jonathan Chan 2017-07-15 01:12:28 -07:00
parent 989b707623
commit 1301bcdf10
3 changed files with 77 additions and 97 deletions

View file

@ -763,19 +763,93 @@ impl<Impl: SelectorImpl> ToCss for Selector<Impl> {
// NB: A parse-order iterator is a Rev<>, which doesn't expose as_slice(),
// which we need for |split|. So we split by combinators on a match-order
// sequence and then reverse.
let mut combinators = self.iter_raw_match_order().rev().filter(|x| x.is_combinator());
let mut combinators = self.iter_raw_match_order().rev().filter(|x| x.is_combinator()).peekable();
let compound_selectors = self.iter_raw_match_order().as_slice().split(|x| x.is_combinator()).rev();
let mut combinators_exhausted = false;
for compound in compound_selectors {
debug_assert!(!combinators_exhausted);
for item in compound.iter() {
item.to_css(dest)?;
// https://drafts.csswg.org/cssom/#serializing-selectors
if !compound.is_empty() {
// 1. If there is only one simple selector in the compound selectors
// which is a universal selector, append the result of
// serializing the universal selector to s.
//
// Check if `!compound.empty()` first--this can happen if we have
// something like `... > ::before`, because we store `>` and `::`
// both as combinators internally.
//
// If we are in this case, we continue to the next iteration of the
// `for compound in compound_selectors` loop.
let (can_elide_namespace, first_non_namespace) = match &compound[0] {
&Component::ExplicitAnyNamespace |
&Component::ExplicitNoNamespace |
&Component::Namespace(_, _) => (false, 1),
&Component::DefaultNamespace(_) => (true, 1),
_ => (true, 0),
};
if first_non_namespace == compound.len() - 1 {
match (combinators.peek(), &compound[first_non_namespace]) {
// We have to be careful here, because if there is a pseudo
// element "combinator" there isn't really just the one
// simple selector. Technically this compound selector
// contains the pseudo element selector as
// well--Combinator::PseudoElement doesn't exist in the
// spec.
(Some(&&Component::Combinator(Combinator::PseudoElement)), _) => (),
(_, &Component::ExplicitUniversalType) => {
// Iterate over everything so we serialize the namespace
// too.
for simple in compound.iter() {
simple.to_css(dest)?;
}
continue
}
(_, _) => (),
}
}
// 2. Otherwise, for each simple selector in the compound selectors
// that is not a universal selector of which the namespace prefix
// maps to a namespace that is not the default namespace
// serialize the simple selector and append the result to s.
//
// See https://github.com/w3c/csswg-drafts/issues/1606, which is
// proposing to change this to match up with the behavior asserted
// in cssom/serialize-namespaced-type-selectors.html, which the
// following code tries to match.
for simple in compound.iter() {
if let Component::ExplicitUniversalType = *simple {
// Can't have a namespace followed by a pseudo-element
// selector followed by a universal selector in the same
// compound selector, so we don't have to worry about the
// real namespace being in a different `compound`.
if can_elide_namespace {
continue
}
}
simple.to_css(dest)?;
}
}
// 3. If this is not the last part of the chain of the selector
// append a single SPACE (U+0020), followed by the combinator
// ">", "+", "~", ">>", "||", as appropriate, followed by another
// single SPACE (U+0020) if the combinator was not whitespace, to
// s.
match combinators.next() {
Some(c) => c.to_css(dest)?,
None => combinators_exhausted = true,
};
// 4. If this is the last part of the chain of the selector and
// there is a pseudo-element, append "::" followed by the name of
// the pseudo-element, to s.
//
// (we handle this above)
}
Ok(())

View file

@ -1,47 +0,0 @@
[serialize-namespaced-type-selectors.htm]
type: testharness
[Universal selector followed by class]
expected: FAIL
[Universal selector followed by id]
expected: FAIL
[Universal selector followed by pseudo class]
expected: FAIL
[Universal selector followed by pseudo element]
expected: FAIL
[Universal selector followed by attribute selector]
expected: FAIL
[Universal selector in any namespace followed by class]
expected: FAIL
[Universal selector in any namespace followed by id]
expected: FAIL
[Universal selector in any namespace followed by pseudo class]
expected: FAIL
[Universal selector in any namespace followed by pseudo element]
expected: FAIL
[Universal selector in any namespace followed by attribute selector]
expected: FAIL
[Universal selector with namespace equal to default namespace followed by class]
expected: FAIL
[Universal selector with namespace equal to default namespace followed by id]
expected: FAIL
[Universal selector with namespace equal to default namespace followed by pseudo class]
expected: FAIL
[Universal selector with namespace equal to default namespace followed by pseudo element]
expected: FAIL
[Universal selector with namespace equal to default namespace followed by attribute selector]
expected: FAIL

View file

@ -1,47 +0,0 @@
[serialize-namespaced-type-selectors.html]
type: testharness
[Universal selector followed by class]
expected: FAIL
[Universal selector followed by id]
expected: FAIL
[Universal selector followed by pseudo class]
expected: FAIL
[Universal selector followed by pseudo element]
expected: FAIL
[Universal selector followed by attribute selector]
expected: FAIL
[Universal selector in any namespace followed by class]
expected: FAIL
[Universal selector in any namespace followed by id]
expected: FAIL
[Universal selector in any namespace followed by pseudo class]
expected: FAIL
[Universal selector in any namespace followed by pseudo element]
expected: FAIL
[Universal selector in any namespace followed by attribute selector]
expected: FAIL
[Universal selector with namespace equal to default namespace followed by class]
expected: FAIL
[Universal selector with namespace equal to default namespace followed by id]
expected: FAIL
[Universal selector with namespace equal to default namespace followed by pseudo class]
expected: FAIL
[Universal selector with namespace equal to default namespace followed by pseudo element]
expected: FAIL
[Universal selector with namespace equal to default namespace followed by attribute selector]
expected: FAIL