See the attached repro.
Created attachment 191160 [details]
This is a style sharing bug. StyleResolver::styleSharingCandidateMatchesHostRules only matches whether one of the elements matches host rules. Hence shared styles won't bleed from an element that ISN'T targeted by @host rules to one that IS, but can bleed from an element that IS targeted by @host rules to one that ISN'T. This is what we're seeing here.
Elements that have Shadow DOM and are targeted by @host rules should not share styles.
The reason is basically the same reason why elements with <style scoped> children should not share styles. Their styles may be different to their siblings/cousins/etc. due to rules set in the scoped style.
The question is how to trade off how precisely we compute which elements can share styles, and what extra resources it takes to do that.
At one extreme of using no extra resources (directly) we can conservatively turn off sharing for all elements with Shadow DOM. The most unfortunate side effect is that many form controls use Shadow DOM internally; those will no longer be able to share styles.
At another extreme we can add a bit to ElementRareData which tracks whether an element is targeted by an @host rule, like the "children affected by first child" bits.
To chew up more cycles but no more bytes we could broaden the meaning of Node::hasScopedHTMLStyleChild from <style scoped> elements to include Shadow DOM with <style> elements containing @host rules. In future if the syntax of @host rules is broadened to also style descendants of the host, there's strong overlap in semantics between these two: The @host rules are like a "virtual <style scoped>." It should also be straightforward to make it work with shared stylesheets added with the ShadowRoot::addStyleSheet API.
For now I am going to pursue this approach. Feedback or ideas welcome.
My initial idea is complicated by the timing of calls: Style sharing resolves that the shadow host and ordinary div can share styles before the shadow host knows it bears a style element (via ShadowRoot::registerScopedHTMLStyleChild.)
I need to understand better the relationship between style sharing and style recalculation. It seems there is basically tension between wanting to share as many styles are possible, but having to use a conservative heuristic about which styles can be shared. I hope it is not necessary to go to such extremes as disabling style sharing for all shadow hosts, or reapplying styles to the sibling list of an element which becomes a shadow host.
I ran into some obviously smelly code: ShadowRoot uses the HasScopedHTMLStyleChild flag to mean "has a style element in the subtree" so it seems methods like (un)registerScopedHTMLStyleChild and hasScopedHTMLStyleChild need to be renamed, if not these responsibilities untangled. Maybe I should pick at that scab for a while as I contemplate this.
*** Bug 112590 has been marked as a duplicate of this bug. ***