mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-04-25 17:25:08 +02:00
LibWeb: Fix crash removing link stylesheet nested in a shadow tree
HTMLLinkElement::removed_from() used `old_root` to find the StyleSheetList to remove the link's stylesheet from. That's wrong when the link element lives inside a shadow tree that is itself nested within a larger removed subtree: Node::remove() hands every shadow-including descendant the outer subtree's root as `old_root`, not the descendant's own containing root. So we'd look in the document's list while the sheet was actually in the shadow root's list, failing the did_remove VERIFY in StyleSheetList::remove_sheet. Fix by using the sheet's own owning-root tracking. A link-owned sheet always has exactly one owning document or shadow root (only constructed stylesheets can be adopted, and link sheets are never constructed), so we can just read that entry. Also make owning_documents_or_shadow_roots() return by const reference instead of copying the HashTable on every call, which benefits existing iterating callers too. Fixes a crash on https://nytimes.com/.
This commit is contained in:
committed by
Andreas Kling
parent
bd4ef4b95a
commit
3cf24872c4
Notes:
github-actions[bot]
2026-04-23 20:38:05 +00:00
Author: https://github.com/awesomekling Commit: https://github.com/LadybirdBrowser/ladybird/commit/3cf24872c47 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/9061
@@ -90,7 +90,7 @@ public:
|
||||
void for_each_effective_keyframes_at_rule(Function<void(CSSKeyframesRule const&)> const& callback) const;
|
||||
void for_each_effective_counter_style_at_rule(Function<void(CSSCounterStyleRule const&)> const& callback) const;
|
||||
|
||||
HashTable<GC::Ptr<DOM::Node>> owning_documents_or_shadow_roots() const { return m_owning_documents_or_shadow_roots; }
|
||||
HashTable<GC::Ptr<DOM::Node>> const& owning_documents_or_shadow_roots() const { return m_owning_documents_or_shadow_roots; }
|
||||
void add_owning_document_or_shadow_root(DOM::Node& document_or_shadow_root);
|
||||
void remove_owning_document_or_shadow_root(DOM::Node& document_or_shadow_root);
|
||||
void invalidate_owners(DOM::StyleInvalidationReason, ShadowRootStylesheetEffects const* previous_sheet_effects = nullptr);
|
||||
|
||||
@@ -85,11 +85,18 @@ void HTMLLinkElement::removed_from(IsSubtreeRoot is_subtree_root, Node* old_ance
|
||||
Base::removed_from(is_subtree_root, old_ancestor, old_root);
|
||||
|
||||
if (m_loaded_style_sheet) {
|
||||
auto& style_sheet_list = [&old_root] -> CSS::StyleSheetList& {
|
||||
if (auto* shadow_root = as_if<DOM::ShadowRoot>(old_root); shadow_root)
|
||||
// NB: We can't use `old_root` here. When this link element is nested
|
||||
// inside a shadow tree within a larger removed subtree, `old_root`
|
||||
// is the outer subtree's root, not the shadow root that actually
|
||||
// contains our stylesheet. Use the sheet's own tracked owning
|
||||
// root, which is the one whose StyleSheetList it belongs to.
|
||||
auto const& owning_roots = m_loaded_style_sheet->owning_documents_or_shadow_roots();
|
||||
VERIFY(owning_roots.size() == 1);
|
||||
auto& owning_root = **owning_roots.begin();
|
||||
auto& style_sheet_list = [&owning_root] -> CSS::StyleSheetList& {
|
||||
if (auto* shadow_root = as_if<DOM::ShadowRoot>(owning_root))
|
||||
return shadow_root->style_sheets();
|
||||
|
||||
return as<DOM::Document>(old_root).style_sheets();
|
||||
return as<DOM::Document>(owning_root).style_sheets();
|
||||
}();
|
||||
|
||||
style_sheet_list.remove_a_css_style_sheet(*m_loaded_style_sheet);
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<div id="container">
|
||||
<div id="host"></div>
|
||||
</div>
|
||||
<script>
|
||||
const host = document.getElementById('host');
|
||||
const shadow = host.attachShadow({ mode: 'open' });
|
||||
const link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.href = 'data:text/css,body{color:red}';
|
||||
link.onload = () => {
|
||||
document.getElementById('container').remove();
|
||||
};
|
||||
shadow.appendChild(link);
|
||||
</script>
|
||||
Reference in New Issue
Block a user