Skip to content
Permalink
Browse files
Invalidate animation keyframes using container units on when containe…
…r size changes

https://bugs.webkit.org/show_bug.cgi?id=241546

Reviewed by Antoine Quint.

Container size change also changes the interpretation of container units used in keyframes.

* LayoutTests/imported/w3c/web-platform-tests/css/css-contain/container-queries/container-units-animation-expected.txt:
* Source/WebCore/dom/Element.cpp:
(WebCore::Element::invalidateForQueryContainerSizeChange):
(WebCore::Element::needsUpdateQueryContainerDependentStyle const):
(WebCore::Element::clearNeedsUpdateQueryContainerDependentStyle):
(WebCore::Element::invalidateForQueryContainerChange): Deleted.

Add a new bit that tells when a container has been resized.

* Source/WebCore/dom/Element.h:
* Source/WebCore/dom/Node.h:
* Source/WebCore/rendering/style/KeyframeList.cpp:
(WebCore::KeyframeList::usesContainerUnits const):

Check for container unit use.

* Source/WebCore/rendering/style/KeyframeList.h:
* Source/WebCore/style/StyleScope.cpp:
(WebCore::Style::Scope::updateQueryContainerState):
* Source/WebCore/style/StyleTreeResolver.cpp:
(WebCore::Style::TreeResolver::createAnimatedElementUpdate):

Invalidate the keyframes if needed when computing the style.

(WebCore::Style::TreeResolver::pushParent):

Track if the subtree is withing a resized container.

* Source/WebCore/style/StyleTreeResolver.h:
* Source/WebCore/style/Styleable.cpp:
(WebCore::Styleable::queryContainerDidChange const):
* Source/WebCore/style/Styleable.h:

Canonical link: https://commits.webkit.org/251574@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@295569 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
anttijk committed Jun 15, 2022
1 parent f0ea7d0 commit dd58707ce90879e1a05102aacf52e9d2ab45f64c
Show file tree
Hide file tree
Showing 11 changed files with 65 additions and 13 deletions.
@@ -1,14 +1,14 @@

PASS Animation using cqw unit
FAIL Animation using cqw unit responds to changing container size assert_equals: expected "90px" but got "60px"
PASS Animation using cqw unit responds to changing container size
PASS Animation using cqh unit
FAIL Animation using cqh unit responds to changing container size assert_equals: expected "90px" but got "60px"
PASS Animation using cqh unit responds to changing container size
PASS Animation using cqi unit
FAIL Animation using cqi unit responds to changing container size assert_equals: expected "90px" but got "60px"
PASS Animation using cqi unit responds to changing container size
PASS Animation using cqb unit
FAIL Animation using cqb unit responds to changing container size assert_equals: expected "90px" but got "60px"
PASS Animation using cqb unit responds to changing container size
PASS Animation using cqmin unit
FAIL Animation using cqmin unit responds to changing container size assert_equals: expected "90px" but got "60px"
PASS Animation using cqmin unit responds to changing container size
PASS Animation using cqmax unit
FAIL Animation using cqmax unit responds to changing container size assert_equals: expected "90px" but got "60px"
PASS Animation using cqmax unit responds to changing container size

@@ -2265,10 +2265,21 @@ void Element::invalidateStyleForSubtreeInternal()
Node::invalidateStyle(Style::Validity::SubtreeInvalid);
}

void Element::invalidateForQueryContainerChange()
void Element::invalidateForQueryContainerSizeChange()
{
// FIXME: Ideally we would just recompute things that are actually affected by containers queries within the subtree.
Node::invalidateStyle(Style::Validity::SubtreeInvalid);
setNodeFlag(NodeFlag::NeedsUpdateQueryContainerDependentStyle);
}

bool Element::needsUpdateQueryContainerDependentStyle() const
{
return hasNodeFlag(NodeFlag::NeedsUpdateQueryContainerDependentStyle);
}

void Element::clearNeedsUpdateQueryContainerDependentStyle()
{
clearNodeFlag(NodeFlag::NeedsUpdateQueryContainerDependentStyle);
}

void Element::invalidateEventListenerRegions()
@@ -639,7 +639,10 @@ class Element : public ContainerNode {

void invalidateStyleInternal();
void invalidateStyleForSubtreeInternal();
void invalidateForQueryContainerChange();
void invalidateForQueryContainerSizeChange();

bool needsUpdateQueryContainerDependentStyle() const;
void clearNeedsUpdateQueryContainerDependentStyle();

void invalidateEventListenerRegions();

@@ -582,9 +582,10 @@ class Node : public EventTarget {
IsComputedStyleInvalidFlag = 1 << 25,
HasShadowRootContainingSlots = 1 << 26,
IsInTopLayer = 1 << 27,
NeedsSVGRendererUpdate = 1 << 28
NeedsSVGRendererUpdate = 1 << 28,
NeedsUpdateQueryContainerDependentStyle = 1 << 29,

// Bits 29-31 are free.
// Bits 30-31 are free.
};

enum class TabIndexState : uint8_t {
@@ -217,4 +217,14 @@ bool KeyframeList::containsAnimatableProperty() const
return false;
}

bool KeyframeList::usesContainerUnits() const
{
for (auto& keyframe : m_keyframes) {
if (keyframe.style()->usesContainerUnits())
return true;
}
return false;
}


} // namespace WebCore
@@ -105,6 +105,8 @@ class KeyframeList {
auto begin() const { return m_keyframes.begin(); }
auto end() const { return m_keyframes.end(); }

bool usesContainerUnits() const;

private:
AtomString m_animationName;
Vector<KeyframeValue> m_keyframes; // Kept sorted by key.
@@ -829,7 +829,7 @@ bool Scope::updateQueryContainerState(QueryContainerUpdateContext& context)
}

