LibWeb: Use correct scope when removing style sheet inside a shadow tree

Before this change, removing a style element from inside a shadow tree
would cause it to be unregistered with the document-level list of sheets
instead of the shadow-root-level list.

This would eventually lead to a verification failure if someone tried to
update the text contents of that style element, since it was still in
the shadow-root-level list, but now with a null owner element.

Fixes a crash on https://www.swedbank.se/
This commit is contained in:
Andreas Kling 2024-07-23 09:53:32 +02:00 committed by Andreas Kling
parent ebacb921da
commit 3b7534b362
Notes: github-actions[bot] 2024-07-23 09:13:10 +00:00
6 changed files with 45 additions and 5 deletions

View File

@ -0,0 +1,4 @@
hello [object StyleSheetList]
Before remove, sheet count: 1
After remove, sheet count: 0
After setting innerText of removed sheet, we're still alive!

View File

@ -0,0 +1,20 @@
<div id=foo>
<template shadowrootmode="open">
<style>
div { border: 5px solid black; }
</style>
<div>hello</div>
</template>
</div>
<script src="../include.js"></script>
<script>
test(() => {
println(foo.shadowRoot.styleSheets);
println("Before remove, sheet count: " + foo.shadowRoot.styleSheets.length);
let style = foo.shadowRoot.firstElementChild;
style.remove()
println("After remove, sheet count: " + foo.shadowRoot.styleSheets.length);
style.innerText = "div { border: 10px solid red; }";
println("After setting innerText of removed sheet, we're still alive!");
});
</script>

View File

@ -7,11 +7,21 @@
#include <LibWeb/CSS/Parser/Parser.h>
#include <LibWeb/CSS/StyleComputer.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/DOM/ShadowRoot.h>
#include <LibWeb/DOM/StyleElementUtils.h>
#include <LibWeb/Infra/Strings.h>
namespace Web::DOM {
static CSS::StyleSheetList& relevant_style_sheet_list_for_node(DOM::Node& node)
{
auto& root_node = node.root();
if (is<DOM::ShadowRoot>(root_node))
return static_cast<DOM::ShadowRoot&>(root_node).style_sheets();
return node.document().style_sheets();
}
// The user agent must run the "update a style block" algorithm whenever one of the following conditions occur:
// FIXME: The element is popped off the stack of open elements of an HTML parser or XML parser.
//
@ -22,7 +32,7 @@ namespace Web::DOM {
// The element is not on the stack of open elements of an HTML parser or XML parser, and it becomes connected or disconnected.
//
// https://html.spec.whatwg.org/multipage/semantics.html#update-a-style-block
void StyleElementUtils::update_a_style_block(DOM::Element& style_element)
void StyleElementUtils::update_a_style_block(DOM::Element& style_element, JS::GCPtr<DOM::Node> old_parent_if_removed_from)
{
// OPTIMIZATION: Skip parsing CSS if we're in the middle of parsing a HTML fragment.
// The style block will be parsed upon insertion into a proper document.
@ -33,7 +43,13 @@ void StyleElementUtils::update_a_style_block(DOM::Element& style_element)
// 2. If element has an associated CSS style sheet, remove the CSS style sheet in question.
if (m_associated_css_style_sheet) {
style_element.document_or_shadow_root_style_sheets().remove_a_css_style_sheet(*m_associated_css_style_sheet);
// NOTE: If we're here in response to a node being removed from the tree, we need to remove the stylesheet from the style scope
// of the old parent, not the style scope of the node itself, since it's too late to find it that way!
if (old_parent_if_removed_from) {
relevant_style_sheet_list_for_node(*old_parent_if_removed_from).remove_a_css_style_sheet(*m_associated_css_style_sheet);
} else {
style_element.document_or_shadow_root_style_sheets().remove_a_css_style_sheet(*m_associated_css_style_sheet);
}
// FIXME: This should probably be handled by StyleSheet::set_owner_node().
m_associated_css_style_sheet = nullptr;

View File

@ -14,7 +14,7 @@ namespace Web::DOM {
class StyleElementUtils {
public:
void update_a_style_block(DOM::Element& style_element);
void update_a_style_block(DOM::Element& style_element, JS::GCPtr<DOM::Node> old_parent_if_removed_from = nullptr);
CSS::CSSStyleSheet* sheet() { return m_associated_css_style_sheet; }
CSS::CSSStyleSheet const* sheet() const { return m_associated_css_style_sheet; }

View File

@ -46,7 +46,7 @@ void HTMLStyleElement::inserted()
void HTMLStyleElement::removed_from(Node* old_parent)
{
m_style_element_utils.update_a_style_block(*this);
m_style_element_utils.update_a_style_block(*this, old_parent);
Base::removed_from(old_parent);
}

View File

@ -44,7 +44,7 @@ void SVGStyleElement::inserted()
void SVGStyleElement::removed_from(Node* old_parent)
{
m_style_element_utils.update_a_style_block(*this);
m_style_element_utils.update_a_style_block(*this, old_parent);
Base::removed_from(old_parent);
}