Bug 1854658 - Fix the part combinator to deal with exportparts correctly. r=zrhoffman

We need to jump to an element in the correct scope just like ::slotted()
does.

Factor that code to its own function both so that it's easier to reason
about and so that the code for that function remains small. At the end
of the day other combinators like descendant or sibling get executed a
ton of times (while pseudo-elements only jump once).

Differential Revision: https://phabricator.services.mozilla.com/D189478
This commit is contained in:
Emilio Cobos Álvarez 2023-10-02 11:51:03 +00:00
Родитель 338171a737
Коммит 0e00ba901b
3 изменённых файлов: 67 добавлений и 12 удалений

Просмотреть файл

@ -646,6 +646,37 @@ enum Rightmost {
No,
}
fn host_for_part<E>(element: &E, context: &MatchingContext<E::Impl>) -> Option<E>
where
E: Element,
{
let scope = context.current_host;
let mut curr = element.containing_shadow_host()?;
if scope == Some(curr.opaque()) {
return Some(curr);
}
loop {
let parent = curr.containing_shadow_host();
if parent.as_ref().map(|h| h.opaque()) == scope {
return Some(curr);
}
curr = parent?;
}
}
fn assigned_slot<E>(element: &E, context: &MatchingContext<E::Impl>) -> Option<E>
where
E: Element,
{
debug_assert!(element.assigned_slot().map_or(true, |s| s.is_html_slot_element()));
let scope = context.current_host?;
let mut current_slot = element.assigned_slot()?;
while current_slot.containing_shadow_host().unwrap().opaque() != scope {
current_slot = current_slot.assigned_slot()?;
}
Some(current_slot)
}
#[inline(always)]
fn next_element_for_combinator<E>(
element: &E,
@ -690,18 +721,8 @@ where
element.containing_shadow_host()
},
Combinator::Part => element.containing_shadow_host(),
Combinator::SlotAssignment => {
debug_assert!(element
.assigned_slot()
.map_or(true, |s| s.is_html_slot_element()));
let scope = context.current_host?;
let mut current_slot = element.assigned_slot()?;
while current_slot.containing_shadow_host().unwrap().opaque() != scope {
current_slot = current_slot.assigned_slot()?;
}
Some(current_slot)
},
Combinator::Part => host_for_part(element, context),
Combinator::SlotAssignment => assigned_slot(element, context),
Combinator::PseudoElement => element.pseudo_element_originating_element(),
}
}

Просмотреть файл

@ -0,0 +1,3 @@
<!doctype html>
<title>CSS Test Reference</title>
<span style="color: green">Should be green</span>

Просмотреть файл

@ -0,0 +1,31 @@
<!doctype html>
<meta charset="utf-8">
<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
<link rel="author" href="https://mozilla.org" title="Mozilla">
<link rel="help" href="https://drafts.csswg.org/css-shadow-parts/">
<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1854658">
<link rel="match" href="exportparts-different-scope-ref.html">
<style>
my-foo::part(text) { color: green; }
my-bar::part(text) { color: red; background-color: red; }
</style>
<my-foo></my-foo>
<script>
customElements.define('my-foo', class extends HTMLElement {
constructor(){
super()
this.attachShadow({mode: 'closed'}).innerHTML = `
<my-bar exportparts="text"></my-bar>
`;
}
})
customElements.define('my-bar', class extends HTMLElement {
constructor(){
super()
this.attachShadow({mode: 'closed'}).innerHTML = `
<span part="text">Should be green</span>
`;
}
})
</script>