for (auto* toInvalidate : containersToInvalidate)
toInvalidate->invalidateForQueryContainerChange();
toInvalidate->invalidateForQueryContainerSizeChange();

return !containersToInvalidate.isEmpty();
}
@@ -577,7 +577,9 @@ ElementUpdate TreeResolver::createAnimatedElementUpdate(std::unique_ptr<RenderSt
auto& document = element.document();
auto* oldStyle = element.renderOrDisplayContentsStyle(styleable.pseudoId);

OptionSet<AnimationImpact> animationImpact;
// FIXME: Something like this is also needed for viewport units.
if (oldStyle && parent().needsUpdateQueryContainerDependentStyle)
styleable.queryContainerDidChange();

// First, we need to make sure that any new CSS animation occuring on this element has a matching WebAnimation
// on the document timeline.
@@ -592,6 +594,8 @@ ElementUpdate TreeResolver::createAnimatedElementUpdate(std::unique_ptr<RenderSt
styleable.updateCSSAnimations(oldStyle, *newStyle, resolutionContext);
}

OptionSet<AnimationImpact> animationImpact;

// Now we can update all Web animations, which will include CSS Animations as well
// as animations created via the JS API.
if (styleable.hasKeyframeEffects()) {
@@ -640,6 +644,9 @@ void TreeResolver::pushParent(Element& element, const RenderStyle& style, Change
parent.didPushScope = true;
}

parent.needsUpdateQueryContainerDependentStyle = m_parentStack.last().needsUpdateQueryContainerDependentStyle || element.needsUpdateQueryContainerDependentStyle();
element.clearNeedsUpdateQueryContainerDependentStyle();

m_parentStack.append(WTFMove(parent));
}

@@ -69,7 +69,7 @@ class TreeResolver {
enum class DescendantsToResolve : uint8_t { None, ChildrenWithExplicitInherit, Children, All };
std::pair<ElementUpdate, DescendantsToResolve> resolveElement(Element&, ResolutionType);

static ElementUpdate createAnimatedElementUpdate(std::unique_ptr<RenderStyle>, const Styleable&, Change, const ResolutionContext&);
ElementUpdate createAnimatedElementUpdate(std::unique_ptr<RenderStyle>, const Styleable&, Change, const ResolutionContext&);
std::optional<ElementUpdate> resolvePseudoElement(Element&, PseudoId, const ElementUpdate&);
std::optional<ElementUpdate> resolveAncestorPseudoElement(Element&, PseudoId, const ElementUpdate&);
std::unique_ptr<RenderStyle> resolveAncestorFirstLinePseudoElement(Element&, const ElementUpdate&);
@@ -95,6 +95,7 @@ class TreeResolver {
DescendantsToResolve descendantsToResolve { DescendantsToResolve::None };
bool didPushScope { false };
bool resolvedFirstLineAndLetterChild { false };
bool needsUpdateQueryContainerDependentStyle { false };

Parent(Document&);
Parent(Element&, const RenderStyle&, Change, DescendantsToResolve);
@@ -625,4 +625,19 @@ void Styleable::updateCSSTransitions(const RenderStyle& currentStyle, const Rend
updateCSSTransitionsForStyleableAndProperty(*this, property, currentStyle, newStyle, generationTime);
}

void Styleable::queryContainerDidChange() const
{
auto* animations = this->animations();
if (!animations)
return;
for (auto animation : *animations) {
auto* cssAnimation = dynamicDowncast<CSSAnimation>(animation.get());
if (!cssAnimation)
continue;
auto* keyframeEffect = dynamicDowncast<KeyframeEffect>(cssAnimation->effect());
if (keyframeEffect && keyframeEffect->blendingKeyframes().usesContainerUnits())
cssAnimation->keyframesRuleDidChange();
}
}

} // namespace WebCore
@@ -161,6 +161,8 @@ struct Styleable {
element.keyframesRuleDidChange(pseudoId);
}

void queryContainerDidChange() const;

bool animationListContainsNewlyValidAnimation(const AnimationList&) const;

void elementWasRemoved() const;

0 comments on commit dd58707

Please sign in to comment.