Skip to content

Commit

Permalink
Bug 1830134 - [css-nesting] Process nested rules at cascade data rebu…
Browse files Browse the repository at this point in the history
…ild. r=firefox-style-system-reviewers,zrhoffman

More nesting plumbing. Still does nothing because we don't parse the
nested rules.

Should be trivial to prove this patch doesn't change any behavior so
far, but I want to land it on its own because it can have performance
implications.

This follows the pattern of what we do with other rules like layers and
container conditions, that is, keep the ancestor selectors in a stack,
and poke at the last one in order to replace the ancestor.

This changes the behavior of replace_parent_selector as with the newer
version of the spec, stuff like:

  div {
    .foo {
      stuff
    }
  }

Should work as `div .foo`. A test is added for this case.

Differential Revision: https://phabricator.services.mozilla.com/D176560
  • Loading branch information
emilio committed Apr 28, 2023
1 parent cf223f8 commit 789476f
Show file tree
Hide file tree
Showing 4 changed files with 209 additions and 131 deletions.
205 changes: 111 additions & 94 deletions selectors/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -845,11 +845,7 @@ impl<Impl: SelectorImpl> Selector<Impl> {
Selector(builder.build_with_specificity_and_flags(spec))
}

pub fn replace_parent_selector(&self, parent: &[Selector<Impl>]) -> Cow<Self> {
if !self.has_parent_selector() {
return Cow::Borrowed(self);
}

pub fn replace_parent_selector(&self, parent: &[Selector<Impl>]) -> Self {
// FIXME(emilio): Shouldn't allow replacing if parent has a pseudo-element selector
// or what not.
let flags = self.flags() - SelectorFlags::HAS_PARENT;
Expand Down Expand Up @@ -879,7 +875,7 @@ impl<Impl: SelectorImpl> Selector<Impl> {
return s.clone();
}
any = true;
s.replace_parent_selector(parent).into_owned()
s.replace_parent_selector(parent)
})
.collect();

