Files
ladybird/Tests/LibWeb/Text/input/css/getComputedStyle-targeted-style-update.html
Andreas Kling fb11732526 LibWeb: Fix style inheritance for slotted elements
Two issues prevented slotted elements from correctly inheriting
styles from their assigned slot:

1. Element::element_to_inherit_style_from() was skipping the slot
   element and returning the shadow host instead. This meant slotted
   elements inherited from the host, completely ignoring any styles
   on the slot itself.

2. When a slot element's style changed during the style tree walk,
   its assigned (slotted) nodes were never marked for recomputation.
   The tree walk follows the DOM tree, but slotted elements are DOM
   children of the shadow host, not the slot, so they were missed.

Fix (1) by returning the slot directly as the inheritance parent.
Fix (2) by marking assigned nodes dirty in update_style_recursively
when a slot's style changes.
2026-02-13 10:22:30 +01:00

91 lines
3.6 KiB
HTML

<!DOCTYPE html>
<link rel="author" href="mailto:andreas@ladybird.org">
<script src="../include.js"></script>
<style>
.red { color: red; }
.green { color: green; }
.blue { color: blue; }
</style>
<div id="parent">
<div id="target">hello</div>
<div id="sibling">world</div>
</div>
<div id="slot-host">
<template shadowrootmode="open">
<slot id="the-slot"></slot>
</template>
<span id="slotted-child">slotted</span>
</div>
<div id="shadow-host-2">
<template shadowrootmode="open">
<style>
#inside { color: blue; }
</style>
<div id="inside">inside shadow</div>
</template>
</div>
<div id="inherit-parent" class="red">
<div id="inherit-child">inherits</div>
</div>
<script>
test(() => {
const target = document.getElementById("target");
const sibling = document.getElementById("sibling");
const parent = document.getElementById("parent");
const slottedChild = document.getElementById("slotted-child");
const slotHost = document.getElementById("slot-host");
const inheritParent = document.getElementById("inherit-parent");
const inheritChild = document.getElementById("inherit-child");
// Force initial style computation.
getComputedStyle(target).color;
getComputedStyle(sibling).color;
getComputedStyle(slottedChild).color;
getComputedStyle(inheritChild).color;
// 1. Reading computed style without changes should return correct values.
println(`1: ${getComputedStyle(target).color}`);
// 2. After changing the element's own style, getComputedStyle must reflect it.
target.classList.add("red");
println(`2: ${getComputedStyle(target).color}`);
// 3. After changing an ancestor's style, inherited properties must update.
inheritParent.classList.remove("red");
inheritParent.classList.add("green");
println(`3: ${getComputedStyle(inheritChild).color}`);
// 4. Changing a sibling should not affect our element's computed style.
target.classList.remove("red");
target.classList.add("blue");
getComputedStyle(target).color; // Force update after our change.
sibling.classList.add("red"); // Now dirty the sibling.
// target's color should still be correct even though sibling is dirty.
println(`4: ${getComputedStyle(target).color}`);
// 5. Slotted element: changing the slot's style must affect the slotted child,
// since the slot is the flat tree parent and inheritance follows the flat tree.
const shadowRoot = slotHost.shadowRoot;
const theSlot = shadowRoot.getElementById("the-slot");
theSlot.style.color = "red";
println(`5: ${getComputedStyle(slottedChild).color}`);
theSlot.style.color = "green";
println(`5b: ${getComputedStyle(slottedChild).color}`);
// 6. Element inside shadow DOM should get correct computed style.
const shadowHost2 = document.getElementById("shadow-host-2");
const inside = shadowHost2.shadowRoot.getElementById("inside");
println(`6: ${getComputedStyle(inside).color}`);
// 7. Reading computed style of a pseudo-element.
const styleEl = document.createElement("style");
styleEl.textContent = "#target::before { content: 'X'; color: green; }";
document.head.appendChild(styleEl);
println(`7: ${getComputedStyle(target, "::before").color}`);
// 8. Multiple reads without changes return consistent values.
println(`8a: ${getComputedStyle(target).color}`);
println(`8b: ${getComputedStyle(target).color}`);
});
</script>