ladybird/Libraries/LibWeb/DOM/AbstractElement.h
Andreas Kling 92221f0c57
Some checks are pending
CI / macOS, arm64, Sanitizer, Clang (push) Waiting to run
CI / Linux, x86_64, Fuzzers, Clang (push) Waiting to run
CI / Linux, x86_64, Sanitizer, GNU (push) Waiting to run
CI / Linux, x86_64, Sanitizer, Clang (push) Waiting to run
Package the js repl as a binary artifact / Linux, x86_64 (push) Waiting to run
Label PRs with merge conflicts / auto-labeler (push) Waiting to run
Push notes / build (push) Waiting to run
Package the js repl as a binary artifact / Linux, arm64 (push) Waiting to run
Package the js repl as a binary artifact / macOS, arm64 (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
LibWeb: Apply all animations and transitions before invalidating style
This fixes an issue where only the last KeyframeEffect applied to an
element would actually have an effect on the computed properties.

It was particularly noticeable when animating a shorthand property like
border-width, since only one of the border edges would have its width
actually animate.

By deferring the invalidation until all animations have been processed,
we also reduce the amount of work that gets done on pages with many
animations/transitions per element. Discord is very fond of this for
example.
2025-07-19 11:08:46 -04:00

69 lines
2.3 KiB
C++

/*
* Copyright (c) 2025, Sam Atkins <sam@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibGC/Cell.h>
#include <LibWeb/CSS/Selector.h>
#include <LibWeb/Forward.h>
namespace Web::DOM {
// Either an Element or a PseudoElement
class AbstractElement {
public:
AbstractElement(GC::Ref<Element>, Optional<CSS::PseudoElement> = {});
Document& document() const;
Element& element() { return m_element; }
Element const& element() const { return m_element; }
Optional<CSS::PseudoElement> pseudo_element() const { return m_pseudo_element; }
GC::Ptr<Layout::NodeWithStyle> layout_node();
GC::Ptr<Layout::NodeWithStyle const> layout_node() const { return const_cast<AbstractElement*>(this)->layout_node(); }
GC::Ptr<Element const> parent_element() const;
Optional<AbstractElement> previous_in_tree_order() { return walk_layout_tree(WalkMethod::Previous); }
Optional<AbstractElement> previous_sibling_in_tree_order() { return walk_layout_tree(WalkMethod::PreviousSibling); }
bool is_before(AbstractElement const&) const;
GC::Ptr<CSS::ComputedProperties const> computed_properties() const;
void set_custom_properties(HashMap<FlyString, CSS::StyleProperty>&& custom_properties);
[[nodiscard]] HashMap<FlyString, CSS::StyleProperty> const& custom_properties() const;
RefPtr<CSS::CSSStyleValue const> get_custom_property(FlyString const& name) const;
bool has_non_empty_counters_set() const;
Optional<CSS::CountersSet const&> counters_set() const;
CSS::CountersSet& ensure_counters_set();
void set_counters_set(OwnPtr<CSS::CountersSet>&&);
void visit(GC::Cell::Visitor& visitor) const;
String debug_description() const;
bool operator==(AbstractElement const&) const = default;
private:
enum class WalkMethod : u8 {
Previous,
PreviousSibling,
};
Optional<AbstractElement> walk_layout_tree(WalkMethod);
GC::Ref<Element> m_element;
Optional<CSS::PseudoElement> m_pseudo_element;
};
}
template<>
struct AK::Traits<Web::DOM::AbstractElement> : public DefaultTraits<Web::DOM::AbstractElement> {
static unsigned hash(Web::DOM::AbstractElement const& key)
{
return pair_int_hash(ptr_hash(&key.element()), key.pseudo_element().has_value() ? to_underlying(key.pseudo_element().value()) : -1);
}
};