Expand Down Expand Up @@ -910,7 +906,7 @@ impl<Impl: SelectorImpl> Selector<Impl> {
any = true;
RelativeSelector {
match_hint: s.match_hint,
selector: s.selector.replace_parent_selector(parent).into_owned(),
selector: s.selector.replace_parent_selector(parent),
}
})
.collect();
Expand All @@ -931,108 +927,121 @@ impl<Impl: SelectorImpl> Selector<Impl> {
parent: &[Selector<Impl>],
specificity: &mut Specificity,
) -> Selector<Impl> {
let new_selector = orig.replace_parent_selector(parent);
if matches!(new_selector, Cow::Owned(..)) {
*specificity += Specificity::from(new_selector.specificity() - orig.specificity());
if !orig.has_parent_selector() {
return orig.clone();
}
new_selector.into_owned()
let new_selector = orig.replace_parent_selector(parent);
*specificity += Specificity::from(new_selector.specificity() - orig.specificity());
new_selector
}

let iter = self.iter_raw_match_order().map(|component| {
use self::Component::*;
match *component {
LocalName(..) |
ID(..) |
Class(..) |
AttributeInNoNamespaceExists { .. } |
AttributeInNoNamespace { .. } |
AttributeOther(..) |
ExplicitUniversalType |
ExplicitAnyNamespace |
ExplicitNoNamespace |
DefaultNamespace(..) |
Namespace(..) |
Root |
Empty |
Scope |
Nth(..) |
NonTSPseudoClass(..) |
PseudoElement(..) |
Combinator(..) |
Host(None) |
Part(..) |
RelativeSelectorAnchor => component.clone(),
ParentSelector => {
specificity += parent_specificity;
Is(parent.to_vec().into_boxed_slice())
},
Negation(ref selectors) => {
Negation(
replace_parent_on_selector_list(
let mut items = if !self.has_parent_selector() {
// Implicit `&` plus descendant combinator.
let iter = self.iter_raw_match_order();
let len = iter.len() + 2;
specificity += parent_specificity;
let iter = iter
.cloned()
.chain(std::iter::once(Component::Combinator(Combinator::Descendant)))
.chain(std::iter::once(Component::Is(parent.to_vec().into_boxed_slice())));
let header = HeaderWithLength::new(specificity_and_flags, len);
UniqueArc::from_header_and_iter_with_size(header, iter, len)
} else {
let iter = self.iter_raw_match_order().map(|component| {
use self::Component::*;
match *component {
LocalName(..) |
ID(..) |
Class(..) |
AttributeInNoNamespaceExists { .. } |
AttributeInNoNamespace { .. } |
AttributeOther(..) |
ExplicitUniversalType |
ExplicitAnyNamespace |
ExplicitNoNamespace |
DefaultNamespace(..) |
Namespace(..) |
Root |
Empty |
Scope |
Nth(..) |
NonTSPseudoClass(..) |
PseudoElement(..) |
Combinator(..) |
Host(None) |
Part(..) |
RelativeSelectorAnchor => component.clone(),
ParentSelector => {
specificity += parent_specificity;
Is(parent.to_vec().into_boxed_slice())
},
Negation(ref selectors) => {
Negation(
replace_parent_on_selector_list(
selectors,
parent,
&mut specificity,
/* with_specificity = */ true,
)
.into_boxed_slice(),
)
},
Is(ref selectors) => {
Is(replace_parent_on_selector_list(
selectors,
parent,
&mut specificity,
/* with_specificity = */ true,
)
.into_boxed_slice(),
)
},
Is(ref selectors) => {
Is(replace_parent_on_selector_list(
.into_boxed_slice())
},
Where(ref selectors) => {
Where(
replace_parent_on_selector_list(
selectors,
parent,
&mut specificity,
/* with_specificity = */ false,
)
.into_boxed_slice(),
)
},
Has(ref selectors) => Has(replace_parent_on_relative_selector_list(
selectors,
parent,
&mut specificity,
/* with_specificity = */ true,
)
.into_boxed_slice())
},
Where(ref selectors) => {
Where(
replace_parent_on_selector_list(
selectors,
.into_boxed_slice()),

Host(Some(ref selector)) => Host(Some(replace_parent_on_selector(
selector,
parent,
&mut specificity,
))),
NthOf(ref data) => {
let selectors = replace_parent_on_selector_list(
data.selectors(),
parent,
&mut specificity,
/* with_specificity = */ false,
)
.into_boxed_slice(),
)
},
Has(ref selectors) => Has(replace_parent_on_relative_selector_list(
selectors,
parent,
&mut specificity,
)
.into_boxed_slice()),

Host(Some(ref selector)) => Host(Some(replace_parent_on_selector(
selector,
parent,
&mut specificity,
))),
NthOf(ref data) => {
let selectors = replace_parent_on_selector_list(
data.selectors(),
/* with_specificity = */ true,
);
NthOf(NthOfSelectorData::new(
data.nth_data(),
selectors.into_iter(),
))
},
Slotted(ref selector) => Slotted(replace_parent_on_selector(
selector,
parent,
&mut specificity,
/* with_specificity = */ true,
);
NthOf(NthOfSelectorData::new(
data.nth_data(),
selectors.into_iter(),
))
},
Slotted(ref selector) => Slotted(replace_parent_on_selector(
selector,
parent,
&mut specificity,
)),
}
});

let header = HeaderWithLength::new(specificity_and_flags, iter.len());
let mut items = UniqueArc::from_header_and_iter(header, iter);
)),
}
});
let header = HeaderWithLength::new(specificity_and_flags, iter.len());
UniqueArc::from_header_and_iter(header, iter)
};
items.header_mut().specificity = specificity.into();
Cow::Owned(Selector(items.shareable_thin()))
Selector(items.shareable_thin())
}

/// Returns count of simple selectors and combinators in the Selector.
Expand Down Expand Up @@ -4028,17 +4037,25 @@ pub mod tests {
assert_eq!(
SelectorList::from_vec(vec![child.0[0]
.replace_parent_selector(&parent.0)
.into_owned()]),
]),
parse("#foo :is(.bar, div .baz).bar").unwrap()
);

let has_child = parse("#foo:has(&.bar)").unwrap();
assert_eq!(
SelectorList::from_vec(vec![has_child.0[0]
.replace_parent_selector(&parent.0)
.into_owned()]),
]),
parse("#foo:has(:is(.bar, div .baz).bar)").unwrap()
);

let child = parse("#foo").unwrap();
assert_eq!(
SelectorList::from_vec(vec![child.0[0]
.replace_parent_selector(&parent.0)
]),
parse(":is(.bar, div .baz) #foo").unwrap()
);
}

#[test]
Expand Down
Loading

0 comments on commit 789476f

Please sign in to comment.