LibWeb: Refresh stale inherited custom-property data on ancestor change
When an ancestor's custom property changes, intermediate elements that don't themselves consume `var()`/`inherit()` were skipped during style recomputation, leaving their `m_custom_property_data` pointing at the ancestor's stale `CustomPropertyData`. Deeper descendants that did consume `var()` then resolved against the out-of-date chain. This change adds `Element::refresh_inherited_custom_property_data()` to re-link an element's inherited chain to its parent's current data without re-cascading. This is called during `update_style_recursively()` for elements that don't need a full recompute.
This commit is contained in:
parent
82b016ed19
commit
da210eaa9d
7 changed files with 83 additions and 7 deletions
|
|
@ -1845,8 +1845,11 @@ bool Document::layout_is_up_to_date() const
|
|||
|| (recompute_elements_depending_on_custom_properties && (element.style_uses_var_css_function() || element.style_uses_inherit_css_function()))
|
||||
|| needs_style_update_due_to_if_media) {
|
||||
node_invalidation = element.recompute_style(did_change_custom_properties);
|
||||
} else if (needs_inherited_style_update) {
|
||||
node_invalidation = element.recompute_inherited_style();
|
||||
} else {
|
||||
if (needs_inherited_style_update)
|
||||
node_invalidation = element.recompute_inherited_style();
|
||||
if (recompute_elements_depending_on_custom_properties && element.refresh_inherited_custom_property_data())
|
||||
did_change_custom_properties = true;
|
||||
}
|
||||
is_display_none = static_cast<Element&>(node).computed_properties()->display().is_none();
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#include <LibWeb/CSS/CSSStyleProperties.h>
|
||||
#include <LibWeb/CSS/ComputedProperties.h>
|
||||
#include <LibWeb/CSS/CountersSet.h>
|
||||
#include <LibWeb/CSS/CustomPropertyData.h>
|
||||
#include <LibWeb/CSS/Invalidation/AttributeInvalidator.h>
|
||||
#include <LibWeb/CSS/Invalidation/CustomElementInvalidator.h>
|
||||
#include <LibWeb/CSS/Invalidation/ElementStateInvalidator.h>
|
||||
|
|
@ -3561,6 +3562,20 @@ RefPtr<CSS::CustomPropertyData const> Element::custom_property_data(Optional<CSS
|
|||
return ensure_pseudo_element(pseudo_element.value()).custom_property_data();
|
||||
}
|
||||
|
||||
bool Element::refresh_inherited_custom_property_data()
|
||||
{
|
||||
RefPtr<CSS::CustomPropertyData const> parent_data;
|
||||
if (auto inherit_from = element_to_inherit_style_from({})) {
|
||||
if (auto data = inherit_from->custom_property_data({}))
|
||||
parent_data = data->inheritable(document());
|
||||
}
|
||||
|
||||
if (m_custom_property_data == parent_data)
|
||||
return false;
|
||||
m_custom_property_data = move(parent_data);
|
||||
return true;
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/cssom-view/#dom-element-scroll
|
||||
GC::Ref<WebIDL::Promise> Element::scroll(double x, double y)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -268,6 +268,8 @@ public:
|
|||
void set_custom_property_data(Optional<CSS::PseudoElement>, RefPtr<CSS::CustomPropertyData const>);
|
||||
[[nodiscard]] RefPtr<CSS::CustomPropertyData const> custom_property_data(Optional<CSS::PseudoElement>) const;
|
||||
|
||||
[[nodiscard]] bool refresh_inherited_custom_property_data();
|
||||
|
||||
bool style_uses_attr_css_function() const { return m_style_uses_attr_css_function; }
|
||||
void set_style_uses_attr_css_function() { m_style_uses_attr_css_function = true; }
|
||||
bool style_uses_var_css_function() const { return m_style_uses_var_css_function; }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
case 1 initial display: inline
|
||||
case 1 final display: inline-flex
|
||||
case 1 final size: 24x24
|
||||
case 2 width: 20px
|
||||
case 2 color: rgb(0, 128, 0)
|
||||
|
|
@ -32,8 +32,8 @@ pseudo::before --pseudo-var: from-pseudo
|
|||
pseudo::before --inherited-var: from-element
|
||||
=== Dynamic update ===
|
||||
before: (empty)
|
||||
after:
|
||||
changed:
|
||||
after: hello
|
||||
changed: world
|
||||
removed: (empty)
|
||||
=== Nonexistent ===
|
||||
nonexistent: (empty)
|
||||
|
|
|
|||
|
|
@ -2,8 +2,7 @@ Harness status: OK
|
|||
|
||||
Found 3 tests
|
||||
|
||||
2 Pass
|
||||
1 Fail
|
||||
3 Pass
|
||||
Pass inherit() invalidates when values changes on parent element, inherited
|
||||
Fail inherit() invalidates when values changes on parent element, inherited, deeper
|
||||
Pass inherit() invalidates when values changes on parent element, inherited, deeper
|
||||
Pass inherit() invalidates when values changes on parent element, non-inherited
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
<!DOCTYPE html>
|
||||
<script src="../include.js"></script>
|
||||
<script>
|
||||
test(() => {
|
||||
class MyIcon extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
const shadow = this.attachShadow({ mode: "open" });
|
||||
const style = document.createElement("style");
|
||||
style.textContent = `
|
||||
:host {
|
||||
display: var(--my-display);
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
`;
|
||||
shadow.appendChild(style);
|
||||
}
|
||||
}
|
||||
customElements.define("my-icon", MyIcon);
|
||||
|
||||
const icon = document.createElement("my-icon");
|
||||
document.body.appendChild(icon);
|
||||
const initialDisplay = getComputedStyle(icon).display;
|
||||
|
||||
const iconStyleElement = document.createElement("style");
|
||||
iconStyleElement.textContent = `html { --my-display: inline-flex; }`;
|
||||
document.head.appendChild(iconStyleElement);
|
||||
|
||||
const iconRect = icon.getBoundingClientRect();
|
||||
println(`case 1 initial display: ${initialDisplay}`);
|
||||
println(`case 1 final display: ${getComputedStyle(icon).display}`);
|
||||
println(`case 1 final size: ${iconRect.width}x${iconRect.height}`);
|
||||
icon.remove();
|
||||
iconStyleElement.remove();
|
||||
|
||||
|
||||
const combo = document.createElement("div");
|
||||
combo.style.width = "var(--size)";
|
||||
document.body.appendChild(combo);
|
||||
getComputedStyle(combo).width;
|
||||
|
||||
const comboStyleElement = document.createElement("style");
|
||||
comboStyleElement.textContent = `html { --size: 20px; color: green; }`;
|
||||
document.head.appendChild(comboStyleElement);
|
||||
|
||||
println(`case 2 width: ${getComputedStyle(combo).width}`);
|
||||
println(`case 2 color: ${getComputedStyle(combo).color}`);
|
||||
combo.remove();
|
||||
comboStyleElement.remove();
|
||||
});
|
||||
</script>
|
||||
Loading…
Reference in a new issue