Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
85adfde
LibWeb: Establish shared Rust style value handles
awesomekling Jul 22, 2026
bff24e8
LibWeb: Retain transform and list children in Rust
awesomekling Jul 22, 2026
507c85c
LibWeb: Move transform interpolation to Rust
awesomekling Jul 23, 2026
96f21b1
LibWeb: Establish batched Rust animation evaluation
awesomekling Jul 23, 2026
9436da9
LibWeb: Move compound geometry animation values to Rust
awesomekling Jul 23, 2026
861a0c9
LibWeb: Move tagged and function animation values to Rust
awesomekling Jul 23, 2026
a197c45
LibWeb: Retain miscellaneous compound style values in Rust
awesomekling Jul 23, 2026
6a644b7
LibWeb: Retain complex style values in Rust
awesomekling Jul 23, 2026
cd91dbb
LibWeb: Move remaining scalar and transform animation types to Rust
awesomekling Jul 23, 2026
26ba2c8
LibWeb: Evaluate keyframe intervals and transitions in Rust
awesomekling Jul 23, 2026
c7a365e
LibWeb: Move complex animation interpolation to Rust
awesomekling Jul 23, 2026
6126c82
LibWeb: Complete per-element animation batching in Rust
awesomekling Jul 23, 2026
e6950c7
LibWeb: Remove C++ animation value fallbacks
awesomekling Jul 23, 2026
d622a45
LibWeb: Pass cascade and longhand values as Rust handles
awesomekling Jul 23, 2026
f9472d6
LibWeb: Resolve animation property conflicts in Rust
awesomekling Jul 24, 2026
dd7f664
LibWeb: Drive keyframe value preparation from Rust
awesomekling Jul 24, 2026
079c833
LibWeb: Remove obsolete C++ CSS helpers
awesomekling Jul 24, 2026
6e21c11
LibWeb: Return animation and transition results directly from Rust
awesomekling Jul 24, 2026
82436cf
LibWeb: Batch external calculation operations
awesomekling Jul 24, 2026
f971df4
LibWeb: Keep longhand computation in Rust
awesomekling Jul 24, 2026
7292f7e
LibWeb: Move computed style filtering and sizing to Rust
awesomekling Jul 24, 2026
c3c7d16
LibWeb: Move remaining computed style groups to Rust
awesomekling Jul 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Documentation/CSSGeneratedFiles.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,13 +281,14 @@ The generated code provides:
- `bool is_element_backed_pseudo_element(PseudoElement)` returns whether the pseudo-element is element-backed
- `bool is_tree_abiding_pseudo_element(PseudoElement)` returns whether the pseudo-element is tree-abiding
- `bool is_pseudo_element_root(PseudoElement)` returns whether the pseudo-element is a [pseudo-element root](https://drafts.csswg.org/css-view-transitions/#pseudo-element-root)
- `bool pseudo_element_supports_property(PseudoElement, PropertyID)` returns whether the property can be applied to this pseudo-element

### `property-whitelist`

This is an array of strings. Properties can be named directly ("color"), or categories of properties with a leading `#`
("#font-properties"), as the specs often says a group is allowed instead of listing the properties exactly.
Any properties we don't support yet can be prefixed with "FIXME:" and will be ignored.
The property groups and the properties that always apply are defined in
`PseudoElementPropertyGroups.txt` and generated into the Rust cascade tables.

The following categories are supported:

Expand Down
17 changes: 9 additions & 8 deletions Libraries/LibWeb/Animations/Animation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ GC::Ref<Animation> Animation::construct_impl(JS::Realm& realm, GC::Ptr<Animation
}

// https://www.w3.org/TR/web-animations-1/#animation-set-the-associated-effect-of-an-animation
void Animation::set_effect(GC::Ptr<AnimationEffect> new_effect)
void Animation::set_effect(GC::Ptr<AnimationEffect> new_effect, ShouldInvalidate should_invalidate)
{
// Setting this attribute updates the object’s associated effect using the procedure to set the associated effect of
// an animation.
Expand Down Expand Up @@ -106,7 +106,7 @@ void Animation::set_effect(GC::Ptr<AnimationEffect> new_effect)

// 7. Run the procedure to update an animation’s finished state for animation with the did seek flag set to false,
// and the synchronously notify flag set to false.
update_finished_state(DidSeek::No, SynchronouslyNotify::No);
update_finished_state(DidSeek::No, SynchronouslyNotify::No, should_invalidate);
}

GC::Ptr<AnimationTimeline> Animation::timeline_for_bindings() const
Expand Down Expand Up @@ -809,16 +809,16 @@ WebIDL::ExceptionOr<void> Animation::finish()
}

