KeyframeEffect.cpp 6.7 KB

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