diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp index f3c054aa3a..ed3cebf441 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp @@ -44,6 +44,7 @@ static bool is_platform_object(Type const& type) "ImageData"sv, "Instance"sv, "IntersectionObserverEntry"sv, + "KeyframeEffect"sv, "Memory"sv, "MessagePort"sv, "Module"sv, diff --git a/Userland/Libraries/LibWeb/Animations/AnimationEffect.h b/Userland/Libraries/LibWeb/Animations/AnimationEffect.h index ef4aa4dcfa..075b8a8004 100644 --- a/Userland/Libraries/LibWeb/Animations/AnimationEffect.h +++ b/Userland/Libraries/LibWeb/Animations/AnimationEffect.h @@ -127,6 +127,8 @@ public: Optional current_iteration() const; Optional transformed_progress() const; + virtual DOM::Element* target() const { return {}; } + protected: AnimationEffect(JS::Realm&); diff --git a/Userland/Libraries/LibWeb/Animations/KeyframeEffect.cpp b/Userland/Libraries/LibWeb/Animations/KeyframeEffect.cpp new file mode 100644 index 0000000000..b755ad2a0f --- /dev/null +++ b/Userland/Libraries/LibWeb/Animations/KeyframeEffect.cpp @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2023, Matthew Olsson + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace Web::Animations { + +JS::NonnullGCPtr KeyframeEffect::create(JS::Realm& realm) +{ + return realm.heap().allocate(realm, realm); +} + +// https://www.w3.org/TR/web-animations-1/#dom-keyframeeffect-keyframeeffect +WebIDL::ExceptionOr> KeyframeEffect::construct_impl( + JS::Realm& realm, + JS::Handle const& target, + JS::Handle const& keyframes, + Variant options) +{ + auto& vm = realm.vm(); + + // 1. Create a new KeyframeEffect object, effect. + auto effect = vm.heap().allocate(realm, realm); + + // 2. Set the target element of effect to target. + effect->set_target(target); + + // 3. Set the target pseudo-selector to the result corresponding to the first matching condition from below. + + // If options is a KeyframeEffectOptions object with a pseudoElement property, + if (options.has()) { + // Set the target pseudo-selector to the value of the pseudoElement property. + // + // When assigning this property, the error-handling defined for the pseudoElement setter on the interface is + // applied. If the setter requires an exception to be thrown, this procedure must throw the same exception and + // abort all further steps. + effect->set_pseudo_element(options.get().pseudo_element); + } + // Otherwise, + else { + // Set the target pseudo-selector to null. + // Note: This is the default when constructed + } + + // 4. Let timing input be the result corresponding to the first matching condition from below. + EffectTiming timing_input; + + // If options is a KeyframeEffectOptions object, + if (options.has()) { + // Let timing input be options. + timing_input = options.get(); + } + // Otherwise (if options is a double), + else { + // Let timing input be a new EffectTiming object with all members set to their default values and duration set + // to options. + timing_input = { .duration = options.get() }; + } + + // 5. Call the procedure to update the timing properties of an animation effect of effect from timing input. + // If that procedure causes an exception to be thrown, propagate the exception and abort this procedure. + TRY(effect->update_timing(timing_input.to_optional_effect_timing())); + + // 6. If options is a KeyframeEffectOptions object, assign the composite property of effect to the corresponding + // value from options. + // + // When assigning this property, the error-handling defined for the corresponding setter on the KeyframeEffect + // interface is applied. If the setter requires an exception to be thrown for the value specified by options, + // this procedure must throw the same exception and abort all further steps. + if (options.has()) + effect->set_composite(options.get().composite); + + // 7. Initialize the set of keyframes by performing the procedure defined for setKeyframes() passing keyframes as + // the input. + TRY(effect->set_keyframes(keyframes)); + + return effect; +} + +WebIDL::ExceptionOr> KeyframeEffect::construct_impl(JS::Realm& realm, JS::NonnullGCPtr source) +{ + auto& vm = realm.vm(); + + // 1. Create a new KeyframeEffect object, effect. + auto effect = vm.heap().allocate(realm, realm); + + // 2. Set the following properties of effect using the corresponding values of source: + + // - effect target, + effect->m_target_element = source->target(); + + // FIXME: + // - keyframes, + + // - composite operation, and + effect->set_composite(source->composite()); + + // - all specified timing properties: + + // - start delay, + effect->m_start_delay = source->m_start_delay; + + // - end delay, + effect->m_end_delay = source->m_end_delay; + + // - fill mode, + effect->m_fill_mode = source->m_fill_mode; + + // - iteration start, + effect->m_iteration_start = source->m_iteration_start; + + // - iteration count, + effect->m_iteration_count = source->m_iteration_count; + + // - iteration duration, + effect->m_iteration_duration = source->m_iteration_duration; + + // - playback direction, and + effect->m_playback_direction = source->m_playback_direction; + + // - timing function. + effect->m_easing_function = source->m_easing_function; + + return effect; +} + +void KeyframeEffect::set_pseudo_element(Optional pseudo_element) +{ + // FIXME: Implement this + (void)pseudo_element; +} + +// https://www.w3.org/TR/web-animations-1/#dom-keyframeeffect-getkeyframes +WebIDL::ExceptionOr> KeyframeEffect::get_keyframes() const +{ + // FIXME: Implement this + return Vector {}; +} + +// https://www.w3.org/TR/web-animations-1/#dom-keyframeeffect-setkeyframes +WebIDL::ExceptionOr KeyframeEffect::set_keyframes(JS::Object* keyframe_object) +{ + // FIXME: Implement this + (void)keyframe_object; + return {}; +} + +KeyframeEffect::KeyframeEffect(JS::Realm& realm) + : AnimationEffect(realm) +{ +} + +void KeyframeEffect::initialize(JS::Realm& realm) +{ + Base::initialize(realm); + set_prototype(&Bindings::ensure_web_prototype(realm, "KeyframeEffect")); +} + +void KeyframeEffect::visit_edges(Cell::Visitor& visitor) +{ + Base::visit_edges(visitor); + visitor.visit(m_target_element); +} + +} diff --git a/Userland/Libraries/LibWeb/Animations/KeyframeEffect.h b/Userland/Libraries/LibWeb/Animations/KeyframeEffect.h new file mode 100644 index 0000000000..3dd7b34787 --- /dev/null +++ b/Userland/Libraries/LibWeb/Animations/KeyframeEffect.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2023, Matthew Olsson + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace Web::Animations { + +// https://www.w3.org/TR/web-animations-1/#the-keyframeeffectoptions-dictionary +struct KeyframeEffectOptions : public EffectTiming { + Bindings::CompositeOperation composite { Bindings::CompositeOperation::Replace }; + Optional pseudo_element {}; +}; + +// https://www.w3.org/TR/web-animations-1/#dictdef-basepropertyindexedkeyframe +struct BasePropertyIndexedKeyframe { + Variant, Vector>> offset { Vector> {} }; + Variant> easing { Vector {} }; + Variant> composite { Vector {} }; +}; + +// https://www.w3.org/TR/web-animations-1/#dictdef-basekeyframe +struct BaseKeyframe { + Optional offset {}; + String easing { "linear"_string }; + Bindings::CompositeOperationOrAuto composite { Bindings::CompositeOperationOrAuto::Auto }; + + Optional computed_offset {}; +}; + +// https://www.w3.org/TR/web-animations-1/#the-keyframeeffect-interface +class KeyframeEffect : public AnimationEffect { + WEB_PLATFORM_OBJECT(KeyframeEffect, AnimationEffect); + +public: + static JS::NonnullGCPtr create(JS::Realm&); + + static WebIDL::ExceptionOr> construct_impl( + JS::Realm&, + JS::Handle const& target, + JS::Handle const& keyframes, + Variant options = KeyframeEffectOptions {}); + + static WebIDL::ExceptionOr> construct_impl(JS::Realm&, JS::NonnullGCPtr source); + + DOM::Element* target() const override { return m_target_element; } + void set_target(DOM::Element* target) { m_target_element = target; } + + Optional pseudo_element() const { return m_target_pseudo_selector; } + void set_pseudo_element(Optional); + + Bindings::CompositeOperation composite() const { return m_composite; } + void set_composite(Bindings::CompositeOperation value) { m_composite = value; } + + WebIDL::ExceptionOr> get_keyframes() const; + WebIDL::ExceptionOr set_keyframes(JS::Object*); + +private: + KeyframeEffect(JS::Realm&); + + virtual void initialize(JS::Realm&) override; + virtual void visit_edges(Cell::Visitor&) override; + + // https://www.w3.org/TR/web-animations-1/#effect-target-target-element + JS::GCPtr m_target_element {}; + + // https://www.w3.org/TR/web-animations-1/#dom-keyframeeffect-pseudoelement + Optional m_target_pseudo_selector {}; + + // https://www.w3.org/TR/web-animations-1/#dom-keyframeeffect-composite + Bindings::CompositeOperation m_composite { Bindings::CompositeOperation::Replace }; + + // https://www.w3.org/TR/web-animations-1/#keyframe + Vector m_keyframes {}; +}; + +} diff --git a/Userland/Libraries/LibWeb/Animations/KeyframeEffect.idl b/Userland/Libraries/LibWeb/Animations/KeyframeEffect.idl new file mode 100644 index 0000000000..d5b8561c4b --- /dev/null +++ b/Userland/Libraries/LibWeb/Animations/KeyframeEffect.idl @@ -0,0 +1,43 @@ +#import +#import + +// https://www.w3.org/TR/web-animations-1/#the-compositeoperation-enumeration +enum CompositeOperation { "replace", "add", "accumulate" }; + +// https://www.w3.org/TR/web-animations-1/#enumdef-compositeoperationorauto +enum CompositeOperationOrAuto { "replace", "add", "accumulate", "auto" }; + +// https://www.w3.org/TR/web-animations-1/#the-keyframeeffectoptions-dictionary +dictionary KeyframeEffectOptions : EffectTiming { + CompositeOperation composite = "replace"; + CSSOMString? pseudoElement = null; +}; + +// https://www.w3.org/TR/web-animations-1/#dictdef-basepropertyindexedkeyframe +dictionary BasePropertyIndexedKeyframe { + (double? or sequence) offset = []; + (DOMString or sequence) easing = []; + (CompositeOperationOrAuto or sequence) composite = []; +}; + +// https://www.w3.org/TR/web-animations-1/#dictdef-basekeyframe +dictionary BaseKeyframe { + double? offset = null; + DOMString easing = "linear"; + CompositeOperationOrAuto composite = "auto"; +}; + +// https://www.w3.org/TR/web-animations-1/#the-keyframeeffect-interface +[Exposed=Window] +interface KeyframeEffect : AnimationEffect { + constructor(Element? target, + object? keyframes, + optional (unrestricted double or KeyframeEffectOptions) options = {}); + constructor(KeyframeEffect source); + + attribute Element? target; + attribute CSSOMString? pseudoElement; + attribute CompositeOperation composite; + sequence getKeyframes(); + undefined setKeyframes(object? keyframes); +}; diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index f274bf8e99..98308de60e 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -7,6 +7,7 @@ set(SOURCES Animations/AnimationPlaybackEvent.cpp Animations/AnimationTimeline.cpp Animations/DocumentTimeline.cpp + Animations/KeyframeEffect.cpp Animations/TimingFunction.cpp ARIA/AriaData.cpp ARIA/ARIAMixin.cpp diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h index 0bd042366e..1c0fe9a44a 100644 --- a/Userland/Libraries/LibWeb/Forward.h +++ b/Userland/Libraries/LibWeb/Forward.h @@ -32,6 +32,7 @@ class AnimationEffect; class AnimationPlaybackEvent; class AnimationTimeline; class DocumentTimeline; +class KeyframeEffect; } namespace Web::ARIA { diff --git a/Userland/Libraries/LibWeb/idl_files.cmake b/Userland/Libraries/LibWeb/idl_files.cmake index 4f683e26bc..87b044973b 100644 --- a/Userland/Libraries/LibWeb/idl_files.cmake +++ b/Userland/Libraries/LibWeb/idl_files.cmake @@ -6,6 +6,7 @@ libweb_js_bindings(Animations/AnimationEffect) libweb_js_bindings(Animations/AnimationPlaybackEvent) libweb_js_bindings(Animations/AnimationTimeline) libweb_js_bindings(Animations/DocumentTimeline) +libweb_js_bindings(Animations/KeyframeEffect) libweb_js_bindings(Clipboard/Clipboard) libweb_js_bindings(Crypto/Crypto) libweb_js_bindings(Crypto/SubtleCrypto)