Coverage Report

Created: 2026-07-25 07:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibWeb/DOM/ShadowRoot.cpp
Line
Count
Source
1
/*
2
 * Copyright (c) 2020, the SerenityOS developers.
3
 *
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 */
6
7
#include <LibWeb/Bindings/ShadowRootPrototype.h>
8
#include <LibWeb/DOM/AdoptedStyleSheets.h>
9
#include <LibWeb/DOM/Document.h>
10
#include <LibWeb/DOM/Event.h>
11
#include <LibWeb/DOM/ShadowRoot.h>
12
#include <LibWeb/HTML/HTMLTemplateElement.h>
13
#include <LibWeb/HTML/Parser/HTMLParser.h>
14
#include <LibWeb/Layout/BlockContainer.h>
15
16
namespace Web::DOM {
17
18
JS_DEFINE_ALLOCATOR(ShadowRoot);
19
20
ShadowRoot::ShadowRoot(Document& document, Element& host, Bindings::ShadowRootMode mode)
21
0
    : DocumentFragment(document)
22
0
    , m_mode(mode)
23
0
{
24
0
    document.register_shadow_root({}, *this);
25
0
    set_host(&host);
26
0
}
27
28
void ShadowRoot::finalize()
29
0
{
30
0
    Base::finalize();
31
0
    document().unregister_shadow_root({}, *this);
32
0
}
33
34
void ShadowRoot::initialize(JS::Realm& realm)
35
0
{
36
0
    Base::initialize(realm);
37
0
    WEB_SET_PROTOTYPE_FOR_INTERFACE(ShadowRoot);
38
0
}
39
40
// https://dom.spec.whatwg.org/#dom-shadowroot-onslotchange
41
void ShadowRoot::set_onslotchange(WebIDL::CallbackType* event_handler)
42
0
{
43
0
    set_event_handler_attribute(HTML::EventNames::slotchange, event_handler);
44
0
}
45
46
// https://dom.spec.whatwg.org/#dom-shadowroot-onslotchange
47
WebIDL::CallbackType* ShadowRoot::onslotchange()
48
0
{
49
0
    return event_handler_attribute(HTML::EventNames::slotchange);
50
0
}
51
52
// https://dom.spec.whatwg.org/#ref-for-get-the-parent%E2%91%A6
53
EventTarget* ShadowRoot::get_parent(Event const& event)
54
0
{
55
0
    if (!event.composed()) {
56
0
        auto& events_first_invocation_target = verify_cast<Node>(*event.path().first().invocation_target);
57
0
        if (&events_first_invocation_target.root() == this)
58
0
            return nullptr;
59
0
    }
60
61
0
    return host();
62
0
}
63
64
// https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-shadowroot-innerhtml
65
WebIDL::ExceptionOr<String> ShadowRoot::inner_html() const
66
0
{
67
0
    return serialize_fragment(DOMParsing::RequireWellFormed::Yes);
68
0
}
69
70
// https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-shadowroot-innerhtml
71
WebIDL::ExceptionOr<void> ShadowRoot::set_inner_html(StringView value)
72
0
{
73
    // FIXME: 1. Let compliantString be the result of invoking the Get Trusted Type compliant string algorithm with TrustedHTML, this's relevant global object, the given value, "ShadowRoot innerHTML", and "script".
74
75
    // 2. Let context be this's host.
76
0
    auto context = this->host();
77
0
    VERIFY(context);
78
79
    // 3. Let fragment be the result of invoking the fragment parsing algorithm steps with context and compliantString. FIXME: Use compliantString instead of markup.
80
0
    auto fragment = TRY(context->parse_fragment(value));
81
82
    // 4. Replace all with fragment within this.
83
0
    this->replace_all(fragment);
84
85
    // NOTE: We don't invalidate style & layout for <template> elements since they don't affect rendering.
86
0
    if (!is<HTML::HTMLTemplateElement>(*this)) {
87
0
        this->set_needs_style_update(true);
88
89
0
        if (this->is_connected()) {
90
            // NOTE: Since the DOM has changed, we have to rebuild the layout tree.
91
0
            this->document().invalidate_layout_tree();
92
0
        }
93
0
    }
94
95
0
    set_needs_style_update(true);
96
0
    return {};
97
0
}
98
99
// https://html.spec.whatwg.org/#dom-element-gethtml
100
WebIDL::ExceptionOr<String> ShadowRoot::get_html(GetHTMLOptions const& options) const
101
0
{
102
    // ShadowRoot's getHTML(options) method steps are to return the result
103
    // of HTML fragment serialization algorithm with this,
104
    // options["serializableShadowRoots"], and options["shadowRoots"].
105
0
    return HTML::HTMLParser::serialize_html_fragment(
106
0
        *this,
107
0
        options.serializable_shadow_roots ? HTML::HTMLParser::SerializableShadowRoots::Yes : HTML::HTMLParser::SerializableShadowRoots::No,
108
0
        options.shadow_roots);
109
0
}
110
111
// https://html.spec.whatwg.org/#dom-shadowroot-sethtmlunsafe
112
WebIDL::ExceptionOr<void> ShadowRoot::set_html_unsafe(StringView html)
113
0
{
114
    // FIXME: 1. Let compliantHTML be the result of invoking the Get Trusted Type compliant string algorithm with TrustedHTML, this's relevant global object, html, "ShadowRoot setHTMLUnsafe", and "script".
115
116
    // 3. Unsafe set HTML given this, this's shadow host, and compliantHTML. FIXME: Use compliantHTML.
117
0
    TRY(unsafely_set_html(*this->host(), html));
118
119
0
    return {};
120
0
}
121
122
CSS::StyleSheetList& ShadowRoot::style_sheets()
123
0
{
124
0
    if (!m_style_sheets)
125
0
        m_style_sheets = CSS::StyleSheetList::create(*this);
126
0
    return *m_style_sheets;
127
0
}
128
129
CSS::StyleSheetList const& ShadowRoot::style_sheets() const
130
0
{
131
0
    return const_cast<ShadowRoot*>(this)->style_sheets();
132
0
}
133
134
void ShadowRoot::visit_edges(Visitor& visitor)
135
0
{
136
0
    Base::visit_edges(visitor);
137
0
    visitor.visit(m_style_sheets);
138
0
    visitor.visit(m_adopted_style_sheets);
139
0
}
140
141
JS::NonnullGCPtr<WebIDL::ObservableArray> ShadowRoot::adopted_style_sheets() const
142
0
{
143
0
    if (!m_adopted_style_sheets)
144
0
        m_adopted_style_sheets = create_adopted_style_sheets_list(const_cast<Document&>(document()));
145
0
    return *m_adopted_style_sheets;
146
0
}
147
148
WebIDL::ExceptionOr<void> ShadowRoot::set_adopted_style_sheets(JS::Value new_value)
149
0
{
150
0
    if (!m_adopted_style_sheets)
151
0
        m_adopted_style_sheets = create_adopted_style_sheets_list(const_cast<Document&>(document()));
152
153
0
    m_adopted_style_sheets->clear();
154
0
    auto iterator_record = TRY(get_iterator(vm(), new_value, JS::IteratorHint::Sync));
155
0
    while (true) {
156
0
        auto next = TRY(iterator_step_value(vm(), iterator_record));
157
0
        if (!next.has_value())
158
0
            break;
159
0
        TRY(m_adopted_style_sheets->append(*next));
160
0
    }
161
162
0
    return {};
163
0
}
164
165
void ShadowRoot::for_each_css_style_sheet(Function<void(CSS::CSSStyleSheet&)>&& callback) const
166
0
{
167
0
    for (auto& style_sheet : style_sheets().sheets())
168
0
        callback(*style_sheet);
169
170
0
    if (m_adopted_style_sheets) {
171
0
        m_adopted_style_sheets->for_each<CSS::CSSStyleSheet>([&](auto& style_sheet) {
172
0
            callback(style_sheet);
173
0
        });
174
0
    }
175
0
}
176
177
Vector<JS::NonnullGCPtr<Animations::Animation>> ShadowRoot::get_animations()
178
0
{
179
0
    Vector<JS::NonnullGCPtr<Animations::Animation>> relevant_animations;
180
0
    for_each_child_of_type<Element>([&](auto& child) {
181
0
        relevant_animations.extend(child.get_animations({ .subtree = true }));
182
0
        return IterationDecision::Continue;
183
0
    });
184
0
    return relevant_animations;
185
0
}
186
187
}