/* * Copyright (c) 2023, Matthew Olsson * * SPDX-License-Identifier: BSD-2-Clause */ #include #include namespace Web::Animations { JS_DEFINE_ALLOCATOR(KeyframeEffect); 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, Optional> 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) { // On setting, sets the target pseudo-selector of the animation effect to the provided value after applying the // following exceptions: // FIXME: // - If the provided value is not null and is an invalid , the user agent must throw a // DOMException with error name SyntaxError and leave the target pseudo-selector of this animation effect // unchanged. // - If one of the legacy Selectors Level 2 single-colon selectors (':before', ':after', ':first-letter', or // ':first-line') is specified, the target pseudo-selector must be set to the equivalent two-colon selector // (e.g. '::before'). if (pseudo_element.has_value()) { auto value = pseudo_element.value(); if (value == ":before" || value == ":after" || value == ":first-letter" || value == ":first-line") { m_target_pseudo_selector = MUST(String::formatted(":{}", value)); return; } } m_target_pseudo_selector = 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(Optional> const&) { // FIXME: Implement this 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"_fly_string)); } void KeyframeEffect::visit_edges(Cell::Visitor& visitor) { Base::visit_edges(visitor); visitor.visit(m_target_element); } }