diff --git a/components/selectors/matching.rs b/components/selectors/matching.rs index 45acc13e7e1..49b7f3dbd32 100644 --- a/components/selectors/matching.rs +++ b/components/selectors/matching.rs @@ -667,7 +667,7 @@ where match *selector { 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) => { // are never flattened tree slottables. !element.is_html_slot_element() && diff --git a/components/selectors/parser.rs b/components/selectors/parser.rs index 982dff793a8..507121bbf97 100644 --- a/components/selectors/parser.rs +++ b/components/selectors/parser.rs @@ -607,7 +607,7 @@ impl Selector { } #[inline] - pub fn part(&self) -> Option<&Impl::PartName> { + pub fn parts(&self) -> Option<&[Impl::PartName]> { if !self.is_part() { return None; } @@ -1013,7 +1013,7 @@ pub enum Component { Slotted(Selector), /// The `::part` pseudo-element. /// 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: /// /// https://drafts.csswg.org/css-scoping/#host-selector @@ -1302,9 +1302,14 @@ impl ToCss for Component { selector.to_css(dest)?; dest.write_char(')') }, - Part(ref part_name) => { + Part(ref part_names) => { 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(')') }, PseudoElement(ref p) => p.to_css(dest), @@ -1626,7 +1631,7 @@ enum SimpleSelectorParseResult { SimpleSelector(Component), PseudoElement(Impl::PseudoElement), SlottedPseudo(Selector), - PartPseudo(Impl::PartName), + PartPseudo(Box<[Impl::PartName]>), } #[derive(Debug)] @@ -2029,10 +2034,10 @@ where SimpleSelectorParseResult::SimpleSelector(s) => { builder.push_simple_selector(s); }, - SimpleSelectorParseResult::PartPseudo(part_name) => { + SimpleSelectorParseResult::PartPseudo(part_names) => { state.insert(SelectorParsingState::AFTER_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) => { state.insert(SelectorParsingState::AFTER_SLOTTED); @@ -2193,10 +2198,15 @@ where input.new_custom_error(SelectorParseErrorKind::InvalidState) ); } - let name = input.parse_nested_block(|input| { - Ok(input.expect_ident()?.as_ref().into()) + let names = input.parse_nested_block(|input| { + 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 !state.allows_slotted() { @@ -3051,8 +3061,7 @@ pub mod tests { assert!(parse("::part()").is_err()); assert!(parse("::part(42)").is_err()); - // Though note https://github.com/w3c/csswg-drafts/issues/3502 - assert!(parse("::part(foo bar)").is_err()); + assert!(parse("::part(foo bar)").is_ok()); assert!(parse("::part(foo):hover").is_ok()); assert!(parse("::part(foo) + bar").is_err()); diff --git a/components/style/stylist.rs b/components/style/stylist.rs index ed9af91af14..ab6fe309f3f 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -2032,11 +2032,17 @@ impl CascadeData { // Part is special, since given it doesn't have any // selectors inside, it's not worth using a whole // 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 .get_or_insert_with(|| Box::new(Default::default())) .for_insertion(pseudo_element) - .try_entry(part.clone())? + .try_entry(parts.last().unwrap().clone())? .or_insert_with(SmallVec::new) .try_push(rule)?; } else {