LibWeb: Apply shadow root style sheets in StyleComputer

Now, if an element belongs to a shadow tree, we use only the style
sheets from the corresponding shadow root during style computation,
instead of using all available style sheets as was the case
previously.

The only exception is the user agent style sheets, which are still
taken into account for all elements.

Tests/LibWeb/Layout/input/input-element-with-display-inline.html
is affected because style of document no longer affects shadow tree
of input element, like it is supposed to be.

Co-authored-by: Simon Wanner <simon+git@skyrising.xyz>
This commit is contained in:
Aliaksandr Kalenik 2023-03-19 17:01:26 +01:00 committed by Andreas Kling
parent 91ec1d6f95
commit 33294aea86
Notes: sideshowbarker 2024-07-17 22:09:47 +09:00
7 changed files with 99 additions and 27 deletions

View file

@ -1,15 +1,15 @@
Viewport <#document> at (0,0) content-size 800x600 children: not-inline
BlockContainer <html> at (1,1) content-size 798x46 [BFC] children: not-inline
BlockContainer <body> at (10,10) content-size 780x28 children: inline
frag 0 from BlockContainer start: 0, length: 0, rect: [11,11 200x26] baseline: 28
BlockContainer <input> at (11,11) content-size 200x26 inline-block [BFC] children: not-inline
Box <div> at (13,12) content-size 196x24 flex-container(row) [FFC] children: not-inline
BlockContainer <div> at (14,13) content-size 194x22 flex-item [BFC] children: inline
BlockContainer <html> at (1,1) content-size 798x44 [BFC] children: not-inline
BlockContainer <body> at (10,10) content-size 780x26 children: inline
frag 0 from BlockContainer start: 0, length: 0, rect: [11,11 200x24] baseline: 26
BlockContainer <input> at (11,11) content-size 200x24 inline-block [BFC] children: not-inline
Box <div> at (13,12) content-size 196x22 flex-container(row) [FFC] children: not-inline
BlockContainer <div> at (13,12) content-size 196x22 flex-item [BFC] children: inline
TextNode <#text>
ViewportPaintable (Viewport<#document>) [0,0 800x600]
PaintableWithLines (BlockContainer<HTML>) [0,0 800x48]
PaintableWithLines (BlockContainer<BODY>) [9,9 782x30]
PaintableWithLines (BlockContainer<INPUT>) [10,10 202x28]
PaintableBox (Box<DIV>) [11,11 200x26]
PaintableWithLines (BlockContainer<DIV>) [13,12 196x24]
PaintableWithLines (BlockContainer<HTML>) [0,0 800x46]
PaintableWithLines (BlockContainer<BODY>) [9,9 782x28]
PaintableWithLines (BlockContainer<INPUT>) [10,10 202x26]
PaintableBox (Box<DIV>) [11,11 200x24]
PaintableWithLines (BlockContainer<DIV>) [13,12 196x22]

View file

@ -0,0 +1 @@
border of #test = (2px solid rgb(173, 255, 47))

View file

@ -0,0 +1,37 @@
<script src="include.js"></script>
<style>
#test {
border: 10px solid black;
}
</style>
<my-custom-element></my-custom-element>
<script>
test(() => {
class MyCustomElement extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
const test = document.createElement('div');
test.setAttribute('id', 'test');
const sheet = new CSSStyleSheet();
sheet.replaceSync(`
#test {
width: 100px;
height: 100px;
background-color: magenta;
border: 2px solid greenyellow;
}`);
shadow.adoptedStyleSheets = [sheet];
shadow.appendChild(test);
const testComputedStyle = getComputedStyle(test);
println(`border of #test = (${testComputedStyle.getPropertyValue("border")})`);
}
}
customElements.define('my-custom-element', MyCustomElement);
});
</script>

View file