// https://www.w3.org/TR/web-animations-1/#dom-animation-play
WebIDL::ExceptionOr<void> Animation::play()
WebIDL::ExceptionOr<void> Animation::play(ShouldInvalidate should_invalidate)
{
// Begins or resumes playback of the animation by running the procedure to play an animation passing true as the
// value of the auto-rewind flag.
return play_an_animation(AutoRewind::Yes);
return play_an_animation(AutoRewind::Yes, should_invalidate);
}

// https://drafts.csswg.org/web-animations-1/#playing-an-animation-section
// https://drafts.csswg.org/web-animations-2/#play-an-animation
WebIDL::ExceptionOr<void> Animation::play_an_animation(AutoRewind auto_rewind)
WebIDL::ExceptionOr<void> Animation::play_an_animation(AutoRewind auto_rewind, ShouldInvalidate should_invalidate)
{
// 1. Let aborted pause be a boolean flag that is true if animation has a pending pause task, and false otherwise.
auto aborted_pause = m_pending_pause_task == TaskState::Scheduled;
Expand Down Expand Up @@ -921,7 +921,7 @@ WebIDL::ExceptionOr<void> Animation::play_an_animation(AutoRewind auto_rewind)

// 13. Run the procedure to update an animation’s finished state for animation with the did seek flag set to false,
// and the synchronously notify flag set to false.
update_finished_state(DidSeek::No, SynchronouslyNotify::No);
update_finished_state(DidSeek::No, SynchronouslyNotify::No, should_invalidate);

return {};
}
Expand Down Expand Up @@ -1284,7 +1284,7 @@ WebIDL::ExceptionOr<void> Animation::silently_set_current_time(Optional<TimeValu
}

// https://www.w3.org/TR/web-animations-1/#update-an-animations-finished-state
void Animation::update_finished_state(DidSeek did_seek, SynchronouslyNotify synchronously_notify)
void Animation::update_finished_state(DidSeek did_seek, SynchronouslyNotify synchronously_notify, ShouldInvalidate should_invalidate)
{
auto& realm = this->realm();

Expand Down Expand Up @@ -1442,7 +1442,8 @@ void Animation::update_finished_state(DidSeek did_seek, SynchronouslyNotify sync
m_is_finished = false;
}

invalidate_effect();
if (should_invalidate == ShouldInvalidate::Yes)
invalidate_effect();
}

