2015-12-04 02:34:12 +03:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
|
#include "EffectCompositor.h"
|
|
|
|
|
|
|
|
#include "mozilla/dom/Animation.h"
|
2016-01-06 05:04:04 +03:00
|
|
|
#include "mozilla/dom/Element.h"
|
|
|
|
#include "mozilla/dom/KeyframeEffect.h" // For KeyframeEffectReadOnly
|
2015-12-04 02:34:12 +03:00
|
|
|
#include "mozilla/AnimationUtils.h"
|
|
|
|
#include "mozilla/EffectSet.h"
|
2016-01-13 01:54:54 +03:00
|
|
|
#include "mozilla/InitializerList.h"
|
2016-01-06 05:04:04 +03:00
|
|
|
#include "mozilla/LayerAnimationInfo.h"
|
2016-01-06 05:04:05 +03:00
|
|
|
#include "nsComputedDOMStyle.h" // nsComputedDOMStyle::GetPresShellForContent
|
2016-01-06 05:04:04 +03:00
|
|
|
#include "nsCSSPropertySet.h"
|
2016-01-06 05:04:05 +03:00
|
|
|
#include "nsCSSProps.h"
|
2016-01-06 05:04:05 +03:00
|
|
|
#include "nsIPresShell.h"
|
2015-12-04 02:34:12 +03:00
|
|
|
#include "nsLayoutUtils.h"
|
2016-01-06 05:04:04 +03:00
|
|
|
#include "nsRuleNode.h" // For nsRuleNode::ComputePropertiesOverridingAnimation
|
2016-01-15 09:15:47 +03:00
|
|
|
#include "nsRuleProcessorData.h" // For ElementRuleProcessorData etc.
|
2016-01-06 05:04:04 +03:00
|
|
|
#include "nsTArray.h"
|
2016-01-13 01:54:54 +03:00
|
|
|
#include "RestyleManager.h"
|
2015-12-04 02:34:12 +03:00
|
|
|
|
|
|
|
using mozilla::dom::Animation;
|
2016-01-06 05:04:04 +03:00
|
|
|
using mozilla::dom::Element;
|
2015-12-04 02:34:12 +03:00
|
|
|
using mozilla::dom::KeyframeEffectReadOnly;
|
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
|
2016-01-13 01:54:53 +03:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_CLASS(EffectCompositor)
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(EffectCompositor)
|
|
|
|
for (auto& elementSet : tmp->mElementsToRestyle) {
|
|
|
|
elementSet.Clear();
|
|
|
|
}
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(EffectCompositor)
|
|
|
|
for (auto& elementSet : tmp->mElementsToRestyle) {
|
|
|
|
for (auto iter = elementSet.Iter(); !iter.Done(); iter.Next()) {
|
|
|
|
CycleCollectionNoteChild(cb, iter.Key().mElement,
|
|
|
|
"EffectCompositor::mElementsToRestyle[]",
|
|
|
|
cb.Flags());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
2016-01-13 01:54:53 +03:00
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(EffectCompositor, AddRef)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(EffectCompositor, Release)
|
|
|
|
|
2015-12-10 00:28:10 +03:00
|
|
|
// Helper function to factor out the common logic from
|
|
|
|
// GetAnimationsForCompositor and HasAnimationsForCompositor.
|
|
|
|
//
|
|
|
|
// Takes an optional array to fill with eligible animations.
|
|
|
|
//
|
|
|
|
// Returns true if there are eligible animations, false otherwise.
|
|
|
|
bool
|
|
|
|
FindAnimationsForCompositor(const nsIFrame* aFrame,
|
|
|
|
nsCSSProperty aProperty,
|
|
|
|
nsTArray<RefPtr<dom::Animation>>* aMatches /*out*/)
|
2015-12-04 02:34:12 +03:00
|
|
|
{
|
2015-12-10 00:28:10 +03:00
|
|
|
MOZ_ASSERT(!aMatches || aMatches->IsEmpty(),
|
|
|
|
"Matches array, if provided, should be empty");
|
|
|
|
|
|
|
|
EffectSet* effects = EffectSet::GetEffectSet(aFrame);
|
|
|
|
if (!effects || effects->IsEmpty()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aFrame->RefusedAsyncAnimation()) {
|
|
|
|
return false;
|
|
|
|
}
|
2015-12-04 02:34:12 +03:00
|
|
|
|
2016-01-06 05:04:05 +03:00
|
|
|
// The animation cascade will almost always be up-to-date by this point
|
|
|
|
// but there are some cases such as when we are restoring the refresh driver
|
|
|
|
// from test control after seeking where it might not be the case.
|
|
|
|
//
|
|
|
|
// Those cases are probably not important but just to be safe, let's make
|
|
|
|
// sure the cascade is up to date since if it *is* up to date, this is
|
|
|
|
// basically a no-op.
|
|
|
|
Maybe<Pair<dom::Element*, nsCSSPseudoElements::Type>> pseudoElement =
|
|
|
|
EffectCompositor::GetAnimationElementAndPseudoForFrame(aFrame);
|
|
|
|
if (pseudoElement) {
|
|
|
|
EffectCompositor::MaybeUpdateCascadeResults(pseudoElement->first(),
|
|
|
|
pseudoElement->second(),
|
|
|
|
aFrame->StyleContext());
|
|
|
|
}
|
|
|
|
|
2015-12-04 02:34:12 +03:00
|
|
|
if (!nsLayoutUtils::AreAsyncAnimationsEnabled()) {
|
|
|
|
if (nsLayoutUtils::IsAnimationLoggingEnabled()) {
|
|
|
|
nsCString message;
|
|
|
|
message.AppendLiteral("Performance warning: Async animations are "
|
|
|
|
"disabled");
|
|
|
|
AnimationUtils::LogAsyncAnimationFailure(message);
|
|
|
|
}
|
2015-12-10 00:28:10 +03:00
|
|
|
return false;
|
2015-12-04 02:34:12 +03:00
|
|
|
}
|
|
|
|
|
2015-12-10 00:28:10 +03:00
|
|
|
bool foundSome = false;
|
2015-12-04 02:34:12 +03:00
|
|
|
for (KeyframeEffectReadOnly* effect : *effects) {
|
|
|
|
MOZ_ASSERT(effect && effect->GetAnimation());
|
|
|
|
Animation* animation = effect->GetAnimation();
|
|
|
|
|
|
|
|
if (!animation->IsPlaying()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (effect->ShouldBlockCompositorAnimations(aFrame)) {
|
2015-12-10 00:28:10 +03:00
|
|
|
if (aMatches) {
|
|
|
|
aMatches->Clear();
|
|
|
|
}
|
|
|
|
return false;
|
2015-12-04 02:34:12 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!effect->HasAnimationOfProperty(aProperty)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-12-10 00:28:10 +03:00
|
|
|
if (aMatches) {
|
|
|
|
aMatches->AppendElement(animation);
|
|
|
|
}
|
|
|
|
foundSome = true;
|
2015-12-04 02:34:12 +03:00
|
|
|
}
|
|
|
|
|
2015-12-10 00:28:10 +03:00
|
|
|
MOZ_ASSERT(!foundSome || !aMatches || !aMatches->IsEmpty(),
|
|
|
|
"If return value is true, matches array should be non-empty");
|
|
|
|
return foundSome;
|
|
|
|
}
|
|
|
|
|
2016-01-13 01:54:53 +03:00
|
|
|
void
|
|
|
|
EffectCompositor::RequestRestyle(dom::Element* aElement,
|
|
|
|
nsCSSPseudoElements::Type aPseudoType,
|
|
|
|
RestyleType aRestyleType,
|
|
|
|
CascadeLevel aCascadeLevel)
|
|
|
|
{
|
2016-01-13 01:54:54 +03:00
|
|
|
if (!mPresContext) {
|
|
|
|
// Pres context will be null after the effect compositor is disconnected.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-01-13 01:54:53 +03:00
|
|
|
auto& elementsToRestyle = mElementsToRestyle[aCascadeLevel];
|
|
|
|
PseudoElementHashKey key = { aElement, aPseudoType };
|
|
|
|
|
2016-01-18 22:54:00 +03:00
|
|
|
if (aRestyleType == RestyleType::Throttled) {
|
|
|
|
if (!elementsToRestyle.Contains(key)) {
|
|
|
|
elementsToRestyle.Put(key, false);
|
|
|
|
}
|
2016-01-13 01:54:54 +03:00
|
|
|
mPresContext->Document()->SetNeedStyleFlush();
|
2016-01-13 01:54:53 +03:00
|
|
|
} else {
|
2016-01-13 01:54:54 +03:00
|
|
|
// Get() returns 0 if the element is not found. It will also return
|
|
|
|
// false if the element is found but does not have a pending restyle.
|
|
|
|
bool hasPendingRestyle = elementsToRestyle.Get(key);
|
|
|
|
if (!hasPendingRestyle) {
|
|
|
|
PostRestyleForAnimation(aElement, aPseudoType, aCascadeLevel);
|
|
|
|
}
|
2016-01-13 01:54:53 +03:00
|
|
|
elementsToRestyle.Put(key, true);
|
|
|
|
}
|
2016-01-13 01:54:54 +03:00
|
|
|
|
|
|
|
if (aRestyleType == RestyleType::Layer) {
|
|
|
|
// Prompt layers to re-sync their animations.
|
|
|
|
mPresContext->RestyleManager()->IncrementAnimationGeneration();
|
|
|
|
EffectSet* effectSet =
|
|
|
|
EffectSet::GetEffectSet(aElement, aPseudoType);
|
|
|
|
if (effectSet) {
|
|
|
|
effectSet->UpdateAnimationGeneration(mPresContext);
|
|
|
|
}
|
|
|
|
}
|
2016-01-13 01:54:53 +03:00
|
|
|
}
|
|
|
|
|
2016-01-13 01:54:54 +03:00
|
|
|
void
|
|
|
|
EffectCompositor::PostRestyleForAnimation(dom::Element* aElement,
|
|
|
|
nsCSSPseudoElements::Type aPseudoType,
|
|
|
|
CascadeLevel aCascadeLevel)
|
|
|
|
{
|
|
|
|
if (!mPresContext) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
dom::Element* element = GetElementToRestyle(aElement, aPseudoType);
|
|
|
|
if (!element) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsRestyleHint hint = aCascadeLevel == CascadeLevel::Transitions ?
|
|
|
|
eRestyle_CSSTransitions :
|
|
|
|
eRestyle_CSSAnimations;
|
|
|
|
mPresContext->PresShell()->RestyleForAnimation(element, hint);
|
|
|
|
}
|
|
|
|
|
2016-01-13 01:54:55 +03:00
|
|
|
void
|
|
|
|
EffectCompositor::PostRestyleForThrottledAnimations()
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < kCascadeLevelCount; i++) {
|
|
|
|
CascadeLevel cascadeLevel = CascadeLevel(i);
|
|
|
|
auto& elementSet = mElementsToRestyle[cascadeLevel];
|
|
|
|
|
|
|
|
for (auto iter = elementSet.Iter(); !iter.Done(); iter.Next()) {
|
|
|
|
bool& postedRestyle = iter.Data();
|
|
|
|
if (postedRestyle) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
PostRestyleForAnimation(iter.Key().mElement,
|
|
|
|
iter.Key().mPseudoType,
|
|
|
|
cascadeLevel);
|
|
|
|
postedRestyle = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-13 01:54:53 +03:00
|
|
|
void
|
|
|
|
EffectCompositor::MaybeUpdateAnimationRule(dom::Element* aElement,
|
|
|
|
nsCSSPseudoElements::Type
|
|
|
|
aPseudoType,
|
2016-01-13 01:54:54 +03:00
|
|
|
CascadeLevel aCascadeLevel)
|
2016-01-13 01:54:53 +03:00
|
|
|
{
|
2016-01-13 01:54:55 +03:00
|
|
|
// First update cascade results since that may cause some elements to
|
|
|
|
// be marked as needing a restyle.
|
2016-01-13 01:54:55 +03:00
|
|
|
MaybeUpdateCascadeResults(aElement, aPseudoType);
|
2016-01-13 01:54:55 +03:00
|
|
|
|
2016-01-13 01:54:53 +03:00
|
|
|
auto& elementsToRestyle = mElementsToRestyle[aCascadeLevel];
|
|
|
|
PseudoElementHashKey key = { aElement, aPseudoType };
|
|
|
|
|
2016-01-13 01:54:54 +03:00
|
|
|
if (!mPresContext || !elementsToRestyle.Contains(key)) {
|
2016-01-13 01:54:53 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-01-13 01:54:54 +03:00
|
|
|
ComposeAnimationRule(aElement, aPseudoType, aCascadeLevel,
|
2016-01-13 01:54:54 +03:00
|
|
|
mPresContext->RefreshDriver()->MostRecentRefresh());
|
2016-01-13 01:54:53 +03:00
|
|
|
|
|
|
|
elementsToRestyle.Remove(key);
|
|
|
|
}
|
|
|
|
|
2016-01-13 01:54:55 +03:00
|
|
|
nsIStyleRule*
|
|
|
|
EffectCompositor::GetAnimationRule(dom::Element* aElement,
|
|
|
|
nsCSSPseudoElements::Type aPseudoType,
|
|
|
|
CascadeLevel aCascadeLevel)
|
|
|
|
{
|
2016-01-15 09:15:47 +03:00
|
|
|
// NOTE: We need to be careful about early returns in this method where
|
|
|
|
// we *don't* update mElementsToRestyle. When we get a call to
|
|
|
|
// RequestRestyle that results in a call to PostRestyleForAnimation, we
|
|
|
|
// will set a bool flag in mElementsToRestyle indicating that we've
|
|
|
|
// called PostRestyleForAnimation so we don't need to call it again
|
|
|
|
// until that restyle happens. During that restyle, if we arrive here
|
|
|
|
// and *don't* update mElementsToRestyle we'll continue to skip calling
|
|
|
|
// PostRestyleForAnimation from RequestRestyle.
|
|
|
|
|
2016-01-13 01:54:55 +03:00
|
|
|
if (!mPresContext || !mPresContext->IsDynamic()) {
|
|
|
|
// For print or print preview, ignore animations.
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2016-01-14 09:05:06 +03:00
|
|
|
if (mPresContext->RestyleManager()->SkipAnimationRules()) {
|
2016-01-15 09:15:47 +03:00
|
|
|
// We don't need to worry about updating mElementsToRestyle in this case
|
|
|
|
// since this is not the animation restyle we requested when we called
|
|
|
|
// PostRestyleForAnimation (see comment at start of this method).
|
2016-01-13 01:54:55 +03:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
MaybeUpdateAnimationRule(aElement, aPseudoType, aCascadeLevel);
|
|
|
|
|
2016-01-15 09:15:47 +03:00
|
|
|
#ifdef DEBUG
|
|
|
|
{
|
|
|
|
auto& elementsToRestyle = mElementsToRestyle[aCascadeLevel];
|
|
|
|
PseudoElementHashKey key = { aElement, aPseudoType };
|
|
|
|
MOZ_ASSERT(!elementsToRestyle.Contains(key),
|
|
|
|
"Element should no longer require a restyle after its "
|
|
|
|
"animation rule has been updated");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
EffectSet* effectSet = EffectSet::GetEffectSet(aElement, aPseudoType);
|
|
|
|
if (!effectSet) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2016-01-13 01:54:55 +03:00
|
|
|
return effectSet->AnimationRule(aCascadeLevel);
|
|
|
|
}
|
|
|
|
|
2016-01-13 01:54:54 +03:00
|
|
|
/* static */ dom::Element*
|
|
|
|
EffectCompositor::GetElementToRestyle(dom::Element* aElement,
|
|
|
|
nsCSSPseudoElements::Type aPseudoType)
|
|
|
|
{
|
|
|
|
if (aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement) {
|
|
|
|
return aElement;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
|
|
|
|
if (!primaryFrame) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
nsIFrame* pseudoFrame;
|
|
|
|
if (aPseudoType == nsCSSPseudoElements::ePseudo_before) {
|
|
|
|
pseudoFrame = nsLayoutUtils::GetBeforeFrame(primaryFrame);
|
|
|
|
} else if (aPseudoType == nsCSSPseudoElements::ePseudo_after) {
|
|
|
|
pseudoFrame = nsLayoutUtils::GetAfterFrame(primaryFrame);
|
|
|
|
} else {
|
|
|
|
NS_NOTREACHED("Should not try to get the element to restyle for a pseudo "
|
|
|
|
"other that :before or :after");
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
if (!pseudoFrame) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return pseudoFrame->GetContent()->AsElement();
|
|
|
|
}
|
|
|
|
|
2016-01-13 01:54:55 +03:00
|
|
|
bool
|
|
|
|
EffectCompositor::HasPendingStyleUpdates() const
|
|
|
|
{
|
|
|
|
for (auto& elementSet : mElementsToRestyle) {
|
|
|
|
if (elementSet.Count()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
EffectCompositor::HasThrottledStyleUpdates() const
|
|
|
|
{
|
|
|
|
for (auto& elementSet : mElementsToRestyle) {
|
|
|
|
for (auto iter = elementSet.ConstIter(); !iter.Done(); iter.Next()) {
|
|
|
|
if (!iter.Data()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-01-13 01:54:55 +03:00
|
|
|
void
|
|
|
|
EffectCompositor::AddStyleUpdatesTo(RestyleTracker& aTracker)
|
|
|
|
{
|
|
|
|
if (!mPresContext) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i = 0; i < kCascadeLevelCount; i++) {
|
|
|
|
CascadeLevel cascadeLevel = CascadeLevel(i);
|
|
|
|
auto& elementSet = mElementsToRestyle[cascadeLevel];
|
|
|
|
|
|
|
|
// Copy the list of elements to restyle to a separate array that we can
|
|
|
|
// iterate over. This is because we need to call MaybeUpdateCascadeResults
|
|
|
|
// on each element, but doing that can mutate elementSet. In this case
|
|
|
|
// it will only mutate the bool value associated with each element in the
|
|
|
|
// set but even doing that will cause assertions in PLDHashTable to fail
|
|
|
|
// if we are iterating over the hashtable at the same time.
|
|
|
|
nsTArray<PseudoElementHashKey> elementsToRestyle(elementSet.Count());
|
|
|
|
for (auto iter = elementSet.Iter(); !iter.Done(); iter.Next()) {
|
|
|
|
elementsToRestyle.AppendElement(iter.Key());
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto& pseudoElem : elementsToRestyle) {
|
|
|
|
MaybeUpdateCascadeResults(pseudoElem.mElement, pseudoElem.mPseudoType);
|
|
|
|
|
|
|
|
ComposeAnimationRule(pseudoElem.mElement,
|
|
|
|
pseudoElem.mPseudoType,
|
|
|
|
cascadeLevel,
|
|
|
|
mPresContext->RefreshDriver()->MostRecentRefresh());
|
|
|
|
|
|
|
|
dom::Element* elementToRestyle =
|
|
|
|
GetElementToRestyle(pseudoElem.mElement, pseudoElem.mPseudoType);
|
|
|
|
if (elementToRestyle) {
|
|
|
|
nsRestyleHint rshint = cascadeLevel == CascadeLevel::Transitions ?
|
|
|
|
eRestyle_CSSTransitions :
|
|
|
|
eRestyle_CSSAnimations;
|
|
|
|
aTracker.AddPendingRestyle(elementToRestyle, rshint, nsChangeHint(0));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
elementSet.Clear();
|
|
|
|
// Note: mElement pointers in elementsToRestyle might now dangle
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-10 00:28:10 +03:00
|
|
|
/* static */ bool
|
|
|
|
EffectCompositor::HasAnimationsForCompositor(const nsIFrame* aFrame,
|
|
|
|
nsCSSProperty aProperty)
|
|
|
|
{
|
|
|
|
return FindAnimationsForCompositor(aFrame, aProperty, nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* static */ nsTArray<RefPtr<dom::Animation>>
|
|
|
|
EffectCompositor::GetAnimationsForCompositor(const nsIFrame* aFrame,
|
|
|
|
nsCSSProperty aProperty)
|
|
|
|
{
|
|
|
|
nsTArray<RefPtr<dom::Animation>> result;
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
bool foundSome =
|
|
|
|
#endif
|
|
|
|
FindAnimationsForCompositor(aFrame, aProperty, &result);
|
|
|
|
MOZ_ASSERT(!foundSome || !result.IsEmpty(),
|
|
|
|
"If return value is true, matches array should be non-empty");
|
|
|
|
|
2015-12-04 02:34:12 +03:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-01-13 01:54:56 +03:00
|
|
|
/* static */ void
|
|
|
|
EffectCompositor::ClearIsRunningOnCompositor(const nsIFrame *aFrame,
|
|
|
|
nsCSSProperty aProperty)
|
|
|
|
{
|
|
|
|
EffectSet* effects = EffectSet::GetEffectSet(aFrame);
|
|
|
|
if (!effects) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (KeyframeEffectReadOnly* effect : *effects) {
|
|
|
|
effect->SetIsRunningOnCompositor(aProperty, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-06 05:04:05 +03:00
|
|
|
/* static */ void
|
|
|
|
EffectCompositor::MaybeUpdateCascadeResults(Element* aElement,
|
|
|
|
nsCSSPseudoElements::Type
|
|
|
|
aPseudoType,
|
|
|
|
nsStyleContext* aStyleContext)
|
|
|
|
{
|
|
|
|
EffectSet* effects = EffectSet::GetEffectSet(aElement, aPseudoType);
|
|
|
|
if (!effects || !effects->CascadeNeedsUpdate()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
UpdateCascadeResults(*effects, aElement, aPseudoType, aStyleContext);
|
|
|
|
|
|
|
|
MOZ_ASSERT(!effects->CascadeNeedsUpdate(), "Failed to update cascade state");
|
|
|
|
}
|
|
|
|
|
2016-01-13 01:54:55 +03:00
|
|
|
/* static */ void
|
|
|
|
EffectCompositor::MaybeUpdateCascadeResults(Element* aElement,
|
|
|
|
nsCSSPseudoElements::Type
|
|
|
|
aPseudoType)
|
|
|
|
{
|
|
|
|
nsStyleContext* styleContext = nullptr;
|
|
|
|
{
|
|
|
|
dom::Element* elementToRestyle = GetElementToRestyle(aElement, aPseudoType);
|
|
|
|
if (elementToRestyle) {
|
|
|
|
nsIFrame* frame = elementToRestyle->GetPrimaryFrame();
|
|
|
|
if (frame) {
|
|
|
|
styleContext = frame->StyleContext();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MaybeUpdateCascadeResults(aElement, aPseudoType, styleContext);
|
|
|
|
}
|
|
|
|
|
2016-01-06 05:04:05 +03:00
|
|
|
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,
|
|
|
|
nsCSSPseudoElements::Type aPseudoType,
|
|
|
|
nsStyleContext* aStyleContext)
|
|
|
|
{
|
|
|
|
EffectSet* effects = EffectSet::GetEffectSet(aElement, aPseudoType);
|
|
|
|
if (!effects) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
UpdateCascadeResults(*effects, aElement, aPseudoType, aStyleContext);
|
|
|
|
}
|
|
|
|
|
2016-01-06 05:04:04 +03:00
|
|
|
/* static */ Maybe<Pair<Element*, nsCSSPseudoElements::Type>>
|
|
|
|
EffectCompositor::GetAnimationElementAndPseudoForFrame(const nsIFrame* aFrame)
|
|
|
|
{
|
|
|
|
// Always return the same object to benefit from return-value optimization.
|
|
|
|
Maybe<Pair<Element*, nsCSSPseudoElements::Type>> result;
|
|
|
|
|
|
|
|
nsIContent* content = aFrame->GetContent();
|
|
|
|
if (!content) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCSSPseudoElements::Type pseudoType =
|
|
|
|
nsCSSPseudoElements::ePseudo_NotPseudoElement;
|
|
|
|
|
|
|
|
if (aFrame->IsGeneratedContentFrame()) {
|
|
|
|
nsIFrame* parent = aFrame->GetParent();
|
|
|
|
if (parent->IsGeneratedContentFrame()) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
nsIAtom* name = content->NodeInfo()->NameAtom();
|
|
|
|
if (name == nsGkAtoms::mozgeneratedcontentbefore) {
|
|
|
|
pseudoType = nsCSSPseudoElements::ePseudo_before;
|
|
|
|
} else if (name == nsGkAtoms::mozgeneratedcontentafter) {
|
|
|
|
pseudoType = nsCSSPseudoElements::ePseudo_after;
|
|
|
|
} else {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
content = content->GetParent();
|
|
|
|
if (!content) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!content->IsElement()) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = Some(MakePair(content->AsElement(), pseudoType));
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-01-06 05:04:06 +03:00
|
|
|
/* static */ void
|
|
|
|
EffectCompositor::ComposeAnimationRule(dom::Element* aElement,
|
|
|
|
nsCSSPseudoElements::Type aPseudoType,
|
|
|
|
CascadeLevel aCascadeLevel,
|
2016-01-13 01:54:54 +03:00
|
|
|
TimeStamp aRefreshTime)
|
2016-01-06 05:04:06 +03:00
|
|
|
{
|
|
|
|
EffectSet* effects = EffectSet::GetEffectSet(aElement, aPseudoType);
|
|
|
|
if (!effects) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The caller is responsible for calling MaybeUpdateCascadeResults first.
|
|
|
|
MOZ_ASSERT(!effects->CascadeNeedsUpdate(),
|
|
|
|
"Animation cascade out of date when composing animation rule");
|
|
|
|
|
|
|
|
// Get a list of effects for the current level sorted by composite order.
|
|
|
|
nsTArray<KeyframeEffectReadOnly*> sortedEffectList;
|
|
|
|
for (KeyframeEffectReadOnly* effect : *effects) {
|
|
|
|
MOZ_ASSERT(effect->GetAnimation());
|
|
|
|
if (effect->GetAnimation()->CascadeLevel() == aCascadeLevel) {
|
|
|
|
sortedEffectList.AppendElement(effect);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sortedEffectList.Sort(EffectCompositeOrderComparator());
|
|
|
|
|
|
|
|
RefPtr<AnimValuesStyleRule>& animationRule =
|
|
|
|
effects->AnimationRule(aCascadeLevel);
|
|
|
|
animationRule = nullptr;
|
|
|
|
|
|
|
|
// If multiple animations specify behavior for the same property the
|
|
|
|
// animation with the *highest* composite order wins.
|
|
|
|
// As a result, we iterate from last animation to first and, if a
|
|
|
|
// property has already been set, we don't change it.
|
|
|
|
nsCSSPropertySet properties;
|
|
|
|
|
|
|
|
for (KeyframeEffectReadOnly* effect : Reversed(sortedEffectList)) {
|
2016-01-13 01:54:54 +03:00
|
|
|
effect->GetAnimation()->ComposeStyle(animationRule, properties);
|
2016-01-06 05:04:06 +03:00
|
|
|
}
|
2016-01-13 01:54:54 +03:00
|
|
|
|
Bug 1240228 - Don't update an effect's timing when tweaking its animation's hold time; r=heycam
In some circumstances when composing style, we tweak the time of the animation
before telling the effect to compose style. This is to avoid visual flicker in
certain situations where the main thread progress is being synchronized with an
animation running on the compositor.
In the past, effects would store their latest sample time locally so when
tweaking the animation time, we would need to call UpdateEffect() after tweaking
the time, and then again after restoring it as otherwise the style composed by
the effect would not reflect the adjusted time.
Now, however, effect's always query their animation for the time so this is no
longer necessary. Furthermore, the actions triggered by UpdateEffect are not
desirable in this case because they can, amongst other things, cause the
associated EffectSet to be destroyed and recreated.
Specifically, Animation::UpdateEffect() calls
KeyframeEffectReadOnly::NotifyAnimationTimingUpdated() which:
* Calls UpdateTargetRegistration which can trigger EffectSet
destruction/creation which is undesirable in this case because we intend to
restore whatever changes we make to the Animation's state and deleting and
recreating the EffectSet will cause any pointers to it to dangle.
* Cause us to possibly reset the "is running on compositor" status.
This too is undesirable since we intend to restore the state of the
Animation immediately after tweaking the hold time so we don't want to
act as if any state has changed.
* Similarly for marking the cascade as possibly needing an update or
requesting a restyle.
In summary, all the actions performed by NotifyAnimationTimingUpdated are
unnecessary and undesirable in this situation where we are temporarily tweaking
an Animation's current time only to restore it immediately afterwards since the
actions are all involved with recognizing actual changes in state.
2016-01-19 02:05:08 +03:00
|
|
|
MOZ_ASSERT(effects == EffectSet::GetEffectSet(aElement, aPseudoType),
|
|
|
|
"EffectSet should not change while composing style");
|
|
|
|
|
2016-01-13 01:54:54 +03:00
|
|
|
effects->UpdateAnimationRuleRefreshTime(aCascadeLevel, aRefreshTime);
|
2016-01-06 05:04:06 +03:00
|
|
|
}
|
|
|
|
|
2016-01-06 05:04:04 +03:00
|
|
|
/* static */ void
|
|
|
|
EffectCompositor::GetOverriddenProperties(nsStyleContext* aStyleContext,
|
2016-01-06 05:04:05 +03:00
|
|
|
EffectSet& aEffectSet,
|
2016-01-06 05:04:04 +03:00
|
|
|
nsCSSPropertySet&
|
|
|
|
aPropertiesOverridden)
|
|
|
|
{
|
2016-02-02 18:36:30 +03:00
|
|
|
AutoTArray<nsCSSProperty, LayerAnimationInfo::kRecords> propertiesToTrack;
|
2016-01-06 05:04:05 +03:00
|
|
|
{
|
|
|
|
nsCSSPropertySet propertiesToTrackAsSet;
|
|
|
|
for (KeyframeEffectReadOnly* effect : aEffectSet) {
|
|
|
|
for (const AnimationProperty& property : effect->Properties()) {
|
|
|
|
if (nsCSSProps::PropHasFlags(property.mProperty,
|
|
|
|
CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR) &&
|
|
|
|
!propertiesToTrackAsSet.HasProperty(property.mProperty)) {
|
|
|
|
propertiesToTrackAsSet.AddProperty(property.mProperty);
|
|
|
|
propertiesToTrack.AppendElement(property.mProperty);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Skip iterating over the rest of the effects if we've already
|
|
|
|
// found all the compositor-animatable properties.
|
|
|
|
if (propertiesToTrack.Length() == LayerAnimationInfo::kRecords) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2016-01-06 05:04:04 +03:00
|
|
|
}
|
2016-01-06 05:04:05 +03:00
|
|
|
|
|
|
|
if (propertiesToTrack.IsEmpty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-01-06 05:04:04 +03:00
|
|
|
nsRuleNode::ComputePropertiesOverridingAnimation(propertiesToTrack,
|
|
|
|
aStyleContext,
|
|
|
|
aPropertiesOverridden);
|
|
|
|
}
|
|
|
|
|
2016-01-06 05:04:05 +03:00
|
|
|
/* static */ void
|
|
|
|
EffectCompositor::UpdateCascadeResults(EffectSet& aEffectSet,
|
|
|
|
Element* aElement,
|
|
|
|
nsCSSPseudoElements::Type aPseudoType,
|
|
|
|
nsStyleContext* aStyleContext)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(EffectSet::GetEffectSet(aElement, aPseudoType) == &aEffectSet,
|
|
|
|
"Effect set should correspond to the specified (pseudo-)element");
|
|
|
|
if (aEffectSet.IsEmpty()) {
|
|
|
|
aEffectSet.MarkCascadeUpdated();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get a list of effects sorted by composite order.
|
|
|
|
nsTArray<KeyframeEffectReadOnly*> sortedEffectList;
|
|
|
|
for (KeyframeEffectReadOnly* effect : aEffectSet) {
|
|
|
|
sortedEffectList.AppendElement(effect);
|
|
|
|
}
|
|
|
|
sortedEffectList.Sort(EffectCompositeOrderComparator());
|
|
|
|
|
|
|
|
// Get properties that override the *animations* level of the cascade.
|
|
|
|
//
|
|
|
|
// We only do this for properties that we can animate on the compositor
|
|
|
|
// since we will apply other properties on the main thread where the usual
|
|
|
|
// cascade applies.
|
|
|
|
nsCSSPropertySet overriddenProperties;
|
|
|
|
if (aStyleContext) {
|
2016-01-06 05:04:05 +03:00
|
|
|
GetOverriddenProperties(aStyleContext, aEffectSet, overriddenProperties);
|
2016-01-06 05:04:05 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bool changed = false;
|
|
|
|
nsCSSPropertySet animatedProperties;
|
|
|
|
|
|
|
|
// Iterate from highest to lowest composite order.
|
|
|
|
for (KeyframeEffectReadOnly* effect : Reversed(sortedEffectList)) {
|
|
|
|
MOZ_ASSERT(effect->GetAnimation(),
|
|
|
|
"Effects on a target element should have an Animation");
|
|
|
|
bool inEffect = effect->IsInEffect();
|
|
|
|
for (AnimationProperty& prop : effect->Properties()) {
|
|
|
|
|
|
|
|
bool winsInCascade = !animatedProperties.HasProperty(prop.mProperty) &&
|
|
|
|
inEffect;
|
|
|
|
|
|
|
|
// If this property wins in the cascade, add it to the set of animated
|
|
|
|
// properties. We need to do this even if the property is overridden
|
|
|
|
// (in which case we set winsInCascade to false below) since we don't
|
|
|
|
// want to fire transitions on these properties.
|
|
|
|
if (winsInCascade) {
|
|
|
|
animatedProperties.AddProperty(prop.mProperty);
|
|
|
|
}
|
|
|
|
|
|
|
|
// For effects that will be applied to the animations level of the
|
|
|
|
// cascade, we need to check that the property isn't being set by
|
|
|
|
// something with higher priority in the cascade.
|
|
|
|
//
|
|
|
|
// We only do this, however, for properties that can be animated on
|
|
|
|
// the compositor. For properties animated on the main thread the usual
|
|
|
|
// cascade ensures these animations will be correctly overridden.
|
|
|
|
if (winsInCascade &&
|
2016-01-06 05:04:05 +03:00
|
|
|
effect->GetAnimation()->CascadeLevel() == CascadeLevel::Animations &&
|
2016-01-06 05:04:05 +03:00
|
|
|
overriddenProperties.HasProperty(prop.mProperty)) {
|
|
|
|
winsInCascade = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (winsInCascade != prop.mWinsInCascade) {
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
prop.mWinsInCascade = winsInCascade;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
aEffectSet.MarkCascadeUpdated();
|
|
|
|
|
|
|
|
// If there is any change in the cascade result, update animations on
|
|
|
|
// layers with the winning animations.
|
|
|
|
nsPresContext* presContext = GetPresContext(aElement);
|
|
|
|
if (changed && presContext) {
|
2016-01-13 01:54:54 +03:00
|
|
|
// Update both transitions and animations. We could detect *which* levels
|
|
|
|
// actually changed and only update them, but that's probably unnecessary.
|
|
|
|
for (auto level : { CascadeLevel::Animations,
|
|
|
|
CascadeLevel::Transitions }) {
|
|
|
|
presContext->EffectCompositor()->RequestRestyle(aElement,
|
|
|
|
aPseudoType,
|
|
|
|
RestyleType::Layer,
|
|
|
|
level);
|
2016-01-06 05:04:05 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* static */ nsPresContext*
|
|
|
|
EffectCompositor::GetPresContext(Element* aElement)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(aElement);
|
|
|
|
nsIPresShell* shell = nsComputedDOMStyle::GetPresShellForContent(aElement);
|
|
|
|
if (!shell) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return shell->GetPresContext();
|
|
|
|
}
|
|
|
|
|
2016-01-15 09:15:47 +03:00
|
|
|
// ---------------------------------------------------------
|
|
|
|
//
|
|
|
|
// Nested class: AnimationStyleRuleProcessor
|
|
|
|
//
|
|
|
|
// ---------------------------------------------------------
|
|
|
|
|
|
|
|
NS_IMPL_ISUPPORTS(EffectCompositor::AnimationStyleRuleProcessor,
|
|
|
|
nsIStyleRuleProcessor)
|
|
|
|
|
|
|
|
nsRestyleHint
|
|
|
|
EffectCompositor::AnimationStyleRuleProcessor::HasStateDependentStyle(
|
|
|
|
StateRuleProcessorData* aData)
|
|
|
|
{
|
|
|
|
return nsRestyleHint(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
nsRestyleHint
|
|
|
|
EffectCompositor::AnimationStyleRuleProcessor::HasStateDependentStyle(
|
|
|
|
PseudoElementStateRuleProcessorData* aData)
|
|
|
|
{
|
|
|
|
return nsRestyleHint(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
EffectCompositor::AnimationStyleRuleProcessor::HasDocumentStateDependentStyle(
|
|
|
|
StateRuleProcessorData* aData)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsRestyleHint
|
|
|
|
EffectCompositor::AnimationStyleRuleProcessor::HasAttributeDependentStyle(
|
|
|
|
AttributeRuleProcessorData* aData,
|
|
|
|
RestyleHintData& aRestyleHintDataResult)
|
|
|
|
{
|
|
|
|
return nsRestyleHint(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
EffectCompositor::AnimationStyleRuleProcessor::MediumFeaturesChanged(
|
|
|
|
nsPresContext* aPresContext)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
EffectCompositor::AnimationStyleRuleProcessor::RulesMatching(
|
|
|
|
ElementRuleProcessorData* aData)
|
|
|
|
{
|
|
|
|
nsIStyleRule *rule =
|
|
|
|
mCompositor->GetAnimationRule(aData->mElement,
|
|
|
|
nsCSSPseudoElements::ePseudo_NotPseudoElement,
|
|
|
|
mCascadeLevel);
|
|
|
|
if (rule) {
|
|
|
|
aData->mRuleWalker->Forward(rule);
|
|
|
|
aData->mRuleWalker->CurrentNode()->SetIsAnimationRule();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
EffectCompositor::AnimationStyleRuleProcessor::RulesMatching(
|
|
|
|
PseudoElementRuleProcessorData* aData)
|
|
|
|
{
|
|
|
|
if (aData->mPseudoType != nsCSSPseudoElements::ePseudo_before &&
|
|
|
|
aData->mPseudoType != nsCSSPseudoElements::ePseudo_after) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIStyleRule *rule =
|
|
|
|
mCompositor->GetAnimationRule(aData->mElement,
|
|
|
|
aData->mPseudoType,
|
|
|
|
mCascadeLevel);
|
|
|
|
if (rule) {
|
|
|
|
aData->mRuleWalker->Forward(rule);
|
|
|
|
aData->mRuleWalker->CurrentNode()->SetIsAnimationRule();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
EffectCompositor::AnimationStyleRuleProcessor::RulesMatching(
|
|
|
|
AnonBoxRuleProcessorData* aData)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef MOZ_XUL
|
|
|
|
void
|
|
|
|
EffectCompositor::AnimationStyleRuleProcessor::RulesMatching(
|
|
|
|
XULTreeRuleProcessorData* aData)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
size_t
|
|
|
|
EffectCompositor::AnimationStyleRuleProcessor::SizeOfExcludingThis(
|
|
|
|
MallocSizeOf aMallocSizeOf) const
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t
|
|
|
|
EffectCompositor::AnimationStyleRuleProcessor::SizeOfIncludingThis(
|
|
|
|
MallocSizeOf aMallocSizeOf) const
|
|
|
|
{
|
|
|
|
return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
|
|
|
|
}
|
|
|
|
|
2015-12-04 02:34:12 +03:00
|
|
|
} // namespace mozilla
|