From 1ca46afa2f05b00bbb46e48b551317dbe2222429 Mon Sep 17 00:00:00 2001 From: Matthew Olsson Date: Mon, 6 Nov 2023 15:17:19 +0000 Subject: [PATCH] LibWeb: Add the Animation IDL object --- .../Libraries/LibWeb/Animations/Animation.cpp | 138 ++++++++++++++++++ .../Libraries/LibWeb/Animations/Animation.h | 87 +++++++++++ .../Libraries/LibWeb/Animations/Animation.idl | 42 ++++++ Userland/Libraries/LibWeb/CMakeLists.txt | 1 + Userland/Libraries/LibWeb/Forward.h | 1 + Userland/Libraries/LibWeb/idl_files.cmake | 1 + 6 files changed, 270 insertions(+) create mode 100644 Userland/Libraries/LibWeb/Animations/Animation.cpp create mode 100644 Userland/Libraries/LibWeb/Animations/Animation.h create mode 100644 Userland/Libraries/LibWeb/Animations/Animation.idl diff --git a/Userland/Libraries/LibWeb/Animations/Animation.cpp b/Userland/Libraries/LibWeb/Animations/Animation.cpp new file mode 100644 index 00000000000..789c552330e --- /dev/null +++ b/Userland/Libraries/LibWeb/Animations/Animation.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2023, Matthew Olsson . + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Web::Animations { + +// https://www.w3.org/TR/web-animations-1/#dom-animation-animation +JS::NonnullGCPtr Animation::create(JS::Realm& realm, JS::GCPtr effect, JS::GCPtr timeline) +{ + // 1. Let animation be a new Animation object. + auto animation = realm.heap().allocate(realm, realm); + + // 2. Run the procedure to set the timeline of an animation on animation passing timeline as the new timeline or, if + // a timeline argument is missing, passing the default document timeline of the Document associated with the + // Window that is the current global object. + if (!timeline) { + auto& window = verify_cast(HTML::current_global_object()); + timeline = window.associated_document().timeline(); + } + animation->set_timeline(timeline); + + // 3. Run the procedure to set the associated effect of an animation on animation passing source as the new effect. + animation->set_effect(effect); + + return animation; +} + +WebIDL::ExceptionOr> Animation::construct_impl(JS::Realm& realm, JS::GCPtr effect, JS::GCPtr timeline) +{ + return create(realm, effect, timeline); +} + +// https://www.w3.org/TR/web-animations-1/#animation-set-the-associated-effect-of-an-animation +void Animation::set_effect(JS::GCPtr new_effect) +{ + // FIXME: Implement + (void)new_effect; +} + +// https://www.w3.org/TR/web-animations-1/#animation-set-the-timeline-of-an-animation +void Animation::set_timeline(JS::GCPtr new_timeline) +{ + // FIXME: Implement + (void)new_timeline; +} + +// https://www.w3.org/TR/web-animations-1/#dom-animation-starttime +// https://www.w3.org/TR/web-animations-1/#set-the-start-time +void Animation::set_start_time(Optional const& new_start_time) +{ + // FIXME: Implement + (void)new_start_time; +} + +// https://www.w3.org/TR/web-animations-1/#animation-current-time +Optional Animation::current_time() const +{ + // FIXME: Implement + return {}; +} + +// https://www.w3.org/TR/web-animations-1/#animation-set-the-current-time +WebIDL::ExceptionOr Animation::set_current_time(Optional const& seek_time) +{ + // FIXME: Implement + (void)seek_time; + return {}; +} + +// https://www.w3.org/TR/web-animations-1/#dom-animation-playbackrate +// https://www.w3.org/TR/web-animations-1/#set-the-playback-rate +WebIDL::ExceptionOr Animation::set_playback_rate(double new_playback_rate) +{ + // FIXME: Implement + (void)new_playback_rate; + return {}; +} + +// https://www.w3.org/TR/web-animations-1/#animation-play-state +Bindings::AnimationPlayState Animation::play_state() const +{ + // FIXME: Implement + return Bindings::AnimationPlayState::Idle; +} + +JS::NonnullGCPtr Animation::current_ready_promise() const +{ + if (!m_current_ready_promise) { + // The current ready promise is initially a resolved Promise created using the procedure to create a new + // resolved Promise with the animation itself as its value and created in the relevant Realm of the animation. + m_current_ready_promise = WebIDL::create_resolved_promise(realm(), this); + } + + return *m_current_ready_promise; +} + +JS::NonnullGCPtr Animation::current_finished_promise() const +{ + if (!m_current_finished_promise) { + // The current finished promise is initially a pending Promise object. + m_current_finished_promise = WebIDL::create_promise(realm()); + } + + return *m_current_finished_promise; +} + +Animation::Animation(JS::Realm& realm) + : DOM::EventTarget(realm) +{ +} + +void Animation::initialize(JS::Realm& realm) +{ + Base::initialize(realm); + set_prototype(&Bindings::ensure_web_prototype(realm, "Animation")); +} + +void Animation::visit_edges(Cell::Visitor& visitor) +{ + Base::visit_edges(visitor); + visitor.visit(m_effect); + visitor.visit(m_timeline); + visitor.visit(m_current_ready_promise); + visitor.visit(m_current_finished_promise); +} + +} diff --git a/Userland/Libraries/LibWeb/Animations/Animation.h b/Userland/Libraries/LibWeb/Animations/Animation.h new file mode 100644 index 00000000000..54a803d8849 --- /dev/null +++ b/Userland/Libraries/LibWeb/Animations/Animation.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2023, Matthew Olsson . + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +namespace Web::Animations { + +// https://www.w3.org/TR/web-animations-1/#the-animation-interface +class Animation : public DOM::EventTarget { + WEB_PLATFORM_OBJECT(Animation, DOM::EventTarget); + +public: + static JS::NonnullGCPtr create(JS::Realm&, JS::GCPtr, JS::GCPtr); + static WebIDL::ExceptionOr> construct_impl(JS::Realm&, JS::GCPtr, JS::GCPtr); + + FlyString const& id() const { return m_id; } + void set_id(FlyString value) { m_id = move(value); } + + JS::GCPtr effect() const { return m_effect; } + void set_effect(JS::GCPtr); + + JS::GCPtr timeline() const { return m_timeline; } + void set_timeline(JS::GCPtr); + + Optional const& start_time() const { return m_start_time; } + void set_start_time(Optional const&); + + Optional current_time() const; + WebIDL::ExceptionOr set_current_time(Optional const&); + + double playback_rate() const { return m_playback_rate; } + WebIDL::ExceptionOr set_playback_rate(double value); + + Bindings::AnimationPlayState play_state() const; + + Bindings::AnimationReplaceState replace_state() const { return m_replace_state; } + + // https://www.w3.org/TR/web-animations-1/#dom-animation-ready + JS::NonnullGCPtr ready() const { return *current_ready_promise()->promise(); } + + // https://www.w3.org/TR/web-animations-1/#dom-animation-finished + JS::NonnullGCPtr finished() const { return *current_finished_promise()->promise(); } + +protected: + Animation(JS::Realm&); + + virtual void initialize(JS::Realm&) override; + virtual void visit_edges(Cell::Visitor&) override; + +private: + JS::NonnullGCPtr current_ready_promise() const; + JS::NonnullGCPtr current_finished_promise() const; + + // https://www.w3.org/TR/web-animations-1/#dom-animation-id + FlyString m_id; + + // https://www.w3.org/TR/web-animations-1/#dom-animation-effect + JS::GCPtr m_effect; + + // https://www.w3.org/TR/web-animations-1/#dom-animation-timeline + JS::GCPtr m_timeline; + + // https://www.w3.org/TR/web-animations-1/#animation-start-time + Optional m_start_time {}; + + // https://www.w3.org/TR/web-animations-1/#playback-rate + double m_playback_rate { 1.0 }; + + // https://www.w3.org/TR/web-animations-1/#dom-animation-replacestate + Bindings::AnimationReplaceState m_replace_state { Bindings::AnimationReplaceState::Active }; + + // Note: The following promises are initialized lazily to avoid constructing them outside of an execution context + // https://www.w3.org/TR/web-animations-1/#current-ready-promise + mutable JS::GCPtr m_current_ready_promise; + + // https://www.w3.org/TR/web-animations-1/#current-finished-promise + mutable JS::GCPtr m_current_finished_promise; +}; + +} diff --git a/Userland/Libraries/LibWeb/Animations/Animation.idl b/Userland/Libraries/LibWeb/Animations/Animation.idl new file mode 100644 index 00000000000..8379c033f36 --- /dev/null +++ b/Userland/Libraries/LibWeb/Animations/Animation.idl @@ -0,0 +1,42 @@ +#import +#import + +// https://www.w3.org/TR/web-animations-1/#the-animation-interface +[Exposed=Window] +interface Animation : EventTarget { + constructor(optional AnimationEffect? effect = null, + optional AnimationTimeline? timeline); + attribute DOMString id; + attribute AnimationEffect? effect; + attribute AnimationTimeline? timeline; + attribute double? startTime; + attribute double? currentTime; + attribute double playbackRate; + readonly attribute AnimationPlayState playState; + readonly attribute AnimationReplaceState replaceState; + // FIXME: + // readonly attribute boolean pending; + readonly attribute Promise ready; + readonly attribute Promise finished; + + // FIXME: + // attribute EventHandler onfinish; + // attribute EventHandler oncancel; + // attribute EventHandler onremove; + + // undefined cancel(); + // undefined finish(); + // undefined play(); + // undefined pause(); + // undefined updatePlaybackRate(double playbackRate); + // undefined reverse(); + // undefined persist(); + // [CEReactions] + // undefined commitStyles(); +}; + +// https://www.w3.org/TR/web-animations-1/#the-animationplaystate-enumeration +enum AnimationPlayState { "idle", "running", "paused", "finished" }; + +// https://www.w3.org/TR/web-animations-1/#the-animationreplacestate-enumeration +enum AnimationReplaceState { "active", "removed", "persisted" }; diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index c361985ea89..aa16ed53ee1 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -2,6 +2,7 @@ include(libweb_generators) include(accelerated_graphics) set(SOURCES + Animations/Animation.cpp Animations/AnimationEffect.cpp Animations/AnimationTimeline.cpp Animations/DocumentTimeline.cpp diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h index e6791298589..42b33c64e7c 100644 --- a/Userland/Libraries/LibWeb/Forward.h +++ b/Userland/Libraries/LibWeb/Forward.h @@ -27,6 +27,7 @@ class RecordingPainter; } namespace Web::Animations { +class Animation; class AnimationEffect; class AnimationTimeline; class DocumentTimeline; diff --git a/Userland/Libraries/LibWeb/idl_files.cmake b/Userland/Libraries/LibWeb/idl_files.cmake index 49f4e79f7b9..c3293ec2391 100644 --- a/Userland/Libraries/LibWeb/idl_files.cmake +++ b/Userland/Libraries/LibWeb/idl_files.cmake @@ -1,6 +1,7 @@ # This file is included from "Meta/CMake/libweb_data.cmake" # It is defined here so that there is no need to go to the Meta directory when adding new idl files +libweb_js_bindings(Animations/Animation) libweb_js_bindings(Animations/AnimationEffect) libweb_js_bindings(Animations/AnimationTimeline) libweb_js_bindings(Animations/DocumentTimeline)