瀏覽代碼

LibWeb: Add KeyframeEffect IDL objects

Matthew Olsson 1 年之前
父節點
當前提交
5824916f8c

+ 1 - 0
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,

+ 2 - 0
Userland/Libraries/LibWeb/Animations/AnimationEffect.h

@@ -127,6 +127,8 @@ public:
     Optional<double> current_iteration() const;
     Optional<double> transformed_progress() const;
 
+    virtual DOM::Element* target() const { return {}; }
+
 protected:
     AnimationEffect(JS::Realm&);
 

+ 169 - 0
Userland/Libraries/LibWeb/Animations/KeyframeEffect.cpp

@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2023, Matthew Olsson <mattco@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibWeb/Animations/KeyframeEffect.h>
+#include <LibWeb/WebIDL/ExceptionOr.h>
+
+namespace Web::Animations {
+
+JS::NonnullGCPtr<KeyframeEffect> KeyframeEffect::create(JS::Realm& realm)
+{
+    return realm.heap().allocate<KeyframeEffect>(realm, realm);
+}
+
+// https://www.w3.org/TR/web-animations-1/#dom-keyframeeffect-keyframeeffect
+WebIDL::ExceptionOr<JS::NonnullGCPtr<KeyframeEffect>> KeyframeEffect::construct_impl(
+    JS::Realm& realm,
+    JS::Handle<DOM::Element> const& target,
+    JS::Handle<JS::Object> const& keyframes,
+    Variant<double, KeyframeEffectOptions> options)
+{
+    auto& vm = realm.vm();
+
+    // 1. Create a new KeyframeEffect object, effect.
+    auto effect = vm.heap().allocate<KeyframeEffect>(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<KeyframeEffectOptions>()) {
+        // 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<KeyframeEffectOptions>().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<KeyframeEffectOptions>()) {
+        // Let timing input be options.
+        timing_input = options.get<KeyframeEffectOptions>();
+    }
+    //     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<double>() };
+    }
+
+    // 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<KeyframeEffectOptions>())
+        effect->set_composite(options.get<KeyframeEffectOptions>().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<JS::NonnullGCPtr<KeyframeEffect>> KeyframeEffect::construct_impl(JS::Realm& realm, JS::NonnullGCPtr<KeyframeEffect> source)
+{
+    auto& vm = realm.vm();
+
+    // 1. Create a new KeyframeEffect object, effect.
+    auto effect = vm.heap().allocate<KeyframeEffect>(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<String> pseudo_element)
+{
+    // FIXME: Implement this
+    (void)pseudo_element;
+}
+
+// https://www.w3.org/TR/web-animations-1/#dom-keyframeeffect-getkeyframes
+WebIDL::ExceptionOr<Vector<JS::Object*>> KeyframeEffect::get_keyframes() const
+{
+    // FIXME: Implement this
+    return Vector<JS::Object*> {};
+}
+
+// https://www.w3.org/TR/web-animations-1/#dom-keyframeeffect-setkeyframes
+WebIDL::ExceptionOr<void> 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<Bindings::KeyframeEffectPrototype>(realm, "KeyframeEffect"));
+}
+
+void KeyframeEffect::visit_edges(Cell::Visitor& visitor)
+{
+    Base::visit_edges(visitor);
+    visitor.visit(m_target_element);
+}
+
+}

+ 85 - 0
Userland/Libraries/LibWeb/Animations/KeyframeEffect.h

@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2023, Matthew Olsson <mattco@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/Optional.h>
+#include <LibWeb/Animations/AnimationEffect.h>
+#include <LibWeb/Bindings/KeyframeEffectPrototype.h>
+#include <LibWeb/Bindings/PlatformObject.h>
+#include <LibWeb/DOM/Element.h>
+
+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<String> pseudo_element {};
+};
+
+// https://www.w3.org/TR/web-animations-1/#dictdef-basepropertyindexedkeyframe
+struct BasePropertyIndexedKeyframe {
+    Variant<Optional<double>, Vector<Optional<double>>> offset { Vector<Optional<double>> {} };
+    Variant<String, Vector<String>> easing { Vector<String> {} };
+    Variant<Bindings::CompositeOperationOrAuto, Vector<Bindings::CompositeOperationOrAuto>> composite { Vector<Bindings::CompositeOperationOrAuto> {} };
+};
+
+// https://www.w3.org/TR/web-animations-1/#dictdef-basekeyframe
+struct BaseKeyframe {
+    Optional<double> offset {};
+    String easing { "linear"_string };
+    Bindings::CompositeOperationOrAuto composite { Bindings::CompositeOperationOrAuto::Auto };
+
+    Optional<double> 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<KeyframeEffect> create(JS::Realm&);
+
+    static WebIDL::ExceptionOr<JS::NonnullGCPtr<KeyframeEffect>> construct_impl(
+        JS::Realm&,
+        JS::Handle<DOM::Element> const& target,
+        JS::Handle<JS::Object> const& keyframes,
+        Variant<double, KeyframeEffectOptions> options = KeyframeEffectOptions {});
+
+    static WebIDL::ExceptionOr<JS::NonnullGCPtr<KeyframeEffect>> construct_impl(JS::Realm&, JS::NonnullGCPtr<KeyframeEffect> source);
+
+    DOM::Element* target() const override { return m_target_element; }
+    void set_target(DOM::Element* target) { m_target_element = target; }
+
+    Optional<String> pseudo_element() const { return m_target_pseudo_selector; }
+    void set_pseudo_element(Optional<String>);
+
+    Bindings::CompositeOperation composite() const { return m_composite; }
+    void set_composite(Bindings::CompositeOperation value) { m_composite = value; }
+
+    WebIDL::ExceptionOr<Vector<JS::Object*>> get_keyframes() const;
+    WebIDL::ExceptionOr<void> 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<DOM::Element> m_target_element {};
+
+    // https://www.w3.org/TR/web-animations-1/#dom-keyframeeffect-pseudoelement
+    Optional<String> 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<BaseKeyframe> m_keyframes {};
+};
+
+}

+ 43 - 0
Userland/Libraries/LibWeb/Animations/KeyframeEffect.idl

@@ -0,0 +1,43 @@
+#import <Animations/AnimationEffect.idl>
+#import <DOM/Element.idl>
+
+// 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<double?>)                         offset = [];
+    (DOMString or sequence<DOMString>)                     easing = [];
+    (CompositeOperationOrAuto or sequence<CompositeOperationOrAuto>) 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<object> getKeyframes();
+    undefined        setKeyframes(object? keyframes);
+};

+ 1 - 0
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

+ 1 - 0
Userland/Libraries/LibWeb/Forward.h

@@ -32,6 +32,7 @@ class AnimationEffect;
 class AnimationPlaybackEvent;
 class AnimationTimeline;
 class DocumentTimeline;
+class KeyframeEffect;
 }
 
 namespace Web::ARIA {

+ 1 - 0
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)