@ -63,6 +63,7 @@
#include <LibWeb/CSS/StyleValues/UnsetStyleValue.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/DOM/Element.h>
#include <LibWeb/DOM/ShadowRoot.h>
#include <LibWeb/HTML/HTMLBRElement.h>
#include <LibWeb/HTML/HTMLHtmlElement.h>
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
@ -255,19 +256,25 @@ template<typename Callback>
void StyleComputer::for_each_stylesheet(CascadeOrigin cascade_origin, Callback callback) const
{
if (cascade_origin == CascadeOrigin::UserAgent) {
callback(default_stylesheet(document()));
callback(default_stylesheet(document()), {});
if (document().in_quirks_mode())
callback(quirks_mode_stylesheet(document()));
callback(mathml_stylesheet(document()));
callback(svg_stylesheet(document()));
callback(quirks_mode_stylesheet(document()), {});
callback(mathml_stylesheet(document()), {});
callback(svg_stylesheet(document()), {});
}
if (cascade_origin == CascadeOrigin::User) {
if (m_user_style_sheet)
callback(*m_user_style_sheet);
callback(*m_user_style_sheet, {});
}
if (cascade_origin == CascadeOrigin::Author) {
document().for_each_css_style_sheet([&](CSSStyleSheet& sheet) {
callback(sheet);
callback(sheet, {});
});
const_cast<DOM::Document&>(document()).for_each_shadow_root([&](DOM::ShadowRoot& shadow_root) {
shadow_root.for_each_css_style_sheet([&](CSSStyleSheet& sheet) {
callback(sheet, &shadow_root);
});
});
}
}
@ -298,6 +305,9 @@ StyleComputer::RuleCache const& StyleComputer::rule_cache_for_cascade_origin(Cas
Vector<MatchingRule> StyleComputer::collect_matching_rules(DOM::Element const& element, CascadeOrigin cascade_origin, Optional<CSS::Selector::PseudoElement::Type> pseudo_element) const
{
auto const& root_node = element.root();
auto shadow_root = is<DOM::ShadowRoot>(root_node) ? static_cast<DOM::ShadowRoot const*>(&root_node) : nullptr;
auto const& rule_cache = rule_cache_for_cascade_origin(cascade_origin);
Vector<MatchingRule> rules_to_run;
@ -331,6 +341,12 @@ Vector<MatchingRule> StyleComputer::collect_matching_rules(DOM::Element const& e
Vector<MatchingRule> matching_rules;
matching_rules.ensure_capacity(rules_to_run.size());
for (auto const& rule_to_run : rules_to_run) {
// FIXME: This needs to be revised when adding support for the :host and ::shadow selectors, which transition shadow tree boundaries
auto rule_root = rule_to_run.shadow_root;
auto from_user_agent_or_user_stylesheet = rule_to_run.cascade_origin == CascadeOrigin::UserAgent || rule_to_run.cascade_origin == CascadeOrigin::User;
if (rule_root != shadow_root && !from_user_agent_or_user_stylesheet)
continue;
auto const& selector = rule_to_run.rule->selectors()[rule_to_run.selector_index];
if (SelectorEngine::matches(selector, *rule_to_run.sheet, element, pseudo_element))
matching_rules.append(rule_to_run);
@ -2277,12 +2293,14 @@ NonnullOwnPtr<StyleComputer::RuleCache> StyleComputer::make_rule_cache_for_casca
Vector<MatchingRule> matching_rules;
size_t style_sheet_index = 0;
for_each_stylesheet(cascade_origin, [&](auto& sheet) {
for_each_stylesheet(cascade_origin, [&](auto& sheet, JS::GCPtr<DOM::ShadowRoot> shadow_root) {
size_t rule_index = 0;
sheet.for_each_effective_style_rule([&](auto const& rule) {
size_t selector_index = 0;
for (CSS::Selector const& selector : rule.selectors()) {
MatchingRule matching_rule {
cascade_origin,
shadow_root,
&rule,
sheet,
style_sheet_index,

View file

@ -20,7 +20,18 @@
namespace Web::CSS {
// https://www.w3.org/TR/css-cascade/#origin
enum class CascadeOrigin {
Author,
User,
UserAgent,
Animation,
Transition,
};
struct MatchingRule {
CascadeOrigin cascade_origin;
JS::GCPtr<DOM::ShadowRoot const> shadow_root;
JS::GCPtr<CSSStyleRule const> rule;
JS::GCPtr<CSSStyleSheet const> sheet;
size_t style_sheet_index { 0 };
@ -55,15 +66,6 @@ public:
NonnullRefPtr<StyleProperties> compute_style(DOM::Element&, Optional<CSS::Selector::PseudoElement::Type> = {}) const;
RefPtr<StyleProperties> compute_pseudo_element_style_if_needed(DOM::Element&, Optional<CSS::Selector::PseudoElement::Type>) const;
// https://www.w3.org/TR/css-cascade/#origin
enum class CascadeOrigin {
Author,
User,
UserAgent,
Animation,
Transition,
};
Vector<MatchingRule> collect_matching_rules(DOM::Element const&, CascadeOrigin, Optional<CSS::Selector::PseudoElement::Type>) const;
void invalidate_rule_cache();

View file

@ -105,4 +105,16 @@ WebIDL::ExceptionOr<void> ShadowRoot::set_adopted_style_sheets(JS::Value new_val
return {};
}
void ShadowRoot::for_each_css_style_sheet(Function<void(CSS::CSSStyleSheet&)>&& callback) const
{
for (auto& style_sheet : style_sheets().sheets())
callback(*style_sheet);
if (m_adopted_style_sheets) {
m_adopted_style_sheets->for_each<CSS::CSSStyleSheet>([&](auto& style_sheet) {
callback(style_sheet);
});
}
}
}

View file

@ -42,6 +42,8 @@ public:
JS::NonnullGCPtr<WebIDL::ObservableArray> adopted_style_sheets() const;
WebIDL::ExceptionOr<void> set_adopted_style_sheets(JS::Value);
void for_each_css_style_sheet(Function<void(CSS::CSSStyleSheet&)>&& callback) const;
virtual void finalize() override;
protected: