LibWeb: Add fast path for animated style properties update

Patch up existing style properties instead of using the regular style
invalidation path, which requires rule matching for each element in the
invalidated subtree.

- !important properties: this change introduces a flag used to skip the
  update of animated properties overridden by !important.
- inherited animated properties: for now, these are invalidated by
  traversing animated element's subtree to propagate the update.
- StyleProperties has a separate array for animated properties that
  allows the removal animated properties after animation has ended,
  without requiring full style invalidation.
This commit is contained in:
Aliaksandr Kalenik 2024-03-16 07:44:48 +01:00 committed by Andreas Kling
parent dd07c7f729
commit a9b8840a82
Notes: sideshowbarker 2024-07-17 07:25:39 +09:00
11 changed files with 194 additions and 32 deletions

View File

@ -15,6 +15,7 @@
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
#include <LibWeb/HTML/Window.h>
#include <LibWeb/HighResolutionTime/Performance.h>
#include <LibWeb/Painting/Paintable.h>
#include <LibWeb/WebIDL/ExceptionOr.h>
#include <LibWeb/WebIDL/Promise.h>
@ -1315,8 +1316,8 @@ JS::NonnullGCPtr<WebIDL::Promise> Animation::current_finished_promise() const
void Animation::invalidate_effect()
{
if (m_effect) {
if (auto target = m_effect->target())
target->invalidate_style();
if (auto target = m_effect->target(); target && target->paintable())
target->paintable()->set_needs_display();
}
}

View File

@ -146,6 +146,8 @@ public:
virtual DOM::Element* target() const { return {}; }
virtual bool is_keyframe_effect() const { return false; }
virtual void update_style_properties() = 0;
protected:
AnimationEffect(JS::Realm&);
virtual ~AnimationEffect() = default;

View File

@ -10,6 +10,8 @@
#include <LibWeb/Animations/KeyframeEffect.h>
#include <LibWeb/CSS/Parser/Parser.h>
#include <LibWeb/CSS/StyleComputer.h>
#include <LibWeb/Layout/Node.h>
#include <LibWeb/Painting/Paintable.h>
#include <LibWeb/WebIDL/ExceptionOr.h>
namespace Web::Animations {
@ -869,4 +871,56 @@ void KeyframeEffect::visit_edges(Cell::Visitor& visitor)
visitor.visit(keyframe);
}
void KeyframeEffect::update_style_properties()
{
if (!target())
return;
if (pseudo_element_type().has_value()) {
// StyleProperties are not saved for pseudo-elements so there is nothing to patch
target()->invalidate_style();
return;
}
auto* style = target()->computed_css_values();
if (!style)
return;
auto style_before_animation_update = style->clone();
auto& document = target()->document();
document.style_computer().collect_animation_into(*this, *style, CSS::StyleComputer::AnimationRefresh::Yes);
// Traversal of the subtree is necessary to update the animated properties inherited from the target element.
target()->for_each_in_subtree_of_type<DOM::Element>([&](auto& element) {
auto* element_style = element.computed_css_values();
if (!element_style || !element.layout_node())
return IterationDecision::Continue;
for (auto i = to_underlying(CSS::first_property_id); i <= to_underlying(CSS::last_property_id); ++i) {
if (element_style->is_property_inherited(static_cast<CSS::PropertyID>(i))) {
auto new_value = CSS::StyleComputer::get_inherit_value(document.realm(), static_cast<CSS::PropertyID>(i), &element);
element_style->set_property(static_cast<CSS::PropertyID>(i), *new_value, nullptr, CSS::StyleProperties::Inherited::Yes);
}
}
element.layout_node()->apply_style(*element_style);
return IterationDecision::Continue;
});
auto invalidation = DOM::Element::compute_required_invalidation(style_before_animation_update, *style);
if (target()->layout_node())
target()->layout_node()->apply_style(*style);
if (invalidation.relayout)
document.set_needs_layout();
if (invalidation.rebuild_layout_tree)
document.invalidate_layout();
if (invalidation.repaint)
document.set_needs_to_resolve_paint_only_properties();
if (invalidation.rebuild_stacking_context_tree)
document.invalidate_stacking_context_tree();
}
}

