KeyframeEffect.cpp 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. /*
  2. * Copyright (c) 2023, Matthew Olsson <mattco@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibWeb/Animations/KeyframeEffect.h>
  7. #include <LibWeb/WebIDL/ExceptionOr.h>
  8. namespace Web::Animations {
  9. JS_DEFINE_ALLOCATOR(KeyframeEffect);
  10. JS::NonnullGCPtr<KeyframeEffect> KeyframeEffect::create(JS::Realm& realm)
  11. {
  12. return realm.heap().allocate<KeyframeEffect>(realm, realm);
  13. }
  14. // https://www.w3.org/TR/web-animations-1/#dom-keyframeeffect-keyframeeffect
  15. WebIDL::ExceptionOr<JS::NonnullGCPtr<KeyframeEffect>> KeyframeEffect::construct_impl(
  16. JS::Realm& realm,
  17. JS::Handle<DOM::Element> const& target,
  18. Optional<JS::Handle<JS::Object>> const& keyframes,
  19. Variant<double, KeyframeEffectOptions> options)
  20. {
  21. auto& vm = realm.vm();
  22. // 1. Create a new KeyframeEffect object, effect.
  23. auto effect = vm.heap().allocate<KeyframeEffect>(realm, realm);
  24. // 2. Set the target element of effect to target.
  25. effect->set_target(target);
  26. // 3. Set the target pseudo-selector to the result corresponding to the first matching condition from below.
  27. // If options is a KeyframeEffectOptions object with a pseudoElement property,
  28. if (options.has<KeyframeEffectOptions>()) {
  29. // Set the target pseudo-selector to the value of the pseudoElement property.
  30. //
  31. // When assigning this property, the error-handling defined for the pseudoElement setter on the interface is
  32. // applied. If the setter requires an exception to be thrown, this procedure must throw the same exception and
  33. // abort all further steps.
  34. effect->set_pseudo_element(options.get<KeyframeEffectOptions>().pseudo_element);
  35. }
  36. // Otherwise,
  37. else {
  38. // Set the target pseudo-selector to null.
  39. // Note: This is the default when constructed
  40. }
  41. // 4. Let timing input be the result corresponding to the first matching condition from below.
  42. EffectTiming timing_input;
  43. // If options is a KeyframeEffectOptions object,
  44. if (options.has<KeyframeEffectOptions>()) {
  45. // Let timing input be options.
  46. timing_input = options.get<KeyframeEffectOptions>();
  47. }
  48. // Otherwise (if options is a double),
  49. else {
  50. // Let timing input be a new EffectTiming object with all members set to their default values and duration set
  51. // to options.
  52. timing_input = { .duration = options.get<double>() };
  53. }
  54. // 5. Call the procedure to update the timing properties of an animation effect of effect from timing input.
  55. // If that procedure causes an exception to be thrown, propagate the exception and abort this procedure.
  56. TRY(effect->update_timing(timing_input.to_optional_effect_timing()));
  57. // 6. If options is a KeyframeEffectOptions object, assign the composite property of effect to the corresponding
  58. // value from options.
  59. //
  60. // When assigning this property, the error-handling defined for the corresponding setter on the KeyframeEffect
  61. // interface is applied. If the setter requires an exception to be thrown for the value specified by options,
  62. // this procedure must throw the same exception and abort all further steps.
  63. if (options.has<KeyframeEffectOptions>())
  64. effect->set_composite(options.get<KeyframeEffectOptions>().composite);
  65. // 7. Initialize the set of keyframes by performing the procedure defined for setKeyframes() passing keyframes as
  66. // the input.
  67. TRY(effect->set_keyframes(keyframes));
  68. return effect;
  69. }
  70. WebIDL::ExceptionOr<JS::NonnullGCPtr<KeyframeEffect>> KeyframeEffect::construct_impl(JS::Realm& realm, JS::NonnullGCPtr<KeyframeEffect> source)
  71. {
  72. auto& vm = realm.vm();
  73. // 1. Create a new KeyframeEffect object, effect.
  74. auto effect = vm.heap().allocate<KeyframeEffect>(realm, realm);
  75. // 2. Set the following properties of effect using the corresponding values of source:
  76. // - effect target,
  77. effect->m_target_element = source->target();
  78. // FIXME:
  79. // - keyframes,
  80. // - composite operation, and
  81. effect->set_composite(source->composite());
  82. // - all specified timing properties:
  83. // - start delay,
  84. effect->m_start_delay = source->m_start_delay;
  85. // - end delay,
  86. effect->m_end_delay = source->m_end_delay;
  87. // - fill mode,
  88. effect->m_fill_mode = source->m_fill_mode;
  89. // - iteration start,
  90. effect->m_iteration_start = source->m_iteration_start;
  91. // - iteration count,
  92. effect->m_iteration_count = source->m_iteration_count;
  93. // - iteration duration,
  94. effect->m_iteration_duration = source->m_iteration_duration;
  95. // - playback direction, and
  96. effect->m_playback_direction = source->m_playback_direction;
  97. // - timing function.
  98. effect->m_easing_function = source->m_easing_function;
  99. return effect;
  100. }
  101. void KeyframeEffect::set_pseudo_element(Optional<String> pseudo_element)
  102. {
  103. // On setting, sets the target pseudo-selector of the animation effect to the provided value after applying the
  104. // following exceptions:
  105. // FIXME:
  106. // - If the provided value is not null and is an invalid <pseudo-element-selector>, the user agent must throw a
  107. // DOMException with error name SyntaxError and leave the target pseudo-selector of this animation effect
  108. // unchanged.
  109. // - If one of the legacy Selectors Level 2 single-colon selectors (':before', ':after', ':first-letter', or
  110. // ':first-line') is specified, the target pseudo-selector must be set to the equivalent two-colon selector
  111. // (e.g. '::before').
  112. if (pseudo_element.has_value()) {
  113. auto value = pseudo_element.value();
  114. if (value == ":before" || value == ":after" || value == ":first-letter" || value == ":first-line") {
  115. m_target_pseudo_selector = MUST(String::formatted(":{}", value));
  116. return;
  117. }
  118. }
  119. m_target_pseudo_selector = pseudo_element;
  120. }
  121. // https://www.w3.org/TR/web-animations-1/#dom-keyframeeffect-getkeyframes
  122. WebIDL::ExceptionOr<Vector<JS::Object*>> KeyframeEffect::get_keyframes() const
  123. {
  124. // FIXME: Implement this
  125. return Vector<JS::Object*> {};
  126. }
  127. // https://www.w3.org/TR/web-animations-1/#dom-keyframeeffect-setkeyframes
  128. WebIDL::ExceptionOr<void> KeyframeEffect::set_keyframes(Optional<JS::Handle<JS::Object>> const&)
  129. {
  130. // FIXME: Implement this
  131. return {};
  132. }
  133. KeyframeEffect::KeyframeEffect(JS::Realm& realm)
  134. : AnimationEffect(realm)
  135. {
  136. }
  137. void KeyframeEffect::initialize(JS::Realm& realm)
  138. {
  139. Base::initialize(realm);
  140. set_prototype(&Bindings::ensure_web_prototype<Bindings::KeyframeEffectPrototype>(realm, "KeyframeEffect"_fly_string));
  141. }
  142. void KeyframeEffect::visit_edges(Cell::Visitor& visitor)
  143. {
  144. Base::visit_edges(visitor);
  145. visitor.visit(m_target_element);
  146. }
  147. }