mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 15:40:19 +00:00
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:
parent
91ec1d6f95
commit
33294aea86
Notes:
sideshowbarker
2024-07-17 22:09:47 +09:00
Author: https://github.com/kalenikaliaksandr Commit: https://github.com/SerenityOS/serenity/commit/33294aea86 Pull-request: https://github.com/SerenityOS/serenity/pull/23533 Issue: https://github.com/SerenityOS/serenity/issues/23410 Reviewed-by: https://github.com/awesomekling
7 changed files with 99 additions and 27 deletions
|
@ -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]
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
border of #test = (2px solid rgb(173, 255, 47))
|
|
@ -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>
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in a new issue