// https://www.w3.org/TR/web-animations-1/#animation-reset-an-animations-pending-tasks
Expand Down
17 changes: 9 additions & 8 deletions Libraries/LibWeb/Animations/Animation.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ class Animation : public DOM::EventTarget {
GC_DECLARE_ALLOCATOR(Animation);

public:
enum class ShouldInvalidate {
Yes,
No,
};

static constexpr bool OVERRIDES_FINALIZE = true;

static GC::Ref<Animation> create(JS::Realm&, GC::Ptr<AnimationEffect>, Optional<GC::Ptr<AnimationTimeline>>);
Expand All @@ -39,7 +44,7 @@ class Animation : public DOM::EventTarget {
void set_id(Utf16FlyString value) { m_id = move(value); }

GC::Ptr<AnimationEffect> effect() const { return m_effect; }
void set_effect(GC::Ptr<AnimationEffect>);
void set_effect(GC::Ptr<AnimationEffect>, ShouldInvalidate = ShouldInvalidate::Yes);

GC::Ptr<AnimationTimeline> timeline() const { return m_timeline; }
void set_timeline(GC::Ptr<AnimationTimeline>);
Expand Down Expand Up @@ -97,14 +102,10 @@ class Animation : public DOM::EventTarget {
Yes,
No,
};
enum class ShouldInvalidate {
Yes,
No,
};
void cancel(ShouldInvalidate = ShouldInvalidate::Yes);
WebIDL::ExceptionOr<void> finish();
WebIDL::ExceptionOr<void> play();
WebIDL::ExceptionOr<void> play_an_animation(AutoRewind);
WebIDL::ExceptionOr<void> play(ShouldInvalidate = ShouldInvalidate::Yes);
WebIDL::ExceptionOr<void> play_an_animation(AutoRewind, ShouldInvalidate = ShouldInvalidate::Yes);
WebIDL::ExceptionOr<void> pause();
WebIDL::ExceptionOr<void> update_playback_rate(double);
WebIDL::ExceptionOr<void> reverse();
Expand Down Expand Up @@ -163,7 +164,7 @@ class Animation : public DOM::EventTarget {

void apply_any_pending_playback_rate();
WebIDL::ExceptionOr<void> silently_set_current_time(Optional<TimeValue>);
void update_finished_state(DidSeek, SynchronouslyNotify);
void update_finished_state(DidSeek, SynchronouslyNotify, ShouldInvalidate = ShouldInvalidate::Yes);
void reset_an_animations_pending_tasks();

bool is_ready() const;
Expand Down
2 changes: 2 additions & 0 deletions Libraries/LibWeb/Animations/AnimationEffect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -859,6 +859,8 @@ AnimationUpdateContext::~AnimationUpdateContext()
continue;
auto& element = it.key;
GC::Ref<DOM::Element> target = element.element();
if (!it.value.effects.is_empty())
target->document().style_computer().collect_animations_into(element, it.value.effects.span(), *style);
auto animated_properties_after_update = style->animated_properties_snapshot();
auto invalidation = compute_required_invalidation_for_animated_properties(it.value.animated_properties_before_update.ptr(), animated_properties_after_update.ptr());

Expand Down
2 changes: 2 additions & 0 deletions Libraries/LibWeb/Animations/AnimationEffect.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <AK/Utf16String.h>
#include <AK/Variant.h>
#include <LibGC/ConservativeHashMap.h>
#include <LibGC/ConservativeVector.h>
#include <LibWeb/Animations/TimeValue.h>
#include <LibWeb/Bindings/AnimationEffect.h>
#include <LibWeb/Bindings/PlatformObject.h>
Expand Down Expand Up @@ -43,6 +44,7 @@ struct AnimationUpdateContext {

RefPtr<CSS::AnimatedProperties const> animated_properties_before_update;
RefPtr<CSS::ComputedProperties> target_style;
GC::ConservativeVector<GC::Ref<KeyframeEffect>> effects;
};

AnimationUpdateContext();
Expand Down
11 changes: 8 additions & 3 deletions Libraries/LibWeb/Animations/KeyframeEffect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -834,9 +834,14 @@ Optional<DOM::AbstractElement> KeyframeEffect::target_abstract_element() const
return {};
}

void KeyframeEffect::set_target(DOM::AbstractElement abstract_element)
void KeyframeEffect::set_target(DOM::AbstractElement abstract_element, InvalidateEffect invalidate)
{
set_target(&abstract_element.element());
if (invalidate == InvalidateEffect::Yes) {
set_target(&abstract_element.element());
} else {
VERIFY(!associated_animation());
m_target_element = &abstract_element.element();
}
m_target_pseudo_selector = abstract_element.pseudo_element().map([](auto it) { return CSS::Selector::PseudoElementSelector { it }; });
}

Expand Down Expand Up @@ -995,7 +1000,7 @@ void KeyframeEffect::update_computed_properties_for_style(AnimationUpdateContext
});

VERIFY(element_data.target_style);
style_computer.collect_animation_into(abstract_element, *this, *element_data.target_style);
element_data.effects.append(*this);
}

