mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-12-26 04:35:41 +03:00
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:
parent
dd07c7f729
commit
a9b8840a82
Notes:
sideshowbarker
2024-07-17 07:25:39 +09:00
Author: https://github.com/kalenikaliaksandr Commit: https://github.com/SerenityOS/serenity/commit/a9b8840a82 Pull-request: https://github.com/SerenityOS/serenity/pull/23591 Reviewed-by: https://github.com/AtkinsSJ Reviewed-by: https://github.com/awesomekling
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user