Browse Source

LibWeb: Set KeyframeEffect's pseudo-element if applicable

Matthew Olsson 1 year ago
parent
commit
2ee022dead

+ 28 - 9
Userland/Libraries/LibWeb/Animations/KeyframeEffect.cpp

@@ -630,7 +630,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<KeyframeEffect>> KeyframeEffect::construct_
         // When assigning this property, the error-handling defined for the pseudoElement setter on the interface is
         // 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
         // applied. If the setter requires an exception to be thrown, this procedure must throw the same exception and
         // abort all further steps.
         // abort all further steps.
-        effect->set_pseudo_element(options.get<KeyframeEffectOptions>().pseudo_element);
+        TRY(effect->set_pseudo_element(options.get<KeyframeEffectOptions>().pseudo_element));
     }
     }
     //     Otherwise,
     //     Otherwise,
     else {
     else {
@@ -730,16 +730,14 @@ void KeyframeEffect::set_target(DOM::Element* target)
         m_target_element->associate_with_effect(*this);
         m_target_element->associate_with_effect(*this);
 }
 }
 
 
-void KeyframeEffect::set_pseudo_element(Optional<String> pseudo_element)
+WebIDL::ExceptionOr<void> KeyframeEffect::set_pseudo_element(Optional<String> pseudo_element)
 {
 {
+    auto& realm = this->realm();
+
     // On setting, sets the target pseudo-selector of the animation effect to the provided value after applying the
     // On setting, sets the target pseudo-selector of the animation effect to the provided value after applying the
     // following exceptions:
     // following exceptions:
 
 
     // FIXME:
     // FIXME:
-    // - If the provided value is not null and is an invalid <pseudo-element-selector>, 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
     // - 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
     //   ':first-line') is specified, the target pseudo-selector must be set to the equivalent two-colon selector
     //   (e.g. '::before').
     //   (e.g. '::before').
@@ -747,12 +745,33 @@ void KeyframeEffect::set_pseudo_element(Optional<String> pseudo_element)
         auto value = pseudo_element.value();
         auto value = pseudo_element.value();
 
 
         if (value == ":before" || value == ":after" || value == ":first-letter" || value == ":first-line") {
         if (value == ":before" || value == ":after" || value == ":first-letter" || value == ":first-line") {
-            m_target_pseudo_selector = MUST(String::formatted(":{}", value));
-            return;
+            m_target_pseudo_selector = CSS::Selector::PseudoElement::from_string(MUST(value.substring_from_byte_offset(1)));
+            return {};
         }
         }
     }
     }
 
 
-    m_target_pseudo_selector = pseudo_element;
+    // - If the provided value is not null and is an invalid <pseudo-element-selector>, the user agent must throw a
+    //   DOMException with error name SyntaxError and leave the target pseudo-selector of this animation effect
+    //   unchanged.
+    if (pseudo_element.has_value()) {
+        auto pseudo_element_without_colons = MUST(pseudo_element->replace("::"sv, ""sv, ReplaceMode::FirstOnly));
+        if (auto value = CSS::Selector::PseudoElement::from_string(pseudo_element_without_colons); value.has_value()) {
+            m_target_pseudo_selector = value;
+        } else {
+            return WebIDL::SyntaxError::create(realm, MUST(String::formatted("Invalid pseudo-element selector: \"{}\"", pseudo_element.value())));
+        }
+    } else {
+        m_target_pseudo_selector = {};
+    }
+
+    return {};
+}
+
+Optional<CSS::Selector::PseudoElement::Type> KeyframeEffect::pseudo_element_type() const
+{
+    if (!m_target_pseudo_selector.has_value())
+        return {};
+    return m_target_pseudo_selector->type();
 }
 }
 
 
 // https://www.w3.org/TR/web-animations-1/#dom-keyframeeffect-getkeyframes
 // https://www.w3.org/TR/web-animations-1/#dom-keyframeeffect-getkeyframes

+ 8 - 3
Userland/Libraries/LibWeb/Animations/KeyframeEffect.h

@@ -12,6 +12,7 @@
 #include <LibWeb/Bindings/KeyframeEffectPrototype.h>
 #include <LibWeb/Bindings/KeyframeEffectPrototype.h>
 #include <LibWeb/Bindings/PlatformObject.h>
 #include <LibWeb/Bindings/PlatformObject.h>
 #include <LibWeb/CSS/PropertyID.h>
 #include <LibWeb/CSS/PropertyID.h>
