LibWeb: Append style sheet to ShadowRoot's list if link el is in one
Some checks are pending
CI / Lagom (arm64, Sanitizer_CI, false, macos-15, macOS, Clang) (push) Waiting to run
CI / Lagom (x86_64, Fuzzers_CI, false, ubuntu-24.04, Linux, Clang) (push) Waiting to run
CI / Lagom (x86_64, Sanitizer_CI, false, ubuntu-24.04, Linux, GNU) (push) Waiting to run
CI / Lagom (x86_64, Sanitizer_CI, true, ubuntu-24.04, Linux, Clang) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (arm64, macos-15, macOS, macOS-universal2) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (x86_64, ubuntu-24.04, Linux, Linux-x86_64) (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Label PRs with merge conflicts / auto-labeler (push) Waiting to run
Push notes / build (push) Waiting to run

We were handling removing the style sheet from the shadow root, but not
appending to it. Fixing this also revealed a bug that a removed link
element would always try to remove from the document's list, as the
root is no longer the shadow root it's in. The fix is to use the passed
in old root to remove the style sheet from.

Fixes the cookie banner on https://nos.nl/
This commit is contained in:
Luke Wilde 2025-04-02 16:53:26 +01:00 committed by Alexander Kalenik
commit cd72e788e9
Notes: github-actions[bot] 2025-04-08 21:22:01 +00:00
3 changed files with 62 additions and 2 deletions

View file

@ -56,7 +56,14 @@ void HTMLLinkElement::removed_from(Node* old_parent, Node& old_root)
{ {
Base::removed_from(old_parent, old_root); Base::removed_from(old_parent, old_root);
if (m_loaded_style_sheet) { if (m_loaded_style_sheet) {
document_or_shadow_root_style_sheets().remove_a_css_style_sheet(*m_loaded_style_sheet); auto& style_sheet_list = [&old_root] -> CSS::StyleSheetList& {
if (auto* shadow_root = as_if<DOM::ShadowRoot>(old_root); shadow_root)
return shadow_root->style_sheets();
return as<DOM::Document>(old_root).style_sheets();
}();
style_sheet_list.remove_a_css_style_sheet(*m_loaded_style_sheet);
m_loaded_style_sheet = nullptr; m_loaded_style_sheet = nullptr;
} }
} }
@ -487,7 +494,7 @@ void HTMLLinkElement::process_stylesheet_resource(bool success, Fetch::Infrastru
if (!response.url_list().is_empty()) if (!response.url_list().is_empty())
location = response.url_list().first().to_string(); location = response.url_list().first().to_string();
document().style_sheets().create_a_css_style_sheet( document_or_shadow_root_style_sheets().create_a_css_style_sheet(
"text/css"_string, "text/css"_string,
this, this,
attribute(HTML::AttributeNames::media).value_or({}), attribute(HTML::AttributeNames::media).value_or({}),

View file

@ -0,0 +1,13 @@
== before appending
is linkEl.sheet null? true
- document stylesheets (length = 0)
- shadow root stylesheets (length = 0)
== link loaded
is linkEl.sheet null? false
- document stylesheets (length = 0)
- shadow root stylesheets (length = 1)
-- shadow root linkEl.sheet === sheet: true
== link removed
is linkEl.sheet null? true
- document stylesheets (length = 0)
- shadow root stylesheets (length = 0)

View file

@ -0,0 +1,40 @@
<!DOCTYPE html>
<div id="shadowhost"></div>
<script src="../include.js"></script>
<script>
asyncTest((done) => {
const shadowHost = document.getElementById("shadowhost");
const shadowRoot = shadowHost.attachShadow({ mode: "closed" });
const linkEl = document.createElement("link");
linkEl.href = "../valid.css";
linkEl.rel = "stylesheet";
const printSheets = () => {
println(`is linkEl.sheet null? ${linkEl.sheet === null}`)
println(`- document stylesheets (length = ${document.styleSheets.length})`);
for (const sheet of document.styleSheets) {
println(`-- document stylesheet linkEl.sheet === sheet: ${linkEl.sheet === sheet}`);
}
println(`- shadow root stylesheets (length = ${shadowRoot.styleSheets.length})`);
for (const sheet of shadowRoot.styleSheets) {
println(`-- shadow root linkEl.sheet === sheet: ${linkEl.sheet === sheet}`);
}
};
println("== before appending");
printSheets();
linkEl.addEventListener("load", () => {
println("== link loaded");
printSheets();
linkEl.remove();
println("== link removed");
printSheets();
done();
}, { once: true });
shadowRoot.appendChild(linkEl);
});
</script>