Bindings::CompositeOperation css_animation_composition_to_bindings_composite_operation(CSS::AnimationComposition composition)
Expand Down
6 changes: 5 additions & 1 deletion Libraries/LibWeb/Animations/KeyframeEffect.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,11 @@ class KeyframeEffect final : public AnimationEffect {
WebIDL::ExceptionOr<void> set_pseudo_element(Optional<Utf16String>);

Optional<DOM::AbstractElement> target_abstract_element() const;
void set_target(DOM::AbstractElement);
enum class InvalidateEffect {
No,
Yes,
};
void set_target(DOM::AbstractElement, InvalidateEffect = InvalidateEffect::Yes);

Optional<CSS::PseudoElement> pseudo_element_type() const;
void set_pseudo_element(Optional<CSS::Selector::PseudoElementSelector> pseudo_element) { m_target_pseudo_selector = pseudo_element; }
Expand Down
5 changes: 0 additions & 5 deletions Libraries/LibWeb/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,6 @@ set(SOURCES
CSS/CSSUnparsedValue.cpp
CSS/CSSVariableReferenceValue.cpp
CSS/ColorFunctionDescriptor.cpp
CSS/ColorInterpolation.cpp
CSS/CustomPropertyData.cpp
CSS/Descriptor.cpp
CSS/Display.cpp
Expand Down Expand Up @@ -201,7 +200,6 @@ set(SOURCES
CSS/Invalidation/SlotInvalidator.cpp
CSS/Invalidation/StyleInvalidator.cpp
CSS/Invalidation/StructuralMutationInvalidator.cpp
CSS/Interpolation.cpp
CSS/InvalidationSet.cpp
CSS/Length.cpp
CSS/LengthBox.cpp
Expand Down Expand Up @@ -234,8 +232,6 @@ set(SOURCES
CSS/Parser/ValueParsing.cpp
CSS/Percentage.cpp
CSS/PreferredColorScheme.cpp
CSS/PreferredContrast.cpp
CSS/PreferredMotion.cpp
CSS/Ratio.cpp
CSS/Resolution.cpp
CSS/Screen.cpp
Expand All @@ -244,7 +240,6 @@ set(SOURCES
CSS/SelectorRustBridge.cpp
CSS/SelectorMatching.cpp
CSS/Serialize.cpp
CSS/Size.cpp
CSS/Sizing.cpp
CSS/StyleComputer.cpp
CSS/StyleInvalidation.cpp
Expand Down
2 changes: 0 additions & 2 deletions Libraries/LibWeb/CSS/CSSCounterStyleRule.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,6 @@ class CSSCounterStyleRule : public CSSRule {

Utf16String speak_as() const;
void set_speak_as(Utf16String const& speak_as);
RefPtr<StyleValue const> const& speak_as_style_value() const { return m_speak_as; }

// https://drafts.csswg.org/css-counter-styles-3/#non-overridable-counter-style-names
static bool matches_non_overridable_counter_style_name(Utf16View name)
{
Expand Down
12 changes: 0 additions & 12 deletions Libraries/LibWeb/CSS/CSSImportRule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -298,18 +298,6 @@ Optional<Utf16String> CSSImportRule::supports_text() const
return m_supports->to_string();
}

Optional<SelectorList> const& CSSImportRule::scope_start_selectors() const
{
VERIFY(m_scope.has_value());
return m_scope->start_selectors;
}

Optional<SelectorList> const& CSSImportRule::scope_end_selectors() const
{
VERIFY(m_scope.has_value());
return m_scope->end_selectors;
}

Optional<SelectorList> const& CSSImportRule::scope_start_selectors_for_matching() const
{
VERIFY(m_scope.has_value());
Expand Down
2 changes: 0 additions & 2 deletions Libraries/LibWeb/CSS/CSSImportRule.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,6 @@ class WEB_API CSSImportRule final

bool matches() const;
bool has_scope() const { return m_scope.has_value(); }
Optional<SelectorList> const& scope_start_selectors() const;
Optional<SelectorList> const& scope_end_selectors() const;
Optional<SelectorList> const& scope_start_selectors_for_matching() const;
Optional<SelectorList> const& scope_end_selectors_for_matching() const;

Expand Down
1 change: 0 additions & 1 deletion Libraries/LibWeb/CSS/CSSNamespaceRule.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ class CSSNamespaceRule final : public CSSRule {

virtual ~CSSNamespaceRule() = default;

void set_namespace_uri(Utf16FlyString value) { m_namespace_uri = move(value); }
Utf16FlyString const& namespace_uri() const { return m_namespace_uri; }
void set_prefix(Utf16FlyString value) { m_prefix = move(value); }
Utf16FlyString const& prefix() const { return m_prefix; }
Expand Down
2 changes: 0 additions & 2 deletions Libraries/LibWeb/CSS/CSSPropertyRule.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ class CSSPropertyRule final : public CSSRule {
Utf16FlyString const& syntax() const { return m_syntax; }
bool inherits() const { return m_inherits; }
Optional<Utf16String> initial_value() const;
RefPtr<StyleValue const> initial_style_value() const { return m_initial_value; }

CustomPropertyRegistration to_registration() const;

private:
Expand Down
18 changes: 0 additions & 18 deletions Libraries/LibWeb/CSS/CSSScopeRule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ void CSSScopeRule::initialize(JS::Realm& realm)
void CSSScopeRule::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_cached_nearest_ancestor_scope_rule);
}

Optional<Utf16String> CSSScopeRule::start() const
Expand Down Expand Up @@ -150,28 +149,11 @@ GC::Ptr<CSSRule const> nearest_ancestor_scope_rule_for_matching(CSSRule const& s
return nearest_scoped_owner_import(scope_rule.parent_style_sheet());
}

GC::Ptr<CSSScopeRule const> CSSScopeRule::nearest_ancestor_scope_rule() const
{
if (m_cached_nearest_ancestor_scope_rule.has_value())
return m_cached_nearest_ancestor_scope_rule.value();

for (auto const* parent = parent_rule(); parent; parent = parent->parent_rule()) {
if (auto const* scope_rule = as_if<CSSScopeRule const>(parent)) {
m_cached_nearest_ancestor_scope_rule = scope_rule;
return m_cached_nearest_ancestor_scope_rule.value();
}
}

m_cached_nearest_ancestor_scope_rule = nullptr;
return m_cached_nearest_ancestor_scope_rule.value();
}

void CSSScopeRule::clear_caches()
{
Base::clear_caches();
m_cached_start_selectors_for_matching.clear();
m_cached_end_selectors_for_matching.clear();
m_cached_nearest_ancestor_scope_rule.clear();
}

// https://drafts.csswg.org/cssom-1/#serialize-a-css-rule
Expand Down
3 changes: 0 additions & 3 deletions Libraries/LibWeb/CSS/CSSScopeRule.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ class CSSScopeRule final : public CSSGroupingRule {
Optional<SelectorList> const& end_selectors() const { return m_end_selectors; }
Optional<SelectorList> const& start_selectors_for_matching() const;
Optional<SelectorList> const& end_selectors_for_matching() const;
GC::Ptr<CSSScopeRule const> nearest_ancestor_scope_rule() const;

Optional<Utf16String> start() const;
Optional<Utf16String> end() const;

Expand All @@ -46,7 +44,6 @@ class CSSScopeRule final : public CSSGroupingRule {
Optional<SelectorList> m_end_selectors;
mutable Optional<SelectorList> m_cached_start_selectors_for_matching;
mutable Optional<SelectorList> m_cached_end_selectors_for_matching;
mutable Optional<GC::Ptr<CSSScopeRule const>> m_cached_nearest_ancestor_scope_rule;
};

template<>
Expand Down
10 changes: 5 additions & 5 deletions Libraries/LibWeb/CSS/CSSStyleProperties.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,7 @@ Optional<StyleProperty> CSSStyleProperties::get_property_internal(PropertyNameAn
auto const& original_shorthand_value = list.first()->as_pending_substitution().original_shorthand_value();
auto all_from_same_original = all_of(list, [&](auto const& value) {
return value->is_pending_substitution()
&& &value->as_pending_substitution().original_shorthand_value() == &original_shorthand_value;
&& value->as_pending_substitution().original_shorthand_value().rust_style_value_data() == original_shorthand_value.rust_style_value_data();
});
if (all_from_same_original) {
return StyleProperty {
Expand Down Expand Up @@ -973,27 +973,27 @@ RefPtr<StyleValue const> CSSStyleProperties::style_value_for_computed_property(L
// none or contents, and the property is not over-constrained, then the resolved value is the used value.
// Otherwise the resolved value is the computed value.
case PropertyID::Bottom: {
auto& inset = layout_node.computed_values().inset();
auto inset = layout_node.computed_values().inset();
if (auto maybe_used_value = used_value_for_inset(inset.bottom(), inset.top(), [](auto const& paintable_box) { return paintable_box.box_model().inset.bottom; }); maybe_used_value.has_value())
return LengthStyleValue::create(Length::make_px(maybe_used_value.release_value()));

return style_value_for_length_percentage_or_auto(inset.bottom());
}
case PropertyID::Left: {
auto& inset = layout_node.computed_values().inset();
auto inset = layout_node.computed_values().inset();
if (auto maybe_used_value = used_value_for_inset(inset.left(), inset.right(), [](auto const& paintable_box) { return paintable_box.box_model().inset.left; }); maybe_used_value.has_value())
return LengthStyleValue::create(Length::make_px(maybe_used_value.release_value()));
return style_value_for_length_percentage_or_auto(inset.left());
}
case PropertyID::Right: {
auto& inset = layout_node.computed_values().inset();
auto inset = layout_node.computed_values().inset();
if (auto maybe_used_value = used_value_for_inset(inset.right(), inset.left(), [](auto const& paintable_box) { return paintable_box.box_model().inset.right; }); maybe_used_value.has_value())
return LengthStyleValue::create(Length::make_px(maybe_used_value.release_value()));

return style_value_for_length_percentage_or_auto(inset.right());
}
case PropertyID::Top: {
auto& inset = layout_node.computed_values().inset();
auto inset = layout_node.computed_values().inset();
if (auto maybe_used_value = used_value_for_inset(inset.top(), inset.bottom(), [](auto const& paintable_box) { return paintable_box.box_model().inset.top; }); maybe_used_value.has_value())
return LengthStyleValue::create(Length::make_px(maybe_used_value.release_value()));

Expand Down
2 changes: 0 additions & 2 deletions Libraries/LibWeb/CSS/CSSStyleProperties.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,6 @@ class WEB_API CSSStyleProperties
Vector<StyleProperty> const& properties() const { return m_properties; }
OrderedHashMap<Utf16FlyString, StyleProperty> const& custom_properties() const { return m_custom_properties; }

size_t custom_property_count() const { return m_custom_properties.size(); }

virtual bool has_property(PropertyNameAndID const&) const override;
bool has_property(PropertyID) const;

Expand Down
Loading