View File

@ -103,6 +103,8 @@ public:
virtual bool is_keyframe_effect() const override { return true; }
virtual void update_style_properties() override;
private:
KeyframeEffect(JS::Realm&);
virtual ~KeyframeEffect() override = default;

View File

@ -90,7 +90,6 @@ struct Traits<Web::CSS::FontFaceKey> : public DefaultTraits<Web::CSS::FontFaceKe
namespace Web::CSS {
static DOM::Element const* element_to_inherit_style_from(DOM::Element const*, Optional<CSS::Selector::PseudoElement::Type>);
static NonnullRefPtr<StyleValue const> get_inherit_value(JS::Realm& initial_value_context_realm, CSS::PropertyID, DOM::Element const*, Optional<CSS::Selector::PseudoElement::Type>);
StyleComputer::StyleComputer(DOM::Document& document)
: m_document(document)
@ -668,26 +667,27 @@ void StyleComputer::for_each_property_expanding_shorthands(PropertyID property_i
set_longhand_property(property_id, value);
}
void StyleComputer::set_property_expanding_shorthands(StyleProperties& style, CSS::PropertyID property_id, StyleValue const& value, CSS::CSSStyleDeclaration const* declaration, StyleProperties::PropertyValues const& properties_for_revert)
void StyleComputer::set_property_expanding_shorthands(StyleProperties& style, CSS::PropertyID property_id, StyleValue const& value, CSS::CSSStyleDeclaration const* declaration, StyleProperties::PropertyValues const& properties_for_revert, StyleProperties::Important important)
{
for_each_property_expanding_shorthands(property_id, value, [&](PropertyID shorthand_id, StyleValue const& shorthand_value) {
if (shorthand_value.is_revert()) {
auto& property_in_previous_cascade_origin = properties_for_revert[to_underlying(shorthand_id)];
if (property_in_previous_cascade_origin.has_value())
style.set_property(shorthand_id, property_in_previous_cascade_origin->style, property_in_previous_cascade_origin->declaration);
style.set_property(shorthand_id, property_in_previous_cascade_origin->style, property_in_previous_cascade_origin->declaration, StyleProperties::Inherited::No, important);
} else {
style.set_property(shorthand_id, shorthand_value, declaration);
style.set_property(shorthand_id, shorthand_value, declaration, StyleProperties::Inherited::No, important);
}
});
}
void StyleComputer::set_all_properties(DOM::Element& element, Optional<CSS::Selector::PseudoElement::Type> pseudo_element, StyleProperties& style, StyleValue const& value, DOM::Document& document, CSS::CSSStyleDeclaration const* declaration, StyleProperties::PropertyValues const& properties_for_revert) const
void StyleComputer::set_all_properties(DOM::Element& element, Optional<CSS::Selector::PseudoElement::Type> pseudo_element, StyleProperties& style, StyleValue const& value, DOM::Document& document, CSS::CSSStyleDeclaration const* declaration, StyleProperties::PropertyValues const& properties_for_revert, StyleProperties::Important important) const
{
for (auto i = to_underlying(CSS::first_longhand_property_id); i <= to_underlying(CSS::last_longhand_property_id); ++i) {
auto property_id = (CSS::PropertyID)i;
if (value.is_revert()) {
style.m_property_values[to_underlying(property_id)] = properties_for_revert[to_underlying(property_id)];
style.m_property_values[to_underlying(property_id)]->important = important;
continue;
}
@ -696,6 +696,7 @@ void StyleComputer::set_all_properties(DOM::Element& element, Optional<CSS::Sele
style.m_property_values[to_underlying(property_id)] = { { get_inherit_value(document.realm(), property_id, &element, pseudo_element), nullptr } };
else
style.m_property_values[to_underlying(property_id)] = { { property_initial_value(document.realm(), property_id), nullptr } };
style.m_property_values[to_underlying(property_id)]->important = important;
continue;
}
@ -705,7 +706,9 @@ void StyleComputer::set_all_properties(DOM::Element& element, Optional<CSS::Sele
if (!property_value->is_unresolved())
set_property_expanding_shorthands(style, property_id, property_value, declaration, properties_for_revert);
set_property_expanding_shorthands(style, property_id, value, declaration, properties_for_revert);
style.m_property_values[to_underlying(property_id)]->important = important;
set_property_expanding_shorthands(style, property_id, value, declaration, properties_for_revert, important);
}
}
@ -719,7 +722,7 @@ void StyleComputer::cascade_declarations(StyleProperties& style, DOM::Element& e
continue;
if (property.property_id == CSS::PropertyID::All) {
set_all_properties(element, pseudo_element, style, property.value, m_document, &match.rule->declaration(), properties_for_revert);
set_all_properties(element, pseudo_element, style, property.value, m_document, &match.rule->declaration(), properties_for_revert, important == Important::Yes ? StyleProperties::Important::Yes : StyleProperties::Important::No);
continue;
}
@ -727,7 +730,7 @@ void StyleComputer::cascade_declarations(StyleProperties& style, DOM::Element& e
if (property.value->is_unresolved())
property_value = Parser::Parser::resolve_unresolved_style_value({}, Parser::ParsingContext { document() }, element, pseudo_element, property.property_id, property.value->as_unresolved());
if (!property_value->is_unresolved())
set_property_expanding_shorthands(style, property.property_id, property_value, &match.rule->declaration(), properties_for_revert);
set_property_expanding_shorthands(style, property.property_id, property_value, &match.rule->declaration(), properties_for_revert, important == Important::Yes ? StyleProperties::Important::Yes : StyleProperties::Important::No);
}
}
@ -738,7 +741,7 @@ void StyleComputer::cascade_declarations(StyleProperties& style, DOM::Element& e
continue;
if (property.property_id == CSS::PropertyID::All) {
set_all_properties(element, pseudo_element, style, property.value, m_document, inline_style, properties_for_revert);
set_all_properties(element, pseudo_element, style, property.value, m_document, inline_style, properties_for_revert, important == Important::Yes ? StyleProperties::Important::Yes : StyleProperties::Important::No);
continue;
}
@ -746,7 +749,7 @@ void StyleComputer::cascade_declarations(StyleProperties& style, DOM::Element& e
if (property.value->is_unresolved())
property_value = Parser::Parser::resolve_unresolved_style_value({}, Parser::ParsingContext { document() }, element, pseudo_element, property.property_id, property.value->as_unresolved());
if (!property_value->is_unresolved())
set_property_expanding_shorthands(style, property.property_id, property_value, inline_style, properties_for_revert);
set_property_expanding_shorthands(style, property.property_id, property_value, inline_style, properties_for_revert, important == Important::Yes ? StyleProperties::Important::Yes : StyleProperties::Important::No);
}
}
}
@ -1276,7 +1279,7 @@ static ValueComparingRefPtr<StyleValue const> interpolate_property(DOM::Element&
}
}
void StyleComputer::collect_animation_into(JS::NonnullGCPtr<Animations::KeyframeEffect> effect, StyleProperties& style_properties) const
void StyleComputer::collect_animation_into(JS::NonnullGCPtr<Animations::KeyframeEffect> effect, StyleProperties& style_properties, AnimationRefresh refresh) const
{
auto animation = effect->associated_animation();
if (!animation)
@ -1329,6 +1332,8 @@ void StyleComputer::collect_animation_into(JS::NonnullGCPtr<Animations::Keyframe
auto resolve_property = [&](auto& property) {
return property.visit(
[&](Animations::KeyframeEffect::KeyFrameSet::UseInitial) -> RefPtr<StyleValue const> {
if (refresh == AnimationRefresh::Yes)
return {};
return style_properties.maybe_null_property(it.key);
},
[&](RefPtr<StyleValue const> value) { return value; });
@ -1339,7 +1344,7 @@ void StyleComputer::collect_animation_into(JS::NonnullGCPtr<Animations::Keyframe
auto const& end_property = keyframe_end_values.resolved_properties.get(it.key);
if (!end_property.has_value()) {
if (resolved_start_property) {
style_properties.set_property(it.key, *resolved_start_property);
style_properties.set_animated_property(it.key, *resolved_start_property);
dbgln_if(LIBWEB_CSS_ANIMATION_DEBUG, "No end property for property {}, using {}", string_from_property_id(it.key), resolved_start_property->to_string());
}
continue;
@ -1356,13 +1361,17 @@ void StyleComputer::collect_animation_into(JS::NonnullGCPtr<Animations::Keyframe
auto start = resolved_start_property.release_nonnull();
auto end = resolved_end_property.release_nonnull();
if (style_properties.is_property_important(it.key)) {
continue;
}
if (auto next_value = interpolate_property(*effect->target(), it.key, *start, *end, progress_in_keyframe)) {
dbgln_if(LIBWEB_CSS_ANIMATION_DEBUG, "Interpolated value for property {} at {}: {} -> {} = {}", string_from_property_id(it.key), progress_in_keyframe, start->to_string(), end->to_string(), next_value->to_string());
style_properties.set_property(it.key, *next_value);
style_properties.set_animated_property(it.key, *next_value);
} else {
// If interpolate_property() fails, the element should not be rendered
dbgln_if(LIBWEB_CSS_ANIMATION_DEBUG, "Interpolated value for property {} at {}: {} -> {} is invalid", string_from_property_id(it.key), progress_in_keyframe, start->to_string(), end->to_string());
style_properties.set_property(PropertyID::Visibility, IdentifierStyleValue::create(ValueID::Hidden));
style_properties.set_animated_property(PropertyID::Visibility, IdentifierStyleValue::create(ValueID::Hidden));
}
}
}
@ -1570,7 +1579,7 @@ DOM::Element const* element_to_inherit_style_from(DOM::Element const* element, O
return parent_element;
}
NonnullRefPtr<StyleValue const> get_inherit_value(JS::Realm& initial_value_context_realm, CSS::PropertyID property_id, DOM::Element const* element, Optional<CSS::Selector::PseudoElement::Type> pseudo_element)
NonnullRefPtr<StyleValue const> StyleComputer::get_inherit_value(JS::Realm& initial_value_context_realm, CSS::PropertyID property_id, DOM::Element const* element, Optional<CSS::Selector::PseudoElement::Type> pseudo_element)
{
auto* parent_element = element_to_inherit_style_from(element, pseudo_element);
@ -1586,7 +1595,7 @@ void StyleComputer::compute_defaulted_property_value(StyleProperties& style, DOM
auto& value_slot = style.m_property_values[to_underlying(property_id)];
if (!value_slot.has_value()) {
if (is_inherited_property(property_id))
style.m_property_values[to_underlying(property_id)] = { { get_inherit_value(document().realm(), property_id, element, pseudo_element), nullptr } };
style.m_property_values[to_underlying(property_id)] = { { get_inherit_value(document().realm(), property_id, element, pseudo_element), nullptr, StyleProperties::Important::No, StyleProperties::Inherited::Yes } };
else
style.m_property_values[to_underlying(property_id)] = { { property_initial_value(document().realm(), property_id), nullptr } };
return;
@ -1599,6 +1608,7 @@ void StyleComputer::compute_defaulted_property_value(StyleProperties& style, DOM
if (value_slot->style->is_inherit()) {
value_slot->style = get_inherit_value(document().realm(), property_id, element, pseudo_element);
value_slot->inherited = StyleProperties::Inherited::Yes;
return;
}
@ -1608,6 +1618,7 @@ void StyleComputer::compute_defaulted_property_value(StyleProperties& style, DOM
if (is_inherited_property(property_id)) {
// then if it is an inherited property, this is treated as inherit,
value_slot->style = get_inherit_value(document().realm(), property_id, element, pseudo_element);
value_slot->inherited = StyleProperties::Inherited::Yes;
} else {
// and if it is not, this is treated as initial.
value_slot->style = property_initial_value(document().realm(), property_id);

View File

@ -55,7 +55,8 @@ struct FontFaceKey {
class StyleComputer {
public:
static void for_each_property_expanding_shorthands(PropertyID, StyleValue const&, Function<void(PropertyID, StyleValue const&)> const& set_longhand_property);
static void set_property_expanding_shorthands(StyleProperties&, PropertyID, StyleValue const&, CSS::CSSStyleDeclaration const*, StyleProperties::PropertyValues const& properties_for_revert);
static void set_property_expanding_shorthands(StyleProperties&, PropertyID, StyleValue const&, CSS::CSSStyleDeclaration const*, StyleProperties::PropertyValues const& properties_for_revert, StyleProperties::Important = StyleProperties::Important::No);
static NonnullRefPtr<StyleValue const> get_inherit_value(JS::Realm& initial_value_context_realm, CSS::PropertyID, DOM::Element const*, Optional<CSS::Selector::PseudoElement::Type> = {});
explicit StyleComputer(DOM::Document&);
~StyleComputer();
@ -82,6 +83,12 @@ public:
void set_viewport_rect(Badge<DOM::Document>, CSSPixelRect const& viewport_rect) { m_viewport_rect = viewport_rect; }
enum class AnimationRefresh {
No,
Yes,
};
void collect_animation_into(JS::NonnullGCPtr<Animations::KeyframeEffect> animation, StyleProperties& style_properties, AnimationRefresh = AnimationRefresh::No) const;
private:
enum class ComputeStyleMode {
Normal,
@ -105,7 +112,7 @@ private:
void compute_defaulted_property_value(StyleProperties&, DOM::Element const*, CSS::PropertyID, Optional<CSS::Selector::PseudoElement::Type>) const;
void set_all_properties(DOM::Element&, Optional<CSS::Selector::PseudoElement::Type>, StyleProperties&, StyleValue const&, DOM::Document&, CSS::CSSStyleDeclaration const*, StyleProperties::PropertyValues const& properties_for_revert) const;
void set_all_properties(DOM::Element&, Optional<CSS::Selector::PseudoElement::Type>, StyleProperties&, StyleValue const&, DOM::Document&, CSS::CSSStyleDeclaration const*, StyleProperties::PropertyValues const& properties_for_revert, StyleProperties::Important = StyleProperties::Important::No) const;
template<typename Callback>
void for_each_stylesheet(CascadeOrigin, Callback) const;
@ -142,8 +149,6 @@ private:
RuleCache const& rule_cache_for_cascade_origin(CascadeOrigin) const;
void collect_animation_into(JS::NonnullGCPtr<Animations::KeyframeEffect> animation, StyleProperties& style_properties) const;
OwnPtr<RuleCache> m_author_rule_cache;
OwnPtr<RuleCache> m_user_rule_cache;
OwnPtr<RuleCache> m_user_agent_rule_cache;

View File

@ -34,13 +34,49 @@
namespace Web::CSS {
void StyleProperties::set_property(CSS::PropertyID id, NonnullRefPtr<StyleValue const> value, CSS::CSSStyleDeclaration const* source_declaration)
NonnullRefPtr<StyleProperties> StyleProperties::clone() const
{
m_property_values[to_underlying(id)] = StyleAndSourceDeclaration { move(value), source_declaration };
auto clone = adopt_ref(*new StyleProperties);
clone->m_property_values = m_property_values;
clone->m_animated_property_values = m_animated_property_values;
clone->m_font_list = m_font_list;
clone->m_line_height = m_line_height;
clone->m_math_depth = m_math_depth;
return clone;
}
bool StyleProperties::is_property_important(CSS::PropertyID property_id) const
{
return m_property_values[to_underlying(property_id)].has_value() && m_property_values[to_underlying(property_id)]->important == Important::Yes;
}
bool StyleProperties::is_property_inherited(CSS::PropertyID property_id) const
{
return m_property_values[to_underlying(property_id)].has_value() && m_property_values[to_underlying(property_id)]->inherited == Inherited::Yes;
}
void StyleProperties::set_property(CSS::PropertyID id, NonnullRefPtr<StyleValue const> value, CSS::CSSStyleDeclaration const* source_declaration, Inherited inherited, Important important)
{
m_property_values[to_underlying(id)] = StyleAndSourceDeclaration { move(value), source_declaration, important, inherited };
}
void StyleProperties::set_animated_property(CSS::PropertyID id, NonnullRefPtr<StyleValue const> value)
{
m_animated_property_values[to_underlying(id)] = move(value);
}
void StyleProperties::reset_animated_properties()
{
for (auto& animated_property : m_animated_property_values)
animated_property.clear();
}
NonnullRefPtr<StyleValue const> StyleProperties::property(CSS::PropertyID property_id) const
{
auto animated_value = m_animated_property_values[to_underlying(property_id)];
if (animated_value.has_value())
return *animated_value;
auto value = m_property_values[to_underlying(property_id)];
// By the time we call this method, all properties have values assigned.
VERIFY(value.has_value());
@ -49,6 +85,10 @@ NonnullRefPtr<StyleValue const> StyleProperties::property(CSS::PropertyID proper
RefPtr<StyleValue const> StyleProperties::maybe_null_property(CSS::PropertyID property_id) const
{
auto animated_value = m_animated_property_values[to_underlying(property_id)];
if (animated_value.has_value())
return *animated_value;
auto value = m_property_values[to_underlying(property_id)];
if (value.has_value())
return value->style;

View File

@ -23,6 +23,8 @@ public:
static NonnullRefPtr<StyleProperties> create() { return adopt_ref(*new StyleProperties); }
NonnullRefPtr<StyleProperties> clone() const;
template<typename Callback>
inline void for_each_property(Callback callback) const
{
@ -32,16 +34,34 @@ public:
}
}
enum class Important {
No,
Yes
};
enum class Inherited {
No,
Yes
};
struct StyleAndSourceDeclaration {
NonnullRefPtr<StyleValue const> style;
CSS::CSSStyleDeclaration const* declaration = nullptr;
Important important { Important::No };
Inherited inherited { Inherited::No };
};
using PropertyValues = Array<Optional<StyleAndSourceDeclaration>, to_underlying(CSS::last_property_id) + 1>;
auto& properties() { return m_property_values; }
auto const& properties() const { return m_property_values; }
void set_property(CSS::PropertyID, NonnullRefPtr<StyleValue const> value, CSS::CSSStyleDeclaration const* source_declaration = nullptr);
void reset_animated_properties();
bool is_property_important(CSS::PropertyID property_id) const;
bool is_property_inherited(CSS::PropertyID property_id) const;
void set_property(CSS::PropertyID, NonnullRefPtr<StyleValue const> value, CSS::CSSStyleDeclaration const* source_declaration = nullptr, Inherited = Inherited::No, Important = Important::No);
void set_animated_property(CSS::PropertyID, NonnullRefPtr<StyleValue const> value);
NonnullRefPtr<StyleValue const> property(CSS::PropertyID) const;
RefPtr<StyleValue const> maybe_null_property(CSS::PropertyID) const;
CSS::CSSStyleDeclaration const* property_source_declaration(CSS::PropertyID) const;
@ -166,6 +186,8 @@ private:
friend class StyleComputer;
PropertyValues m_property_values;
Array<Optional<NonnullRefPtr<StyleValue const>>, to_underlying(CSS::last_property_id) + 1> m_animated_property_values;
Optional<CSS::Overflow> overflow(CSS::PropertyID) const;
Vector<CSS::ShadowData> shadow(CSS::PropertyID, Layout::Node const&) const;

View File

@ -1163,6 +1163,19 @@ void Document::update_style()
{
if (!browsing_context())
return;
for (auto& timeline : m_associated_animation_timelines) {
for (auto& animation : timeline->associated_animations()) {
if (auto effect = animation->effect(); effect && effect->target())
effect->target()->reset_animated_css_properties();
}
for (auto& animation : timeline->associated_animations()) {
if (auto effect = animation->effect())
effect->update_style_properties();
}
}
if (!needs_full_style_update() && !needs_style_update() && !child_needs_style_update())
return;
@ -1917,7 +1930,8 @@ void Document::dispatch_events_for_animation_if_necessary(JS::NonnullGCPtr<Anima
return;
auto& css_animation = verify_cast<CSS::CSSAnimation>(*animation);
css_animation.owning_element()->set_needs_style_update(true);
if (auto target = effect->target(); target && target->paintable())
target->paintable()->set_needs_display();
auto previous_phase = effect->previous_phase();
auto current_phase = effect->phase();

View File

@ -510,7 +510,7 @@ void Element::attribute_changed(FlyString const& name, Optional<String> const& v
}
}
static Element::RequiredInvalidationAfterStyleChange compute_required_invalidation(CSS::StyleProperties const& old_style, CSS::StyleProperties const& new_style)
Element::RequiredInvalidationAfterStyleChange Element::compute_required_invalidation(CSS::StyleProperties const& old_style, CSS::StyleProperties const& new_style)
{
Element::RequiredInvalidationAfterStyleChange invalidation;
@ -519,12 +519,12 @@ static Element::RequiredInvalidationAfterStyleChange compute_required_invalidati
for (auto i = to_underlying(CSS::first_property_id); i <= to_underlying(CSS::last_property_id); ++i) {
auto property_id = static_cast<CSS::PropertyID>(i);
auto const& old_value = old_style.properties()[i];
auto const& new_value = new_style.properties()[i];
if (!old_value.has_value() && !new_value.has_value())
auto old_value = old_style.maybe_null_property(property_id);
auto new_value = new_style.maybe_null_property(property_id);
if (!old_value && !new_value)
continue;
bool const property_value_changed = (!old_value.has_value() || !new_value.has_value()) || *old_value->style != *new_value->style;
bool const property_value_changed = (!old_value || !new_value) || *old_value != *new_value;
if (!property_value_changed)
continue;
@ -544,7 +544,7 @@ static Element::RequiredInvalidationAfterStyleChange compute_required_invalidati
// OPTIMIZATION: Special handling for CSS `visibility`:
if (property_id == CSS::PropertyID::Visibility) {
// We don't need to relayout if the visibility changes from visible to hidden or vice versa. Only collapse requires relayout.
if ((old_value.has_value() && old_value->style->to_identifier() == CSS::ValueID::Collapse) != (new_value.has_value() && new_value->style->to_identifier() == CSS::ValueID::Collapse))
if ((old_value && old_value->to_identifier() == CSS::ValueID::Collapse) != (new_value && new_value->to_identifier() == CSS::ValueID::Collapse))
invalidation.relayout = true;
// Of course, we still have to repaint on any visibility change.
invalidation.repaint = true;
@ -624,6 +624,13 @@ NonnullRefPtr<CSS::StyleProperties> Element::resolved_css_values()
return properties;
}
void Element::reset_animated_css_properties()
{
if (!m_computed_css_values)
return;
m_computed_css_values->reset_animated_properties();
}
DOMTokenList* Element::class_list()
{
if (!m_class_list)

View File

@ -166,6 +166,8 @@ public:
static RequiredInvalidationAfterStyleChange full() { return { true, true, true, true }; }
};
static Element::RequiredInvalidationAfterStyleChange compute_required_invalidation(CSS::StyleProperties const& old_style, CSS::StyleProperties const& new_style);
RequiredInvalidationAfterStyleChange recompute_style();
Optional<CSS::Selector::PseudoElement::Type> use_pseudo_element() const { return m_use_pseudo_element; }
@ -179,6 +181,8 @@ public:
void set_computed_css_values(RefPtr<CSS::StyleProperties>);
NonnullRefPtr<CSS::StyleProperties> resolved_css_values();
void reset_animated_css_properties();
CSS::CSSStyleDeclaration const* inline_style() const;
CSS::CSSStyleDeclaration* style_for_bindings();