/* * Copyright (c) 2024, Matthew Olsson . * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include namespace Web::Animations { // https://www.w3.org/TR/web-animations-1/#dom-animatable-animate WebIDL::ExceptionOr> Animatable::animate(Optional> keyframes, Variant options) { // 1. Let target be the object on which this method was called. JS::NonnullGCPtr target { *static_cast(this) }; auto& realm = target->realm(); // 2. Construct a new KeyframeEffect object, effect, in the relevant Realm of target by using the same procedure as // the KeyframeEffect(target, keyframes, options) constructor, passing target as the target argument, and the // keyframes and options arguments as supplied. // // If the above procedure causes an exception to be thrown, propagate the exception and abort this procedure. auto effect = TRY(options.visit( [&](Empty) { return KeyframeEffect::construct_impl(realm, target, keyframes); }, [&](auto const& value) { return KeyframeEffect::construct_impl(realm, target, keyframes, value); })); // 3. If options is a KeyframeAnimationOptions object, let timeline be the timeline member of options or, if // timeline member of options is missing, be the default document timeline of the node document of the element // on which this method was called. Optional> timeline; if (options.has()) timeline = options.get().timeline; if (!timeline.has_value()) timeline = target->document().timeline(); // 4. Construct a new Animation object, animation, in the relevant Realm of target by using the same procedure as // the Animation() constructor, passing effect and timeline as arguments of the same name. auto animation = TRY(Animation::construct_impl(realm, effect, move(timeline))); // 5. If options is a KeyframeAnimationOptions object, assign the value of the id member of options to animation’s // id attribute. if (options.has()) animation->set_id(options.get().id); // 6. Run the procedure to play an animation for animation with the auto-rewind flag set to true. TRY(animation->play_an_animation(Animation::AutoRewind::Yes)); // 7. Return animation. return animation; } // https://www.w3.org/TR/web-animations-1/#dom-animatable-getanimations Vector> Animatable::get_animations(GetAnimationsOptions options) { // Returns the set of relevant animations for this object, or, if an options parameter is passed with subtree set to // true, returns the set of relevant animations for a subtree for this object. // The returned list is sorted using the composite order described for the associated animations of effects in // §5.4.2 The effect stack. if (!m_is_sorted_by_composite_order) { quick_sort(m_associated_animations, [](JS::NonnullGCPtr& a, JS::NonnullGCPtr& b) { auto& a_effect = verify_cast(*a->effect()); auto& b_effect = verify_cast(*b->effect()); return KeyframeEffect::composite_order(a_effect, b_effect) < 0; }); m_is_sorted_by_composite_order = true; } Vector> relevant_animations; for (auto const& animation : m_associated_animations) { if (animation->is_relevant()) relevant_animations.append(*animation); } if (options.subtree) { JS::NonnullGCPtr target { *static_cast(this) }; target->for_each_child_of_type([&](auto& child) { relevant_animations.extend(child.get_animations(options)); return IterationDecision::Continue; }); } return relevant_animations; } void Animatable::associate_with_animation(JS::NonnullGCPtr animation) { m_associated_animations.append(animation); m_is_sorted_by_composite_order = false; } void Animatable::disassociate_with_animation(JS::NonnullGCPtr animation) { m_associated_animations.remove_first_matching([&](auto element) { return animation == element; }); } void Animatable::visit_edges(JS::Cell::Visitor& visitor) { visitor.visit(m_associated_animations); for (auto const& cached_animation_source : m_cached_animation_name_source) visitor.visit(cached_animation_source); for (auto const& cached_animation_name : m_cached_animation_name_animation) visitor.visit(cached_animation_name); } JS::GCPtr Animatable::cached_animation_name_source(Optional pseudo_element) const { if (pseudo_element.has_value()) return m_cached_animation_name_source[to_underlying(pseudo_element.value()) + 1]; return m_cached_animation_name_source[0]; } void Animatable::set_cached_animation_name_source(JS::GCPtr value, Optional pseudo_element) { if (pseudo_element.has_value()) { m_cached_animation_name_source[to_underlying(pseudo_element.value()) + 1] = value; } else { m_cached_animation_name_source[0] = value; } } JS::GCPtr Animatable::cached_animation_name_animation(Optional pseudo_element) const { if (pseudo_element.has_value()) return m_cached_animation_name_animation[to_underlying(pseudo_element.value()) + 1]; return m_cached_animation_name_animation[0]; } void Animatable::set_cached_animation_name_animation(JS::GCPtr value, Optional pseudo_element) { if (pseudo_element.has_value()) { m_cached_animation_name_animation[to_underlying(pseudo_element.value()) + 1] = value; } else { m_cached_animation_name_animation[0] = value; } } }