Bug 1435214: Optimize @keyframes rule insertions. r=xidorn,hiro

Also add some missing test for the tag name invalidations (bug 1407522) and an
empty stylesheet just for sanity.

MozReview-Commit-ID: AHwhZynLBv
This commit is contained in:
Emilio Cobos Álvarez 2018-02-02 12:15:59 +01:00
parent 73cea96890
commit beaed0f565
6 changed files with 48 additions and 11 deletions

View File

@ -245,6 +245,7 @@ public:
mozilla::EffectCompositor* EffectCompositor() { return mEffectCompositor; }
nsTransitionManager* TransitionManager() { return mTransitionManager; }
nsAnimationManager* AnimationManager() { return mAnimationManager; }
const nsAnimationManager* AnimationManager() const { return mAnimationManager; }
nsRefreshDriver* RefreshDriver() { return mRefreshDriver; }

View File

@ -367,6 +367,14 @@ Gecko_NoteAnimationOnlyDirtyElement(RawGeckoElementBorrowed aElement)
const_cast<Element*>(aElement)->NoteAnimationOnlyDirtyForServo();
}
bool Gecko_AnimationNameMayBeReferencedFromStyle(
RawGeckoPresContextBorrowed aPresContext,
nsAtom* aName)
{
MOZ_ASSERT(aPresContext);
return aPresContext->AnimationManager()->AnimationMayBeReferenced(aName);
}
CSSPseudoElementType
Gecko_GetImplementedPseudo(RawGeckoElementBorrowed aElement)
{

View File

@ -395,6 +395,10 @@ void Gecko_NoteDirtyElement(RawGeckoElementBorrowed element);
void Gecko_NoteDirtySubtreeForInvalidation(RawGeckoElementBorrowed element);
void Gecko_NoteAnimationOnlyDirtyElement(RawGeckoElementBorrowed element);
bool Gecko_AnimationNameMayBeReferencedFromStyle(
RawGeckoPresContextBorrowed pres_context,
nsAtom* name);
// Incremental restyle.
mozilla::CSSPseudoElementType Gecko_GetImplementedPseudo(RawGeckoElementBorrowed element);
// We'd like to return `nsChangeHint` here, but bindgen bitfield enums don't

View File

@ -1008,20 +1008,23 @@ BuildAnimations(nsPresContext* aPresContext,
const NonOwningAnimationTarget& aTarget,
const nsStyleDisplay& aStyleDisplay,
BuilderType& aBuilder,
nsAnimationManager::CSSAnimationCollection* aCollection)
nsAnimationManager::CSSAnimationCollection* aCollection,
nsTHashtable<nsRefPtrHashKey<nsAtom>>& aReferencedAnimations)
{
nsAnimationManager::OwningCSSAnimationPtrArray result;
for (size_t animIdx = aStyleDisplay.mAnimationNameCount; animIdx-- != 0;) {
nsAtom* name = aStyleDisplay.GetAnimationName(animIdx);
// CSS Animations whose animation-name does not match a @keyframes rule do
// not generate animation events. This includes when the animation-name is
// "none" which is represented by an empty name in the StyleAnimation.
// Since such animations neither affect style nor dispatch events, we do
// not generate a corresponding CSSAnimation for them.
if (aStyleDisplay.GetAnimationName(animIdx) == nsGkAtoms::_empty) {
if (name == nsGkAtoms::_empty) {
continue;
}
aReferencedAnimations.PutEntry(name);
RefPtr<CSSAnimation> dest = BuildAnimation(aPresContext,
aTarget,
aStyleDisplay,
@ -1125,7 +1128,8 @@ nsAnimationManager::DoUpdateAnimations(
aTarget,
aStyleDisplay,
aBuilder,
collection);
collection,
mMaybeReferencedAnimations);
if (newAnimations.IsEmpty()) {
if (collection) {

View File

@ -345,10 +345,23 @@ public:
return false;
}
bool AnimationMayBeReferenced(nsAtom* aName) const
{
return mMaybeReferencedAnimations.Contains(aName);
}
protected:
~nsAnimationManager() override = default;
private:
// This includes all animation names referenced regardless of whether a
// corresponding `@keyframes` rule is available.
//
// It may contain names which are no longer referenced, but it should always
// contain names which are currently referenced, so that it is usable for
// style invalidation.
nsTHashtable<nsRefPtrHashKey<nsAtom>> mMaybeReferencedAnimations;
template<class BuilderType>
void DoUpdateAnimations(
const mozilla::NonOwningAnimationTarget& aTarget,

View File

@ -30,10 +30,14 @@ const TESTS = [
{ selector: "#idScope", restyle: true },
{ selector: "#nonexistentIdScope", restyle: false },
{ selector: "#nonexistentIdScope div + bar", restyle: false },
{ selector: "baz", restyle: false },
{ cssText: " ", restyle: false },
{ cssText: "@keyframes foo { from { color: green } to { color: red } } #whatever { animation-name: foo; }", restyle: false },
];
for (const test of TESTS) {
target.innerHTML = test.selector + " { color: green; }";
let cssText = test.cssText ? test.cssText : (test.selector + " { color: green; }");
target.innerHTML = cssText;
document.body.offsetWidth;
const prevGeneration = utils.restyleGeneration;
@ -42,19 +46,22 @@ for (const test of TESTS) {
document.body.offsetWidth;
(test.restyle ? isnot : is)(utils.restyleGeneration, prevGeneration,
`Stylesheet removal with ${test.selector} should ${test.restyle ? "have" : "not have"} caused a restyle`);
`Stylesheet removal with ${cssText} should ${test.restyle ? "have" : "not have"} caused a restyle`);
target.disabled = false; // Make the stylesheet effective.
let element = document.querySelector(test.selector);
if (element) {
is(test.restyle, true, "How could we not expect a restyle?");
is(getComputedStyle(element).color, "rgb(0, 128, 0)",
"Element style should've changed appropriately");
if (test.selector) {
let element = document.querySelector(test.selector);
if (element) {
is(test.restyle, true, "How could we not expect a restyle?");
is(getComputedStyle(element).color, "rgb(0, 128, 0)",
"Element style should've changed appropriately");
}
}
document.body.offsetWidth;
(test.restyle ? isnot : is)(utils.restyleGeneration, prevGeneration,
`Stylesheet addition with ${test.selector} should ${test.restyle ? "have" : "not have"} caused a restyle`);
`Stylesheet addition with ${cssText} should ${test.restyle ? "have" : "not have"} caused a restyle`);
}
SimpleTest.finish();