mirror of
https://github.com/servo/servo.git
synced 2025-07-23 15:23:42 +01:00
style: Support multiple parts in ::part() selectors.
Differential Revision: https://phabricator.services.mozilla.com/D48753
This commit is contained in:
parent
f701192e38
commit
7965ddefa6
3 changed files with 30 additions and 15 deletions
|
@ -667,7 +667,7 @@ where
|
||||||
|
|
||||||
match *selector {
|
match *selector {
|
||||||
Component::Combinator(_) => unreachable!(),
|
Component::Combinator(_) => unreachable!(),
|
||||||
Component::Part(ref part) => element.is_part(part),
|
Component::Part(ref parts) => parts.iter().all(|part| element.is_part(part)),
|
||||||
Component::Slotted(ref selector) => {
|
Component::Slotted(ref selector) => {
|
||||||
// <slots> are never flattened tree slottables.
|
// <slots> are never flattened tree slottables.
|
||||||
!element.is_html_slot_element() &&
|
!element.is_html_slot_element() &&
|
||||||
|
|
|
@ -607,7 +607,7 @@ impl<Impl: SelectorImpl> Selector<Impl> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn part(&self) -> Option<&Impl::PartName> {
|
pub fn parts(&self) -> Option<&[Impl::PartName]> {
|
||||||
if !self.is_part() {
|
if !self.is_part() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -1013,7 +1013,7 @@ pub enum Component<Impl: SelectorImpl> {
|
||||||
Slotted(Selector<Impl>),
|
Slotted(Selector<Impl>),
|
||||||
/// The `::part` pseudo-element.
|
/// The `::part` pseudo-element.
|
||||||
/// https://drafts.csswg.org/css-shadow-parts/#part
|
/// https://drafts.csswg.org/css-shadow-parts/#part
|
||||||
Part(#[shmem(field_bound)] Impl::PartName),
|
Part(#[shmem(field_bound)] Box<[Impl::PartName]>),
|
||||||
/// The `:host` pseudo-class:
|
/// The `:host` pseudo-class:
|
||||||
///
|
///
|
||||||
/// https://drafts.csswg.org/css-scoping/#host-selector
|
/// https://drafts.csswg.org/css-scoping/#host-selector
|
||||||
|
@ -1302,9 +1302,14 @@ impl<Impl: SelectorImpl> ToCss for Component<Impl> {
|
||||||
selector.to_css(dest)?;
|
selector.to_css(dest)?;
|
||||||
dest.write_char(')')
|
dest.write_char(')')
|
||||||
},
|
},
|
||||||
Part(ref part_name) => {
|
Part(ref part_names) => {
|
||||||
dest.write_str("::part(")?;
|
dest.write_str("::part(")?;
|
||||||
display_to_css_identifier(part_name, dest)?;
|
for (i, name) in part_names.iter().enumerate() {
|
||||||
|
if i != 0 {
|
||||||
|
dest.write_char(' ')?;
|
||||||
|
}
|
||||||
|
display_to_css_identifier(name, dest)?;
|
||||||
|
}
|
||||||
dest.write_char(')')
|
dest.write_char(')')
|
||||||
},
|
},
|
||||||
PseudoElement(ref p) => p.to_css(dest),
|
PseudoElement(ref p) => p.to_css(dest),
|
||||||
|
@ -1626,7 +1631,7 @@ enum SimpleSelectorParseResult<Impl: SelectorImpl> {
|
||||||
SimpleSelector(Component<Impl>),
|
SimpleSelector(Component<Impl>),
|
||||||
PseudoElement(Impl::PseudoElement),
|
PseudoElement(Impl::PseudoElement),
|
||||||
SlottedPseudo(Selector<Impl>),
|
SlottedPseudo(Selector<Impl>),
|
||||||
PartPseudo(Impl::PartName),
|
PartPseudo(Box<[Impl::PartName]>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -2029,10 +2034,10 @@ where
|
||||||
SimpleSelectorParseResult::SimpleSelector(s) => {
|
SimpleSelectorParseResult::SimpleSelector(s) => {
|
||||||
builder.push_simple_selector(s);
|
builder.push_simple_selector(s);
|
||||||
},
|
},
|
||||||
SimpleSelectorParseResult::PartPseudo(part_name) => {
|
SimpleSelectorParseResult::PartPseudo(part_names) => {
|
||||||
state.insert(SelectorParsingState::AFTER_PART);
|
state.insert(SelectorParsingState::AFTER_PART);
|
||||||
builder.push_combinator(Combinator::Part);
|
builder.push_combinator(Combinator::Part);
|
||||||
builder.push_simple_selector(Component::Part(part_name));
|
builder.push_simple_selector(Component::Part(part_names));
|
||||||
},
|
},
|
||||||
SimpleSelectorParseResult::SlottedPseudo(selector) => {
|
SimpleSelectorParseResult::SlottedPseudo(selector) => {
|
||||||
state.insert(SelectorParsingState::AFTER_SLOTTED);
|
state.insert(SelectorParsingState::AFTER_SLOTTED);
|
||||||
|
@ -2193,10 +2198,15 @@ where
|
||||||
input.new_custom_error(SelectorParseErrorKind::InvalidState)
|
input.new_custom_error(SelectorParseErrorKind::InvalidState)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let name = input.parse_nested_block(|input| {
|
let names = input.parse_nested_block(|input| {
|
||||||
Ok(input.expect_ident()?.as_ref().into())
|
let mut result = Vec::with_capacity(1);
|
||||||
|
result.push(input.expect_ident()?.as_ref().into());
|
||||||
|
while !input.is_exhausted() {
|
||||||
|
result.push(input.expect_ident()?.as_ref().into());
|
||||||
|
}
|
||||||
|
Ok(result.into_boxed_slice())
|
||||||
})?;
|
})?;
|
||||||
return Ok(Some(SimpleSelectorParseResult::PartPseudo(name)));
|
return Ok(Some(SimpleSelectorParseResult::PartPseudo(names)));
|
||||||
}
|
}
|
||||||
if P::parse_slotted(parser) && name.eq_ignore_ascii_case("slotted") {
|
if P::parse_slotted(parser) && name.eq_ignore_ascii_case("slotted") {
|
||||||
if !state.allows_slotted() {
|
if !state.allows_slotted() {
|
||||||
|
@ -3051,8 +3061,7 @@ pub mod tests {
|
||||||
|
|
||||||
assert!(parse("::part()").is_err());
|
assert!(parse("::part()").is_err());
|
||||||
assert!(parse("::part(42)").is_err());
|
assert!(parse("::part(42)").is_err());
|
||||||
// Though note https://github.com/w3c/csswg-drafts/issues/3502
|
assert!(parse("::part(foo bar)").is_ok());
|
||||||
assert!(parse("::part(foo bar)").is_err());
|
|
||||||
assert!(parse("::part(foo):hover").is_ok());
|
assert!(parse("::part(foo):hover").is_ok());
|
||||||
assert!(parse("::part(foo) + bar").is_err());
|
assert!(parse("::part(foo) + bar").is_err());
|
||||||
|
|
||||||
|
|
|
@ -2032,11 +2032,17 @@ impl CascadeData {
|
||||||
// Part is special, since given it doesn't have any
|
// Part is special, since given it doesn't have any
|
||||||
// selectors inside, it's not worth using a whole
|
// selectors inside, it's not worth using a whole
|
||||||
// SelectorMap for it.
|
// SelectorMap for it.
|
||||||
if let Some(part) = selector.part() {
|
if let Some(parts) = selector.parts() {
|
||||||
|
// ::part() has all semantics, so we just need to
|
||||||
|
// put any of them in the selector map.
|
||||||
|
//
|
||||||
|
// We choose the last one quite arbitrarily,
|
||||||
|
// expecting it's slightly more likely to be more
|
||||||
|
// specific.
|
||||||
self.part_rules
|
self.part_rules
|
||||||
.get_or_insert_with(|| Box::new(Default::default()))
|
.get_or_insert_with(|| Box::new(Default::default()))
|
||||||
.for_insertion(pseudo_element)
|
.for_insertion(pseudo_element)
|
||||||
.try_entry(part.clone())?
|
.try_entry(parts.last().unwrap().clone())?
|
||||||
.or_insert_with(SmallVec::new)
|
.or_insert_with(SmallVec::new)
|
||||||
.try_push(rule)?;
|
.try_push(rule)?;
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue