Bug 1317209 - Part 5: Trigger composeStyle if there is an animation. r=heycam
1. Add one new FFI, Gecko_GetAnimationRule, which will try
to update the animation rule and retrieve a ServoAnimationRule
from EffectSet.
2. Add GetServoAnimationRule, which calls Animation::ComposeStyle and updates
mElementsToRestyle.
3. There is no eRestyle_{CSSAnimations|CSSTransitions}, so we use
eRestyle_Self and eRestyle_Subtree for stylo.
MozReview-Commit-ID: 9RpJurPSFMk
--- a/dom/animation/EffectCompositor.cpp
+++ b/dom/animation/EffectCompositor.cpp
@@ -268,16 +268,18 @@ EffectCompositor::RequestRestyle(dom::El
bool hasPendingRestyle = elementsToRestyle.Get(key);
if (!hasPendingRestyle) {
PostRestyleForAnimation(aElement, aPseudoType, aCascadeLevel);
}
elementsToRestyle.Put(key, true);
}
if (aRestyleType == RestyleType::Layer) {
+ // FIXME: we call RequestRestyle for both stylo and gecko, so we
+ // should fix this after supporting animations on the compositor.
// Prompt layers to re-sync their animations.
if (mPresContext->RestyleManager()->IsServo()) {
NS_ERROR("stylo: Servo-backed style system should not be using "
"EffectCompositor");
return;
}
mPresContext->RestyleManager()->AsGecko()->IncrementAnimationGeneration();
EffectSet* effectSet =
@@ -300,16 +302,22 @@ EffectCompositor::PostRestyleForAnimatio
dom::Element* element = GetElementToRestyle(aElement, aPseudoType);
if (!element) {
return;
}
nsRestyleHint hint = aCascadeLevel == CascadeLevel::Transitions ?
eRestyle_CSSTransitions :
eRestyle_CSSAnimations;
+
+ // FIXME: stylo only supports Self and Subtree hints now, so we override it
+ // for stylo.
+ if (mPresContext->StyleSet()->IsServo()) {
+ hint = eRestyle_Self | eRestyle_Subtree;
+ }
mPresContext->PresShell()->RestyleForAnimation(element, hint);
}
void
EffectCompositor::PostRestyleForThrottledAnimations()
{
for (size_t i = 0; i < kCascadeLevelCount; i++) {
CascadeLevel cascadeLevel = CascadeLevel(i);
@@ -418,16 +426,91 @@ EffectCompositor::GetAnimationRule(dom::
EffectSet* effectSet = EffectSet::GetEffectSet(aElement, aPseudoType);
if (!effectSet) {
return nullptr;
}
return effectSet->AnimationRule(aCascadeLevel).mGecko;
}
+namespace {
+ class EffectCompositeOrderComparator {
+ public:
+ bool Equals(const KeyframeEffectReadOnly* a,
+ const KeyframeEffectReadOnly* b) const
+ {
+ return a == b;
+ }
+
+ bool LessThan(const KeyframeEffectReadOnly* a,
+ const KeyframeEffectReadOnly* b) const
+ {
+ MOZ_ASSERT(a->GetAnimation() && b->GetAnimation());
+ MOZ_ASSERT(
+ Equals(a, b) ||
+ a->GetAnimation()->HasLowerCompositeOrderThan(*b->GetAnimation()) !=
+ b->GetAnimation()->HasLowerCompositeOrderThan(*a->GetAnimation()));
+ return a->GetAnimation()->HasLowerCompositeOrderThan(*b->GetAnimation());
+ }
+ };
+}
+
+ServoAnimationRule*
+EffectCompositor::GetServoAnimationRule(const dom::Element* aElement,
+ CSSPseudoElementType aPseudoType,
+ CascadeLevel aCascadeLevel)
+{
+ if (!mPresContext || !mPresContext->IsDynamic()) {
+ // For print or print preview, ignore animations.
+ return nullptr;
+ }
+
+ EffectSet* effectSet = EffectSet::GetEffectSet(aElement, aPseudoType);
+ if (!effectSet) {
+ return nullptr;
+ }
+
+ // Get a list of effects sorted by composite order.
+ nsTArray<KeyframeEffectReadOnly*> sortedEffectList(effectSet->Count());
+ for (KeyframeEffectReadOnly* effect : *effectSet) {
+ sortedEffectList.AppendElement(effect);
+ }
+ sortedEffectList.Sort(EffectCompositeOrderComparator());
+
+ AnimationRule& animRule = effectSet->AnimationRule(aCascadeLevel);
+ animRule.mServo = nullptr;
+
+ // If multiple animations affect the same property, animations with higher
+ // composite order (priority) override or add or animations with lower
+ // priority.
+ // stylo: we don't support animations on compositor now, so propertiesToSkip
+ // is an empty set.
+ const nsCSSPropertyIDSet propertiesToSkip;
+ for (KeyframeEffectReadOnly* effect : sortedEffectList) {
+ effect->GetAnimation()->ComposeStyle(animRule, propertiesToSkip);
+ }
+
+ MOZ_ASSERT(effectSet == EffectSet::GetEffectSet(aElement, aPseudoType),
+ "EffectSet should not change while composing style");
+
+ effectSet->UpdateAnimationRuleRefreshTime(
+ aCascadeLevel, mPresContext->RefreshDriver()->MostRecentRefresh());
+ return animRule.mServo;
+}
+
+void
+EffectCompositor::ClearElementsToRestyle()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ const auto iterEnd = mElementsToRestyle.end();
+ for (auto iter = mElementsToRestyle.begin(); iter != iterEnd; ++iter) {
+ iter->Clear();
+ }
+}
+
/* static */ dom::Element*
EffectCompositor::GetElementToRestyle(dom::Element* aElement,
CSSPseudoElementType aPseudoType)
{
if (aPseudoType == CSSPseudoElementType::NotPseudo) {
return aElement;
}
@@ -586,38 +669,16 @@ EffectCompositor::MaybeUpdateCascadeResu
}
}
}
UpdateCascadeResults(*effects, aElement, aPseudoType, styleContext);
MOZ_ASSERT(!effects->CascadeNeedsUpdate(), "Failed to update cascade state");
}
-namespace {
- class EffectCompositeOrderComparator {
- public:
- bool Equals(const KeyframeEffectReadOnly* a,
- const KeyframeEffectReadOnly* b) const
- {
- return a == b;
- }
-
- bool LessThan(const KeyframeEffectReadOnly* a,
- const KeyframeEffectReadOnly* b) const
- {
- MOZ_ASSERT(a->GetAnimation() && b->GetAnimation());
- MOZ_ASSERT(
- Equals(a, b) ||
- a->GetAnimation()->HasLowerCompositeOrderThan(*b->GetAnimation()) !=
- b->GetAnimation()->HasLowerCompositeOrderThan(*a->GetAnimation()));
- return a->GetAnimation()->HasLowerCompositeOrderThan(*b->GetAnimation());
- }
- };
-}
-
/* static */ void
EffectCompositor::UpdateCascadeResults(Element* aElement,
CSSPseudoElementType aPseudoType,
nsStyleContext* aStyleContext)
{
EffectSet* effects = EffectSet::GetEffectSet(aElement, aPseudoType);
if (!effects) {
return;
--- a/dom/animation/EffectCompositor.h
+++ b/dom/animation/EffectCompositor.h
@@ -24,16 +24,17 @@ class nsIStyleRule;
class nsPresContext;
class nsStyleContext;
namespace mozilla {
class EffectSet;
class RestyleTracker;
class StyleAnimationValue;
+class ServoAnimationRule;
struct AnimationPerformanceWarning;
struct AnimationProperty;
struct NonOwningAnimationTarget;
namespace dom {
class Animation;
class Element;
}
@@ -145,16 +146,31 @@ public:
// When we are not resolving style context, |aStyleContext| can be nullptr, we
// will use a style context associated with the primary frame of the specified
// (pseudo-)element.
nsIStyleRule* GetAnimationRule(dom::Element* aElement,
CSSPseudoElementType aPseudoType,
CascadeLevel aCascadeLevel,
nsStyleContext* aStyleContext);
+ // Get animation rule for stylo. This is an equivalent of GetAnimationRule
+ // and will be called from servo side. We need to be careful while doing any
+ // modification because it may case some thread-safe issues.
+ ServoAnimationRule* GetServoAnimationRule(const dom::Element* aElement,
+ CSSPseudoElementType aPseudoType,
+ CascadeLevel aCascadeLevel);
+
+ // Clear mElementsToRestyle hashtable. Unlike GetAnimationRule,
+ // in GetServoAnimationRule, we don't remove the entry of the composed
+ // animation, so we can prevent the thread-safe issues of dom::Element.
+ // Therefore, we need to call Clear mElementsToRestyle until we go back to
+ // Gecko side.
+ // FIXME: we shouldn't clear the animations on the compositor.
+ void ClearElementsToRestyle();
+
bool HasPendingStyleUpdates() const;
bool HasThrottledStyleUpdates() const;
// Tell the restyle tracker about all the animated styles that have
// pending updates so that it can update the animation rule for these
// elements.
void AddStyleUpdatesTo(RestyleTracker& aTracker);
--- a/layout/base/ServoRestyleManager.cpp
+++ b/layout/base/ServoRestyleManager.cpp
@@ -311,16 +311,17 @@ ServoRestyleManager::ProcessPendingResty
ServoStyleSet* styleSet = StyleSet();
nsIDocument* doc = PresContext()->Document();
// XXXbholley: Should this be while() per bug 1316247?
if (HasPendingRestyles()) {
mInStyleRefresh = true;
styleSet->StyleDocument();
+ PresContext()->EffectCompositor()->ClearElementsToRestyle();
// First do any queued-up frame creation. (see bugs 827239 and 997506).
//
// XXXEmilio I'm calling this to avoid random behavior changes, since we
// delay frame construction after styling we should re-check once our
// model is more stable whether we can skip this call.
//
// Note this has to be *after* restyling, because otherwise frame
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -6,16 +6,17 @@
#include "mozilla/ServoBindings.h"
#include "ChildIterator.h"
#include "gfxFontFamilyList.h"
#include "nsAttrValueInlines.h"
#include "nsCSSProps.h"
#include "nsCSSParser.h"
+#include "nsCSSPseudoElements.h"
#include "nsCSSRuleProcessor.h"
#include "nsContentUtils.h"
#include "nsDOMTokenList.h"
#include "nsIContentInlines.h"
#include "nsIDOMNode.h"
#include "nsIDocument.h"
#include "nsIFrame.h"
#include "nsINode.h"
@@ -24,17 +25,19 @@
#include "nsNameSpaceManager.h"
#include "nsNetUtil.h"
#include "nsRuleNode.h"
#include "nsString.h"
#include "nsStyleStruct.h"
#include "nsStyleUtil.h"
#include "nsTArray.h"
+#include "mozilla/EffectCompositor.h"
#include "mozilla/EventStates.h"
+#include "mozilla/ServoAnimationRule.h"
#include "mozilla/ServoElementSnapshot.h"
#include "mozilla/ServoRestyleManager.h"
#include "mozilla/StyleAnimationValue.h"
#include "mozilla/DeclarationBlockInlines.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/ElementInlines.h"
using namespace mozilla;
@@ -319,16 +322,56 @@ Gecko_GetServoDeclarationBlock(RawGeckoE
// document into a Servo-style-backend document. See bug 1330051.
NS_WARNING("stylo: requesting a Gecko declaration block?");
return nullptr;
}
return reinterpret_cast<const RawServoDeclarationBlockStrong*>
(decl->AsServo()->RefRaw());
}
+RawServoDeclarationBlockStrong
+Gecko_GetAnimationRule(RawGeckoElementBorrowed aElement,
+ nsIAtom* aPseudoTag)
+{
+ MOZ_ASSERT(aElement, "Invalid GeckoElement");
+
+ const RawServoDeclarationBlockStrong emptyDeclarationBlock{ nullptr };
+ nsIDocument* doc = aElement->GetComposedDoc();
+ if (!doc || !doc->GetShell()) {
+ return emptyDeclarationBlock;
+ }
+ nsPresContext* presContext = doc->GetShell()->GetPresContext();
+ if (!presContext) {
+ return emptyDeclarationBlock;
+ }
+
+ // FIXME: support different cascading levels in the later patch
+ CSSPseudoElementType pseudoType =
+ aPseudoTag
+ ? nsCSSPseudoElements::GetPseudoType(
+ aPseudoTag,
+ nsCSSProps::EnabledState::eIgnoreEnabledState)
+ : CSSPseudoElementType::NotPseudo;
+ if (pseudoType != CSSPseudoElementType::NotPseudo &&
+ pseudoType != CSSPseudoElementType::before &&
+ pseudoType != CSSPseudoElementType::after) {
+ return emptyDeclarationBlock;
+ }
+
+ ServoAnimationRule* rule =
+ presContext->EffectCompositor()->GetServoAnimationRule(
+ aElement,
+ pseudoType,
+ EffectCompositor::CascadeLevel::Animations);
+ if (!rule) {
+ return emptyDeclarationBlock;
+ }
+ return rule->GetValues();
+}
+
void
Gecko_FillAllBackgroundLists(nsStyleImageLayers* aLayers, uint32_t aMaxLen)
{
nsRuleNode::FillAllBackgroundLists(*aLayers, aMaxLen);
}
void
Gecko_FillAllMaskLists(nsStyleImageLayers* aLayers, uint32_t aMaxLen)
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -153,16 +153,21 @@ SERVO_DECLARE_ELEMENT_ATTR_MATCHING_FUNC
const ServoElementSnapshot*)
#undef SERVO_DECLARE_ELEMENT_ATTR_MATCHING_FUNCTIONS
// Style attributes.
RawServoDeclarationBlockStrongBorrowedOrNull
Gecko_GetServoDeclarationBlock(RawGeckoElementBorrowed element);
+// Animations
+RawServoDeclarationBlockStrong
+Gecko_GetAnimationRule(RawGeckoElementBorrowed aElement,
+ nsIAtom* aPseudoTag);
+
// Atoms.
nsIAtom* Gecko_Atomize(const char* aString, uint32_t aLength);
void Gecko_AddRefAtom(nsIAtom* aAtom);
void Gecko_ReleaseAtom(nsIAtom* aAtom);
const uint16_t* Gecko_GetAtomAsUTF16(nsIAtom* aAtom, uint32_t* aLength);
bool Gecko_AtomEqualsUTF8(nsIAtom* aAtom, const char* aString, uint32_t aLength);
bool Gecko_AtomEqualsUTF8IgnoreCase(nsIAtom* aAtom, const char* aString, uint32_t aLength);