Diff
Modified: trunk/LayoutTests/imported/w3c/ChangeLog (260138 => 260139)
--- trunk/LayoutTests/imported/w3c/ChangeLog 2020-04-15 17:27:51 UTC (rev 260138)
+++ trunk/LayoutTests/imported/w3c/ChangeLog 2020-04-15 17:39:57 UTC (rev 260139)
@@ -1,3 +1,22 @@
+2020-04-15 Antoine Quint <[email protected]>
+
+ [Web Animations] Add support for `pseudoElement` on `KeyframeEffect` and `KeyframeEffectOptions`
+ https://bugs.webkit.org/show_bug.cgi?id=207290
+ <rdar://problem/59199003>
+
+ Reviewed by Antti Koivisto.
+
+ Mark 23 additional WPT tests as PASS.
+
+ * web-platform-tests/css/css-animations/Document-getAnimations.tentative-expected.txt:
+ * web-platform-tests/css/css-animations/Element-getAnimations.tentative-expected.txt:
+ * web-platform-tests/css/css-transitions/Document-getAnimations.tentative-expected.txt:
+ * web-platform-tests/web-animations/idlharness.window-expected.txt:
+ * web-platform-tests/web-animations/interfaces/Animatable/animate-expected.txt:
+ * web-platform-tests/web-animations/interfaces/Animation/commitStyles-expected.txt:
+ * web-platform-tests/web-animations/interfaces/KeyframeEffect/style-change-events-expected.txt:
+ * web-platform-tests/web-animations/interfaces/KeyframeEffect/target-expected.txt:
+
2020-04-14 Youenn Fablet <[email protected]>
Resync featurepolicy.js to fix WPT mediacapture-streams/MediaStream-default-feature-policy.https.html
Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-animations/Document-getAnimations.tentative-expected.txt (260138 => 260139)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-animations/Document-getAnimations.tentative-expected.txt 2020-04-15 17:27:51 UTC (rev 260138)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-animations/Document-getAnimations.tentative-expected.txt 2020-04-15 17:39:57 UTC (rev 260139)
@@ -13,5 +13,5 @@
PASS Yet-to-start CSS Animations are returned
PASS CSS Animations canceled via the API are not returned
PASS CSS Animations canceled and restarted via the API are returned
-FAIL CSS Animations targetting (pseudo-)elements should have correct order after sorting assert_equals: Animation #1 has null pseudo type expected (object) null but got (undefined) undefined
+PASS CSS Animations targetting (pseudo-)elements should have correct order after sorting
Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-animations/Element-getAnimations.tentative-expected.txt (260138 => 260139)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-animations/Element-getAnimations.tentative-expected.txt 2020-04-15 17:27:51 UTC (rev 260138)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-animations/Element-getAnimations.tentative-expected.txt 2020-04-15 17:39:57 UTC (rev 260139)
@@ -17,8 +17,8 @@
PASS getAnimations for CSS Animations that are canceled
PASS getAnimations for CSS Animations follows animation-name order
PASS { subtree: false } on a leaf element returns the element's animations and ignore pseudo-elements
-FAIL { subtree: true } on a leaf element returns the element's animations and its pseudo-elements' animations assert_equals: The animation targeting the parent element should be returned first expected (object) null but got (undefined) undefined
+PASS { subtree: true } on a leaf element returns the element's animations and its pseudo-elements' animations
PASS { subtree: false } on an element with a child returns only the element's animations
-FAIL { subtree: true } on an element with a child returns animations from the element, its pseudo-elements, its child and its child pseudo-elements assert_equals: The animation targeting the parent element should be returned first expected (object) null but got (undefined) undefined
+FAIL { subtree: true } on an element with a child returns animations from the element, its pseudo-elements, its child and its child pseudo-elements assert_equals: The animation targeting the ::after pesudo-element should be returned third expected (string) "::after" but got (object) null
FAIL { subtree: true } on an element with many descendants returns animations from all the descendants assert_equals: The animation targeting the child1 element should be returned second expected Element node <div id="child1" style="animation: anim1 100s;"><div id="... but got Element node <div id="grandchild1" style="animation: anim1 100s;"></div>
Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-transitions/Document-getAnimations.tentative-expected.txt (260138 => 260139)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-transitions/Document-getAnimations.tentative-expected.txt 2020-04-15 17:27:51 UTC (rev 260138)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/css/css-transitions/Document-getAnimations.tentative-expected.txt 2020-04-15 17:39:57 UTC (rev 260139)
@@ -1,6 +1,6 @@
PASS getAnimations for non-animated content
PASS getAnimations for CSS Transitions
-FAIL CSS Transitions targetting (pseudo-)elements should have correct order after sorting assert_equals: Transition #1 has null pseudo type expected (object) null but got (undefined) undefined
+PASS CSS Transitions targetting (pseudo-)elements should have correct order after sorting
PASS Transitions are not returned after they have finished
Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/web-animations/idlharness.window-expected.txt (260138 => 260139)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/web-animations/idlharness.window-expected.txt 2020-04-15 17:27:51 UTC (rev 260138)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/web-animations/idlharness.window-expected.txt 2020-04-15 17:39:57 UTC (rev 260139)
@@ -108,7 +108,7 @@
PASS KeyframeEffect interface: existence and properties of interface prototype object's "constructor" property
PASS KeyframeEffect interface: existence and properties of interface prototype object's @@unscopables property
PASS KeyframeEffect interface: attribute target
-FAIL KeyframeEffect interface: attribute pseudoElement assert_true: The prototype object must have a property "pseudoElement" expected true got false
+PASS KeyframeEffect interface: attribute pseudoElement
PASS KeyframeEffect interface: attribute composite
PASS KeyframeEffect interface: operation getKeyframes()
PASS KeyframeEffect interface: operation setKeyframes(object)
@@ -115,7 +115,7 @@
PASS KeyframeEffect must be primary interface of new KeyframeEffect(null, null)
PASS Stringification of new KeyframeEffect(null, null)
PASS KeyframeEffect interface: new KeyframeEffect(null, null) must inherit property "target" with the proper type
-FAIL KeyframeEffect interface: new KeyframeEffect(null, null) must inherit property "pseudoElement" with the proper type assert_inherits: property "pseudoElement" not found in prototype chain
+PASS KeyframeEffect interface: new KeyframeEffect(null, null) must inherit property "pseudoElement" with the proper type
PASS KeyframeEffect interface: new KeyframeEffect(null, null) must inherit property "composite" with the proper type
PASS KeyframeEffect interface: new KeyframeEffect(null, null) must inherit property "getKeyframes()" with the proper type
PASS KeyframeEffect interface: new KeyframeEffect(null, null) must inherit property "setKeyframes(object)" with the proper type
Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/web-animations/interfaces/Animatable/animate-expected.txt (260138 => 260139)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/web-animations/interfaces/Animatable/animate-expected.txt 2020-04-15 17:27:51 UTC (rev 260138)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/web-animations/interfaces/Animatable/animate-expected.txt 2020-04-15 17:39:57 UTC (rev 260139)
@@ -137,8 +137,8 @@
PASS animate() with pseudoElement parameter without content creates an Animation object
PASS animate() with pseudoElement parameter creates an Animation object for ::marker
PASS animate() with pseudoElement parameter creates an Animation object for ::first-line
-FAIL animate() with pseudoElement an Animation object targeting the correct pseudo-element assert_equals: The returned Animation targets the correct selector expected (string) "::before" but got (undefined) undefined
-FAIL animate() with pseudoElement without content creates an Animation object targeting the correct pseudo-element assert_equals: The returned Animation targets the correct selector expected (string) "::before" but got (undefined) undefined
-FAIL animate() with pseudoElement an Animation object targeting the correct pseudo-element for ::marker assert_equals: The returned Animation targets the correct selector expected (string) "::marker" but got (undefined) undefined
-FAIL animate() with pseudoElement an Animation object targeting the correct pseudo-element for ::first-line assert_equals: The returned Animation targets the correct selector expected (string) "::first-line" but got (undefined) undefined
+PASS animate() with pseudoElement an Animation object targeting the correct pseudo-element
+PASS animate() with pseudoElement without content creates an Animation object targeting the correct pseudo-element
+FAIL animate() with pseudoElement an Animation object targeting the correct pseudo-element for ::marker assert_equals: The returned Animation targets the correct selector expected "::marker" but got ""
+FAIL animate() with pseudoElement an Animation object targeting the correct pseudo-element for ::first-line assert_equals: The returned Animation targets the correct selector expected "::first-line" but got ""
Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/web-animations/interfaces/Animation/commitStyles-expected.txt (260138 => 260139)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/web-animations/interfaces/Animation/commitStyles-expected.txt 2020-04-15 17:27:51 UTC (rev 260138)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/web-animations/interfaces/Animation/commitStyles-expected.txt 2020-04-15 17:39:57 UTC (rev 260139)
@@ -11,9 +11,7 @@
FAIL Commit composites on top of the underlying value assert_approx_equals: expected 0.5 +/- 0.0001 but got 0.20000000298023224
PASS Triggers mutation observers when updating style
FAIL Does NOT trigger mutation observers when the change to style is redundant assert_equals: Should have no mutation records expected 0 but got 1
-FAIL Throws if the target element is a pseudo element assert_throws_dom: function "() => {
- animation.commitStyles();
- }" did not throw
+PASS Throws if the target element is a pseudo element
PASS Throws if the target element is not something with a style attribute
PASS Throws if the target effect is display:none
PASS Throws if the target effect's ancestor is display:none
@@ -20,7 +18,5 @@
PASS Treats display:contents as rendered
PASS Treats display:contents in a display:none subtree as not rendered
PASS Throws if the target effect is disconnected
-FAIL Checks the pseudo element condition before the not rendered condition assert_throws_dom: function "() => {
- animation.commitStyles();
- }" threw object "InvalidStateError: The object is in an invalid state." that is not a DOMException NoModificationAllowedError: property "code" is equal to 11, expected 7
+PASS Checks the pseudo element condition before the not rendered condition
Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/web-animations/interfaces/KeyframeEffect/style-change-events-expected.txt (260138 => 260139)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/web-animations/interfaces/KeyframeEffect/style-change-events-expected.txt 2020-04-15 17:27:51 UTC (rev 260138)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/web-animations/interfaces/KeyframeEffect/style-change-events-expected.txt 2020-04-15 17:39:57 UTC (rev 260139)
@@ -1,9 +1,10 @@
-FAIL All property keys are recognized assert_in_array: Test property 'pseudoElement' should be one of the properties on KeyframeEffect value "pseudoElement" not in array ["getTiming", "getComputedTiming", "updateTiming", "target", "iterationComposite", "composite", "getKeyframes", "setKeyframes", "KeyframeEffect constructor", "KeyframeEffect copy constructor"]
+PASS All property keys are recognized
PASS KeyframeEffect.getTiming does NOT trigger a style change event
PASS KeyframeEffect.getComputedTiming does NOT trigger a style change event
PASS KeyframeEffect.updateTiming does NOT trigger a style change event
PASS KeyframeEffect.target does NOT trigger a style change event
+PASS KeyframeEffect.pseudoElement does NOT trigger a style change event
PASS KeyframeEffect.iterationComposite does NOT trigger a style change event
PASS KeyframeEffect.composite does NOT trigger a style change event
PASS KeyframeEffect.getKeyframes does NOT trigger a style change event
Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/web-animations/interfaces/KeyframeEffect/target-expected.txt (260138 => 260139)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/web-animations/interfaces/KeyframeEffect/target-expected.txt 2020-04-15 17:27:51 UTC (rev 260138)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/web-animations/interfaces/KeyframeEffect/target-expected.txt 2020-04-15 17:39:57 UTC (rev 260139)
@@ -4,18 +4,18 @@
PASS Test setting target from a valid target to null
PASS Test setting target from a valid target to another target
PASS Target element can be set to a foreign element
-FAIL Change target from null to an existing pseudoElement setting target first. assert_equals: Value at 50% progress after setting new target expected "50px" but got "10px"
-FAIL Change target from null to an existing pseudoElement setting pseudoElement first. assert_equals: Value at 50% progress after setting new target expected "50px" but got "10px"
+PASS Change target from null to an existing pseudoElement setting target first.
+PASS Change target from null to an existing pseudoElement setting pseudoElement first.
PASS Change target from an existing pseudo-element to the originating element.
-FAIL Change target from an existing to a different existing pseudo-element by setting target. assert_equals: Animation targets specified pseudo-element (pseudo-selector) expected (string) "::before" but got (undefined) undefined
-FAIL Change target from an existing to a different existing pseudo-element by setting pseudoElement. assert_equals: Value of 2nd element (currently targeted) after changing the effect target expected "50px" but got "20px"
-FAIL Change target from a non-existing to a different existing pseudo-element by setting target. assert_equals: Animation targets specified pseudo-element (pseudo-selector) expected (string) "::before" but got (undefined) undefined
-FAIL Change target from a non-existing to a different existing pseudo-element by setting pseudoElement. assert_equals: Value of 2nd element (currently targeted) after changing the effect target expected "50px" but got "20px"
-FAIL Change target from null to a non-existing pseudoElement setting target first. assert_equals: Value at 50% progress after setting new target expected "50px" but got "10px"
-FAIL Change target from null to a non-existing pseudoElement setting pseudoElement first. assert_equals: Value at 50% progress after setting new target expected "50px" but got "10px"
+PASS Change target from an existing to a different existing pseudo-element by setting target.
+PASS Change target from an existing to a different existing pseudo-element by setting pseudoElement.
+PASS Change target from a non-existing to a different existing pseudo-element by setting target.
+PASS Change target from a non-existing to a different existing pseudo-element by setting pseudoElement.
+PASS Change target from null to a non-existing pseudoElement setting target first.
+PASS Change target from null to a non-existing pseudoElement setting pseudoElement first.
PASS Change target from a non-existing pseudo-element to the originating element.
-FAIL Change target from an existing to a different non-existing pseudo-element by setting target. assert_equals: Animation targets specified pseudo-element (pseudo-selector) expected (string) "::before" but got (undefined) undefined
-FAIL Change target from an existing to a different non-existing pseudo-element by setting pseudoElement. assert_equals: Value of 2nd element (currently targeted) after changing the effect target expected "50px" but got "20px"
-FAIL Change target from a non-existing to a different non-existing pseudo-element by setting target. assert_equals: Animation targets specified pseudo-element (pseudo-selector) expected (string) "::before" but got (undefined) undefined
-FAIL Change target from a non-existing to a different non-existing pseudo-element by setting pseudoElement. assert_equals: Value of 2nd element (currently targeted) after changing the effect target expected "50px" but got "20px"
+PASS Change target from an existing to a different non-existing pseudo-element by setting target.
+PASS Change target from an existing to a different non-existing pseudo-element by setting pseudoElement.
+PASS Change target from a non-existing to a different non-existing pseudo-element by setting target.
+PASS Change target from a non-existing to a different non-existing pseudo-element by setting pseudoElement.
Modified: trunk/Source/WebCore/ChangeLog (260138 => 260139)
--- trunk/Source/WebCore/ChangeLog 2020-04-15 17:27:51 UTC (rev 260138)
+++ trunk/Source/WebCore/ChangeLog 2020-04-15 17:39:57 UTC (rev 260139)
@@ -1,3 +1,65 @@
+2020-04-15 Antoine Quint <[email protected]>
+
+ [Web Animations] Add support for `pseudoElement` on `KeyframeEffect` and `KeyframeEffectOptions`
+ https://bugs.webkit.org/show_bug.cgi?id=207290
+ <rdar://problem/59199003>
+
+ Reviewed by Antti Koivisto.
+
+ We add the required IDL bindings such that JS-originated Web Animations can target pseudo-elements, either via the KeyframeEffect.pseudoElement
+ property, or via the KeyframeEffectOptions.pseudoElement property, which is set on the object passed to the KeyframeEffect constrcutor and
+ Element.animate().
+
+ This means that a PseudoElement can be targeted by an animation even if it's not been created through style resolution by virtue of a ::before
+ or ::after selector and a "content" style rule. This means that when either the "target" or "pseudoElement" property of KeyframeEffect is set,
+ we ensure a PseudoElement is created and set on the host element if required. And additionally, we ensure that during style resolution, animations
+ are applied to such pseudo-elements with a new PseudoElement::isTargetedByKeyframeEffectRequiringPseudoElement() method that indicates that a
+ JS-originated KeyframeEffect targets this pseudo-element.
+
+ * animation/KeyframeEffect.cpp:
+ (WebCore::KeyframeEffect::create): Handle the new KeyframeEffectOptions.pseudoElement property in the KeyframeEffect constructor.
+ (WebCore::KeyframeEffect::targetsPseudoElement const): Indicates whether this effect targets a pseudo-element and not a regular
+ element or a null target.
+ (WebCore::KeyframeEffect::targetElementOrPseudoElement const): Use the new targetsPseudoElement() method to determine whether a
+ pseudo-element is targeted. We also remove an assertion that only made sense when m_pseudoId could only be set via a CSS-originated
+ animation and another one when the only possible m_pseudoId values were PseudoId::Before and PseudoId::After.
+ (WebCore::KeyframeEffect::setTarget): Call the new didChangeTargetElementOrPseudoElement() method if the provided value differs
+ from the stored value for m_target.
+ (WebCore::KeyframeEffect::pseudoElement const): Return the matching normalized string with a `::` prefix for m_pseudoId if the target
+ is a pseudo-element. Note that PseudoElement::pseudoElementNameForEvents() will only return a string for "::before" and "::after" since
+ we only know how to animate these pseudo-elements.
+ (WebCore::KeyframeEffect::setPseudoElement): Determine a matching PseudoId, if any, for the provided string, and call the new
+ didChangeTargetElementOrPseudoElement() method if the provided value differs from the stored value for m_pseudoId.
+ (WebCore::KeyframeEffect::didChangeTargetElementOrPseudoElement): New method called when either m_target or m_pseudoId is changed
+ such that we can ensure the required PseudoElement is created if the animation targets a pseudo-element. Then we run the same logic
+ that we used to in KeyframeEffect::setTarget().
+ (WebCore::KeyframeEffect::requiresPseudoElement const): Indicates whether a PseudoElement must remain created for this KeyframeEffect,
+ which is only necessary for JS-originated effects targeting a pseudo-element.
+ * animation/KeyframeEffect.h:
+ * animation/KeyframeEffect.idl:
+ * animation/KeyframeEffectOptions.h:
+ * animation/KeyframeEffectOptions.idl:
+ * animation/KeyframeEffectStack.cpp:
+ (WebCore::KeyframeEffectStack::requiresPseudoElement const): Indicates whether one or more JS-originated keyframe effects in the stack target
+ the PseudoElement owning this stack.
+ * animation/KeyframeEffectStack.h:
+ * animation/WebAnimation.cpp:
+ (WebCore::WebAnimation::commitStyles): Use KeyframeEffect::targetsPseudoElement() to determine whether the animation's effect's target is a
+ pseudo-element, in which case we need to throw a NoModificationAllowedError exception.
+ * dom/PseudoElement.cpp:
+ (WebCore::PseudoElement::rendererIsNeeded): Return true also when one or more JS-originated keyframe effects in the stack target this pseudo-element.
+ (WebCore::PseudoElement::isTargetedByKeyframeEffectRequiringPseudoElement): Return true when one or more JS-originated keyframe effects in the stack
+ target this pseudo-element.
+ * dom/PseudoElement.h:
+ * rendering/updating/RenderTreeUpdaterGeneratedContent.cpp:
+ (WebCore::createContentRenderers): Remove the assertion that the "content" property was set since it's valid for this function to now be called
+ due to JS-originated keyframe effects targeting the given pseudo-element. Instead we add an assertion that there are such keyframe effects in
+ case no "content" property was set.
+ (WebCore::RenderTreeUpdater::GeneratedContent::updatePseudoElement): Only remove pseudo-elements if there are no JS-originated keyframe effects
+ targeting the specified pseudo-element.
+ * style/StyleTreeResolver.cpp:
+ (WebCore::Style::TreeResolver::resolvePseudoStyle): Allow animated style resolution for pseudo-elements targeted by JS-originated keyframe effects.
+
2020-04-15 Carlos Garcia Campos <[email protected]>
[GTK4] Fix use of gtk init functions
Modified: trunk/Source/WebCore/animation/KeyframeEffect.cpp (260138 => 260139)
--- trunk/Source/WebCore/animation/KeyframeEffect.cpp 2020-04-15 17:27:51 UTC (rev 260138)
+++ trunk/Source/WebCore/animation/KeyframeEffect.cpp 2020-04-15 17:39:57 UTC (rev 260139)
@@ -32,6 +32,7 @@
#include "CSSKeyframeRule.h"
#include "CSSPropertyAnimation.h"
#include "CSSPropertyNames.h"
+#include "CSSSelector.h"
#include "CSSStyleDeclaration.h"
#include "CSSTimingFunctionValue.h"
#include "CSSTransition.h"
@@ -486,6 +487,11 @@
timing.duration = duration;
} else {
auto keyframeEffectOptions = WTF::get<KeyframeEffectOptions>(optionsValue);
+
+ auto setPseudoElementResult = keyframeEffect->setPseudoElement(keyframeEffectOptions.pseudoElement);
+ if (setPseudoElementResult.hasException())
+ return setPseudoElementResult.releaseException();
+
timing = {
keyframeEffectOptions.duration,
keyframeEffectOptions.iterations,
@@ -1072,13 +1078,16 @@
updateEffectStackMembership();
}
+bool KeyframeEffect::targetsPseudoElement() const
+{
+ return m_target.get() && m_pseudoId != PseudoId::None;
+}
+
Element* KeyframeEffect::targetElementOrPseudoElement() const
{
- if (m_pseudoId == PseudoId::None)
+ if (!targetsPseudoElement())
return m_target.get();
- ASSERT(m_target.get());
-
if (m_pseudoId == PseudoId::Before)
return m_target->beforePseudoElement();
@@ -1086,24 +1095,76 @@
return m_target->afterPseudoElement();
// We only support targeting ::before and ::after pseudo-elements at the moment.
- ASSERT_NOT_REACHED();
return nullptr;
}
void KeyframeEffect::setTarget(RefPtr<Element>&& newTarget)
{
- auto* previousTarget = targetElementOrPseudoElement();
- if (previousTarget == newTarget.get())
+ if (m_target.get() == newTarget.get())
return;
+ auto* previousTargetElementOrPseudoElement = targetElementOrPseudoElement();
m_target = makeWeakPtr(newTarget.get());
+ didChangeTargetElementOrPseudoElement(previousTargetElementOrPseudoElement);
+}
- // Until we support pseudo-elements via the Web Animations API, changing target means we should reset m_pseudoId.
- // https://bugs.webkit.org/show_bug.cgi?id=207290
- m_pseudoId = PseudoId::None;
+const String KeyframeEffect::pseudoElement() const
+{
+ // https://drafts.csswg.org/web-animations/#dom-keyframeeffect-pseudoelement
+ // The target pseudo-selector. null if this effect has no effect target or if the effect target is an element (i.e. not a pseudo-element).
+ // When the effect target is a pseudo-element, this specifies the pseudo-element selector (e.g. ::before).
+ if (targetsPseudoElement())
+ return PseudoElement::pseudoElementNameForEvents(m_pseudoId);
+ return { };
+}
+
+ExceptionOr<void> KeyframeEffect::setPseudoElement(const String& pseudoElement)
+{
+ // https://drafts.csswg.org/web-animations/#dom-keyframeeffect-pseudoelement
+
+ // On setting, sets the target pseudo-selector of the animation effect to the provided value after applying the following exceptions:
+ //
+ // - 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. Note, that invalid in this context follows
+ // the definition of an invalid selector defined in [SELECTORS-4] such that syntactically invalid pseudo-elements as well as pseudo-elements
+ // for which the user agent has no usable level of support are both deemed invalid.
+ // - 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').
+ auto pseudoId = PseudoId::None;
+ if (!pseudoElement.isNull()) {
+ auto isLegacy = pseudoElement == ":before" || pseudoElement == ":after" || pseudoElement == ":first-letter" || pseudoElement == ":first-line";
+ if (!isLegacy && !pseudoElement.startsWith("::"))
+ return Exception { SyntaxError };
+ auto pseudoType = CSSSelector::parsePseudoElementType(pseudoElement.substring(isLegacy ? 1 : 2));
+ if (pseudoType == CSSSelector::PseudoElementUnknown)
+ return Exception { SyntaxError };
+ pseudoId = CSSSelector::pseudoId(pseudoType);
+ }
+
+ if (pseudoId == m_pseudoId)
+ return { };
+
+ auto* previousTargetElementOrPseudoElement = targetElementOrPseudoElement();
+ m_pseudoId = pseudoId;
+ didChangeTargetElementOrPseudoElement(previousTargetElementOrPseudoElement);
+
+ return { };
+}
+
+void KeyframeEffect::didChangeTargetElementOrPseudoElement(Element* previousTargetElementOrPseudoElement)
+{
+ auto* newTargetElementOrPseudoElement = targetElementOrPseudoElement();
+
+ // We must ensure a PseudoElement exists for this m_target / m_pseudoId pair if both are specified.
+ if (!newTargetElementOrPseudoElement && m_target.get() && m_pseudoId != PseudoId::None) {
+ // We only support targeting ::before and ::after pseudo-elements at the moment.
+ if (m_pseudoId == PseudoId::Before || m_pseudoId == PseudoId::After)
+ newTargetElementOrPseudoElement = &m_target->ensurePseudoElement(m_pseudoId);
+ }
+
if (auto* effectAnimation = animation())
- effectAnimation->effectTargetDidChange(previousTarget, targetElementOrPseudoElement());
+ effectAnimation->effectTargetDidChange(previousTargetElementOrPseudoElement, newTargetElementOrPseudoElement);
clearBlendingKeyframes();
@@ -1113,14 +1174,14 @@
// Likewise, we need to invalidate styles on the previous target so that
// any animated styles are removed immediately.
- invalidateElement(previousTarget);
+ invalidateElement(previousTargetElementOrPseudoElement);
- if (previousTarget) {
- previousTarget->ensureKeyframeEffectStack().removeEffect(*this);
+ if (previousTargetElementOrPseudoElement) {
+ previousTargetElementOrPseudoElement->ensureKeyframeEffectStack().removeEffect(*this);
m_inTargetEffectStack = false;
}
- if (targetElementOrPseudoElement())
- m_inTargetEffectStack = targetElementOrPseudoElement()->ensureKeyframeEffectStack().addEffect(*this);
+ if (newTargetElementOrPseudoElement)
+ m_inTargetEffectStack = newTargetElementOrPseudoElement->ensureKeyframeEffectStack().addEffect(*this);
}
void KeyframeEffect::apply(RenderStyle& targetStyle)
@@ -1698,4 +1759,9 @@
return true;
}
+bool KeyframeEffect::requiresPseudoElement() const
+{
+ return m_blendingKeyframesSource == BlendingKeyframesSource::WebAnimation && targetsPseudoElement();
+}
+
} // namespace WebCore
Modified: trunk/Source/WebCore/animation/KeyframeEffect.h (260138 => 260139)
--- trunk/Source/WebCore/animation/KeyframeEffect.h 2020-04-15 17:27:51 UTC (rev 260138)
+++ trunk/Source/WebCore/animation/KeyframeEffect.h 2020-04-15 17:39:57 UTC (rev 260139)
@@ -105,6 +105,10 @@
Element* targetElementOrPseudoElement() const;
void setTarget(RefPtr<Element>&&);
+ bool targetsPseudoElement() const;
+ const String pseudoElement() const;
+ ExceptionOr<void> setPseudoElement(const String&);
+
Vector<JSC::Strong<JSC::JSObject>> getKeyframes(JSC::JSGlobalObject&);
ExceptionOr<void> setKeyframes(JSC::JSGlobalObject&, JSC::Strong<JSC::JSObject>&&);
@@ -158,6 +162,8 @@
const RenderStyle* unanimatedStyle() const { return m_unanimatedStyle.get(); }
+ bool requiresPseudoElement() const;
+
private:
KeyframeEffect(Element*, PseudoId);
@@ -168,6 +174,7 @@
Document* document() const;
void updateEffectStackMembership();
void copyPropertiesFromSource(Ref<KeyframeEffect>&&);
+ void didChangeTargetElementOrPseudoElement(Element*);
ExceptionOr<void> processKeyframes(JSC::JSGlobalObject&, JSC::Strong<JSC::JSObject>&&);
void addPendingAcceleratedAction(AcceleratedAction);
void updateAcceleratedActions();
Modified: trunk/Source/WebCore/animation/KeyframeEffect.idl (260138 => 260139)
--- trunk/Source/WebCore/animation/KeyframeEffect.idl 2020-04-15 17:27:51 UTC (rev 260138)
+++ trunk/Source/WebCore/animation/KeyframeEffect.idl 2020-04-15 17:39:57 UTC (rev 260139)
@@ -23,6 +23,8 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+typedef USVString CSSOMString;
+
[
EnabledAtRuntime=WebAnimations,
Exposed=Window,
@@ -33,6 +35,7 @@
Constructor(KeyframeEffect source)
] interface KeyframeEffect : AnimationEffect {
attribute Element? target;
+ [MayThrowException] attribute CSSOMString? pseudoElement;
[EnabledAtRuntime=WebAnimationsCompositeOperations] attribute IterationCompositeOperation iterationComposite;
[EnabledAtRuntime=WebAnimationsCompositeOperations] attribute CompositeOperation composite;
[CallWith=GlobalObject] sequence<object> getKeyframes();
Modified: trunk/Source/WebCore/animation/KeyframeEffectOptions.h (260138 => 260139)
--- trunk/Source/WebCore/animation/KeyframeEffectOptions.h 2020-04-15 17:27:51 UTC (rev 260138)
+++ trunk/Source/WebCore/animation/KeyframeEffectOptions.h 2020-04-15 17:39:57 UTC (rev 260139)
@@ -35,6 +35,7 @@
struct KeyframeEffectOptions : EffectTiming {
IterationCompositeOperation iterationComposite { IterationCompositeOperation::Replace };
CompositeOperation composite { CompositeOperation::Replace };
+ String pseudoElement;
};
} // namespace WebCore
Modified: trunk/Source/WebCore/animation/KeyframeEffectOptions.idl (260138 => 260139)
--- trunk/Source/WebCore/animation/KeyframeEffectOptions.idl 2020-04-15 17:27:51 UTC (rev 260138)
+++ trunk/Source/WebCore/animation/KeyframeEffectOptions.idl 2020-04-15 17:39:57 UTC (rev 260139)
@@ -23,7 +23,10 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+typedef USVString CSSOMString;
+
dictionary KeyframeEffectOptions : EffectTiming {
IterationCompositeOperation iterationComposite = "replace";
CompositeOperation composite = "replace";
+ CSSOMString? pseudoElement = null;
};
Modified: trunk/Source/WebCore/animation/KeyframeEffectStack.cpp (260138 => 260139)
--- trunk/Source/WebCore/animation/KeyframeEffectStack.cpp 2020-04-15 17:27:51 UTC (rev 260138)
+++ trunk/Source/WebCore/animation/KeyframeEffectStack.cpp 2020-04-15 17:39:57 UTC (rev 260139)
@@ -59,6 +59,15 @@
m_effects.removeFirst(&effect);
}
+bool KeyframeEffectStack::requiresPseudoElement() const
+{
+ for (auto& effect : m_effects) {
+ if (effect->requiresPseudoElement())
+ return true;
+ }
+ return false;
+}
+
bool KeyframeEffectStack::isCurrentlyAffectingProperty(CSSPropertyID property) const
{
for (auto& effect : m_effects) {
Modified: trunk/Source/WebCore/animation/KeyframeEffectStack.h (260138 => 260139)
--- trunk/Source/WebCore/animation/KeyframeEffectStack.h 2020-04-15 17:27:51 UTC (rev 260138)
+++ trunk/Source/WebCore/animation/KeyframeEffectStack.h 2020-04-15 17:39:57 UTC (rev 260139)
@@ -47,6 +47,7 @@
const AnimationList* cssAnimationList() const { return m_cssAnimationList.get(); }
void setCSSAnimationList(RefPtr<const AnimationList>&&);
bool isCurrentlyAffectingProperty(CSSPropertyID) const;
+ bool requiresPseudoElement() const;
private:
void ensureEffectsAreSorted();
Modified: trunk/Source/WebCore/animation/WebAnimation.cpp (260138 => 260139)
--- trunk/Source/WebCore/animation/WebAnimation.cpp 2020-04-15 17:27:51 UTC (rev 260138)
+++ trunk/Source/WebCore/animation/WebAnimation.cpp 2020-04-15 17:39:57 UTC (rev 260139)
@@ -1367,7 +1367,7 @@
//
// 2.1 If target is not an element capable of having a style attribute (for example, it is a pseudo-element or is an element in a
// document format for which style attributes are not defined) throw a "NoModificationAllowedError" DOMException and abort these steps.
- if (!is<StyledElement>(target))
+ if (!is<StyledElement>(target) || effect->targetsPseudoElement())
return Exception { NoModificationAllowedError };
auto& styledElement = downcast<StyledElement>(*target);
Modified: trunk/Source/WebCore/dom/PseudoElement.cpp (260138 => 260139)
--- trunk/Source/WebCore/dom/PseudoElement.cpp 2020-04-15 17:27:51 UTC (rev 260138)
+++ trunk/Source/WebCore/dom/PseudoElement.cpp 2020-04-15 17:39:57 UTC (rev 260139)
@@ -32,6 +32,7 @@
#include "ContentData.h"
#include "DocumentTimeline.h"
#include "InspectorInstrumentation.h"
+#include "KeyframeEffectStack.h"
#include "RenderElement.h"
#include "RenderImage.h"
#include "RenderQuote.h"
@@ -100,7 +101,14 @@
bool PseudoElement::rendererIsNeeded(const RenderStyle& style)
{
- return pseudoElementRendererIsNeeded(&style);
+ return pseudoElementRendererIsNeeded(&style) || isTargetedByKeyframeEffectRequiringPseudoElement();
}
+bool PseudoElement::isTargetedByKeyframeEffectRequiringPseudoElement()
+{
+ if (auto* stack = keyframeEffectStack())
+ return stack->requiresPseudoElement();
+ return false;
+}
+
} // namespace
Modified: trunk/Source/WebCore/dom/PseudoElement.h (260138 => 260139)
--- trunk/Source/WebCore/dom/PseudoElement.h 2020-04-15 17:27:51 UTC (rev 260138)
+++ trunk/Source/WebCore/dom/PseudoElement.h 2020-04-15 17:39:57 UTC (rev 260139)
@@ -41,6 +41,7 @@
void clearHostElement();
bool rendererIsNeeded(const RenderStyle&) override;
+ bool isTargetedByKeyframeEffectRequiringPseudoElement();
bool canStartSelection() const override { return false; }
bool canContainRangeEndPoint() const override { return false; }
Modified: trunk/Source/WebCore/rendering/updating/RenderTreeUpdaterGeneratedContent.cpp (260138 => 260139)
--- trunk/Source/WebCore/rendering/updating/RenderTreeUpdaterGeneratedContent.cpp 2020-04-15 17:27:51 UTC (rev 260138)
+++ trunk/Source/WebCore/rendering/updating/RenderTreeUpdaterGeneratedContent.cpp 2020-04-15 17:39:57 UTC (rev 260139)
@@ -71,12 +71,16 @@
static void createContentRenderers(RenderTreeBuilder& builder, RenderElement& pseudoRenderer, const RenderStyle& style)
{
- ASSERT(style.contentData());
-
- for (const ContentData* content = style.contentData(); content; content = content->next()) {
- auto child = content->createContentRenderer(pseudoRenderer.document(), style);
- if (pseudoRenderer.isChildAllowed(*child, style))
- builder.attach(pseudoRenderer, WTFMove(child));
+ if (auto* contentData = style.contentData()) {
+ for (const ContentData* content = contentData; content; content = content->next()) {
+ auto child = content->createContentRenderer(pseudoRenderer.document(), style);
+ if (pseudoRenderer.isChildAllowed(*child, style))
+ builder.attach(pseudoRenderer, WTFMove(child));
+ }
+ } else {
+ // The only valid scenario where this method is called without the "content" property being set
+ // is the case where a pseudo-element has animations set on it via the Web Animations API.
+ ASSERT(is<PseudoElement>(pseudoRenderer.element()) && downcast<PseudoElement>(*pseudoRenderer.element()).isTargetedByKeyframeEffectRequiringPseudoElement());
}
}
@@ -97,7 +101,7 @@
if (auto* renderer = pseudoElement ? pseudoElement->renderer() : nullptr)
m_updater.renderTreePosition().invalidateNextSibling(*renderer);
- if (!needsPseudoElement(update)) {
+ if (!needsPseudoElement(update) && (!pseudoElement || !pseudoElement->isTargetedByKeyframeEffectRequiringPseudoElement())) {
if (pseudoElement) {
if (pseudoId == PseudoId::Before)
removeBeforePseudoElement(current, m_updater.m_builder);
Modified: trunk/Source/WebCore/style/StyleTreeResolver.cpp (260138 => 260139)
--- trunk/Source/WebCore/style/StyleTreeResolver.cpp 2020-04-15 17:27:51 UTC (rev 260138)
+++ trunk/Source/WebCore/style/StyleTreeResolver.cpp 2020-04-15 17:39:57 UTC (rev 260139)
@@ -265,9 +265,14 @@
return { };
auto pseudoStyle = scope().resolver.pseudoStyleForElement(element, { pseudoId }, *elementUpdate.style, parentBoxStyleForPseudo(elementUpdate), &scope().selectorFilter);
- if (!pseudoElementRendererIsNeeded(pseudoStyle.get()))
+ if (!pseudoStyle)
return { };
+ auto* pseudoElement = pseudoId == PseudoId::Before ? element.beforePseudoElement() : element.afterPseudoElement();
+ bool hasAnimations = pseudoElement && pseudoElement->isTargetedByKeyframeEffectRequiringPseudoElement();
+ if (!pseudoElementRendererIsNeeded(pseudoStyle.get()) && !hasAnimations)
+ return { };
+
return createAnimatedElementUpdate(WTFMove(pseudoStyle), element.ensurePseudoElement(pseudoId), elementUpdate.change);
}