+#include <LibWeb/CSS/Selector.h>
 #include <LibWeb/CSS/StyleValue.h>
 #include <LibWeb/CSS/StyleValue.h>
 
 
 namespace Web::Animations {
 namespace Web::Animations {
@@ -84,8 +85,12 @@ public:
     DOM::Element* target() const override { return m_target_element; }
     DOM::Element* target() const override { return m_target_element; }
     void set_target(DOM::Element* target);
     void set_target(DOM::Element* target);
 
 
-    Optional<String> pseudo_element() const { return m_target_pseudo_selector; }
-    void set_pseudo_element(Optional<String>);
+    // JS bindings
+    Optional<StringView> pseudo_element() const { return m_target_pseudo_selector->name(); }
+    WebIDL::ExceptionOr<void> set_pseudo_element(Optional<String>);
+
+    Optional<CSS::Selector::PseudoElement::Type> pseudo_element_type() const;
+    void set_pseudo_element(Optional<CSS::Selector::PseudoElement> pseudo_element) { m_target_pseudo_selector = pseudo_element; }
 
 
     Bindings::CompositeOperation composite() const { return m_composite; }
     Bindings::CompositeOperation composite() const { return m_composite; }
     void set_composite(Bindings::CompositeOperation value) { m_composite = value; }
     void set_composite(Bindings::CompositeOperation value) { m_composite = value; }
@@ -109,7 +114,7 @@ private:
     JS::GCPtr<DOM::Element> m_target_element {};
     JS::GCPtr<DOM::Element> m_target_element {};
 
 
     // https://www.w3.org/TR/web-animations-1/#dom-keyframeeffect-pseudoelement
     // https://www.w3.org/TR/web-animations-1/#dom-keyframeeffect-pseudoelement
-    Optional<String> m_target_pseudo_selector {};
+    Optional<CSS::Selector::PseudoElement> m_target_pseudo_selector {};
 
 
     // https://www.w3.org/TR/web-animations-1/#dom-keyframeeffect-composite
     // https://www.w3.org/TR/web-animations-1/#dom-keyframeeffect-composite
     Bindings::CompositeOperation m_composite { Bindings::CompositeOperation::Replace };
     Bindings::CompositeOperation m_composite { Bindings::CompositeOperation::Replace };

+ 4 - 1
Userland/Libraries/LibWeb/CSS/StyleComputer.cpp

@@ -1074,6 +1074,8 @@ ErrorOr<void> StyleComputer::compute_cascaded_values(StyleProperties& style, DOM
             effect->set_timing_function(move(timing_function));
             effect->set_timing_function(move(timing_function));
             effect->set_fill_mode(Animations::css_fill_mode_to_bindings_fill_mode(fill_mode));
             effect->set_fill_mode(Animations::css_fill_mode_to_bindings_fill_mode(fill_mode));
             effect->set_playback_direction(Animations::css_animation_direction_to_bindings_playback_direction(direction));
             effect->set_playback_direction(Animations::css_animation_direction_to_bindings_playback_direction(direction));
+            if (pseudo_element.has_value())
+                effect->set_pseudo_element(Selector::PseudoElement { pseudo_element.value() });
 
 
             auto animation = CSSAnimation::create(realm);
             auto animation = CSSAnimation::create(realm);
             animation->set_id(animation_name.release_value());
             animation->set_id(animation_name.release_value());
@@ -1099,7 +1101,8 @@ ErrorOr<void> StyleComputer::compute_cascaded_values(StyleProperties& style, DOM
 
 
         if (auto effect = animation->effect(); effect && effect->is_keyframe_effect()) {
         if (auto effect = animation->effect(); effect && effect->is_keyframe_effect()) {
             auto& keyframe_effect = *static_cast<Animations::KeyframeEffect*>(effect.ptr());
             auto& keyframe_effect = *static_cast<Animations::KeyframeEffect*>(effect.ptr());
-            TRY(collect_animation_into(keyframe_effect, style));
+            if (keyframe_effect.pseudo_element_type() == pseudo_element)
+                TRY(collect_animation_into(keyframe_effect, style));
         }
         }
     }
     }