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"
|
2016-09-04 10:34:21 +03:00
|
|
|
#include "mozilla/dom/KeyframeEffectReadOnly.h"
|
2016-10-05 08:26:25 +03:00
|
|
|
#include "mozilla/AnimationComparator.h"
|
2016-03-04 11:54:25 +03:00
|
|
|
#include "mozilla/AnimationPerformanceWarning.h"
|
2016-04-28 18:22:43 +03:00
|
|
|
#include "mozilla/AnimationTarget.h"
|
|
|
|
#include "mozilla/AnimationUtils.h"
|
2017-05-11 08:08:35 +03:00
|
|
|
#include "mozilla/AutoRestore.h"
|
2015-12-04 02:34:12 +03:00
|
|
|
#include "mozilla/EffectSet.h"
|
2016-01-06 05:04:04 +03:00
|
|
|
#include "mozilla/LayerAnimationInfo.h"
|
2017-02-13 06:21:33 +03:00
|
|
|
#include "mozilla/RestyleManager.h"
|
|
|
|
#include "mozilla/RestyleManagerInlines.h"
|
2017-05-19 10:58:47 +03:00
|
|
|
#include "mozilla/ServoBindings.h" // Servo_GetProperties_Overriding_Animation
|
2017-03-10 05:53:19 +03:00
|
|
|
#include "mozilla/ServoStyleSet.h"
|
2016-11-16 00:30:57 +03:00
|
|
|
#include "mozilla/StyleAnimationValue.h"
|
2017-03-27 13:43:04 +03:00
|
|
|
#include "mozilla/TypeTraits.h" // For Forward<>
|
2016-01-06 05:04:05 +03:00
|
|
|
#include "nsComputedDOMStyle.h" // nsComputedDOMStyle::GetPresShellForContent
|
2017-03-13 18:09:49 +03:00
|
|
|
#include "nsContentUtils.h"
|
2017-03-08 23:20:17 +03:00
|
|
|
#include "nsCSSPseudoElements.h"
|
2016-08-17 04:46:58 +03:00
|
|
|
#include "nsCSSPropertyIDSet.h"
|
2016-01-06 05:04:05 +03:00
|
|
|
#include "nsCSSProps.h"
|
2017-03-08 23:20:17 +03:00
|
|
|
#include "nsIAtom.h"
|
2016-01-06 05:04:05 +03:00
|
|
|
#include "nsIPresShell.h"
|
2017-02-10 05:42:27 +03:00
|
|
|
#include "nsIPresShellInlines.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-10-05 08:42:56 +03:00
|
|
|
#include <bitset>
|
2016-10-19 07:33:14 +03:00
|
|
|
#include <initializer_list>
|
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)
|
|
|
|
|
2016-12-02 09:34:13 +03:00
|
|
|
namespace {
|
|
|
|
enum class MatchForCompositor {
|
|
|
|
// This animation matches and should run on the compositor if possible.
|
|
|
|
Yes,
|
|
|
|
// This (not currently playing) animation matches and can be run on the
|
|
|
|
// compositor if there are other animations for this property that return
|
|
|
|
// 'Yes'.
|
|
|
|
IfNeeded,
|
|
|
|
// This animation does not match or can't be run on the compositor.
|
|
|
|
No,
|
|
|
|
// This animation does not match or can't be run on the compositor and,
|
|
|
|
// furthermore, its presence means we should not run any animations for this
|
|
|
|
// property on the compositor.
|
|
|
|
NoAndBlockThisProperty
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
static MatchForCompositor
|
|
|
|
IsMatchForCompositor(const KeyframeEffectReadOnly& aEffect,
|
|
|
|
nsCSSPropertyID aProperty,
|
|
|
|
const nsIFrame* aFrame)
|
|
|
|
{
|
|
|
|
const Animation* animation = aEffect.GetAnimation();
|
|
|
|
MOZ_ASSERT(animation);
|
|
|
|
|
|
|
|
if (!animation->IsRelevant()) {
|
|
|
|
return MatchForCompositor::No;
|
|
|
|
}
|
|
|
|
|
|
|
|
AnimationPerformanceWarning::Type warningType;
|
2016-12-02 04:13:06 +03:00
|
|
|
if (animation->ShouldBeSynchronizedWithMainThread(aProperty, aFrame,
|
|
|
|
warningType)) {
|
2016-12-02 09:34:13 +03:00
|
|
|
EffectCompositor::SetPerformanceWarning(
|
|
|
|
aFrame, aProperty,
|
|
|
|
AnimationPerformanceWarning(warningType));
|
2016-12-02 04:13:06 +03:00
|
|
|
// For a given |aFrame|, we don't want some animations of |aProperty| to
|
|
|
|
// run on the compositor and others to run on the main thread, so if any
|
|
|
|
// need to be synchronized with the main thread, run them all there.
|
2016-12-02 09:34:13 +03:00
|
|
|
return MatchForCompositor::NoAndBlockThisProperty;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!aEffect.HasEffectiveAnimationOfProperty(aProperty)) {
|
|
|
|
return MatchForCompositor::No;
|
|
|
|
}
|
|
|
|
|
2016-12-02 04:13:06 +03:00
|
|
|
return animation->IsPlaying()
|
|
|
|
? MatchForCompositor::Yes
|
|
|
|
: MatchForCompositor::IfNeeded;
|
2016-12-02 09:34:13 +03:00
|
|
|
}
|
|
|
|
|
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,
|
2016-08-17 04:37:48 +03:00
|
|
|
nsCSSPropertyID aProperty,
|
2015-12-10 00:28:10 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-12-02 04:21:11 +03:00
|
|
|
// First check for newly-started transform animations that should be
|
|
|
|
// synchronized with geometric animations. We need to do this before any
|
|
|
|
// other early returns (the one above is ok) since we can only check this
|
|
|
|
// state when the animation is newly-started.
|
|
|
|
if (aProperty == eCSSProperty_transform) {
|
|
|
|
PendingAnimationTracker* tracker =
|
|
|
|
aFrame->PresContext()->Document()->GetPendingAnimationTracker();
|
|
|
|
if (tracker) {
|
|
|
|
tracker->MarkAnimationsThatMightNeedSynchronization();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-05 08:53:58 +03:00
|
|
|
// If the property will be added to the animations level of the cascade but
|
|
|
|
// there is an !important rule for that property in the cascade then the
|
|
|
|
// animation will not be applied since the !important rule overrides it.
|
|
|
|
if (effects->PropertiesWithImportantRules().HasProperty(aProperty) &&
|
|
|
|
effects->PropertiesForAnimationsLevel().HasProperty(aProperty)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-12-10 00:28:10 +03:00
|
|
|
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.
|
2016-03-21 11:49:50 +03:00
|
|
|
Maybe<NonOwningAnimationTarget> pseudoElement =
|
2016-01-06 05:04:05 +03:00
|
|
|
EffectCompositor::GetAnimationElementAndPseudoForFrame(aFrame);
|
|
|
|
if (pseudoElement) {
|
2017-05-19 10:58:47 +03:00
|
|
|
StyleBackendType backend =
|
|
|
|
aFrame->StyleContext()->StyleSource().IsServoComputedValues()
|
|
|
|
? StyleBackendType::Servo
|
|
|
|
: StyleBackendType::Gecko;
|
|
|
|
EffectCompositor::MaybeUpdateCascadeResults(backend,
|
|
|
|
pseudoElement->mElement,
|
2016-03-21 11:49:50 +03:00
|
|
|
pseudoElement->mPseudoType,
|
2016-01-06 05:04:05 +03:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2016-07-29 08:58:32 +03:00
|
|
|
// Disable async animations if we have a rendering observer that
|
|
|
|
// depends on our content (svg masking, -moz-element etc) so that
|
|
|
|
// it gets updated correctly.
|
|
|
|
nsIContent* content = aFrame->GetContent();
|
|
|
|
while (content) {
|
|
|
|
if (content->HasRenderingObservers()) {
|
|
|
|
EffectCompositor::SetPerformanceWarning(
|
|
|
|
aFrame, aProperty,
|
|
|
|
AnimationPerformanceWarning(
|
|
|
|
AnimationPerformanceWarning::Type::HasRenderingObserver));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
content = content->GetParent();
|
|
|
|
}
|
|
|
|
|
2016-12-02 09:34:13 +03:00
|
|
|
bool foundRunningAnimations = false;
|
2015-12-04 02:34:12 +03:00
|
|
|
for (KeyframeEffectReadOnly* effect : *effects) {
|
2016-12-02 09:34:13 +03:00
|
|
|
MatchForCompositor matchResult =
|
|
|
|
IsMatchForCompositor(*effect, aProperty, aFrame);
|
2015-12-04 02:34:12 +03:00
|
|
|
|
2016-12-02 09:34:13 +03:00
|
|
|
if (matchResult == MatchForCompositor::NoAndBlockThisProperty) {
|
2016-12-02 04:13:06 +03:00
|
|
|
// For a given |aFrame|, we don't want some animations of |aProperty| to
|
|
|
|
// run on the compositor and others to run on the main thread, so if any
|
|
|
|
// need to be synchronized with the main thread, run them all there.
|
2015-12-10 00:28:10 +03:00
|
|
|
if (aMatches) {
|
|
|
|
aMatches->Clear();
|
|
|
|
}
|
|
|
|
return false;
|
2015-12-04 02:34:12 +03:00
|
|
|
}
|
|
|
|
|
2016-12-02 09:34:13 +03:00
|
|
|
if (matchResult == MatchForCompositor::No) {
|
2015-12-04 02:34:12 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-12-10 00:28:10 +03:00
|
|
|
if (aMatches) {
|
2016-12-02 09:34:13 +03:00
|
|
|
aMatches->AppendElement(effect->GetAnimation());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (matchResult == MatchForCompositor::Yes) {
|
|
|
|
foundRunningAnimations = true;
|
2015-12-10 00:28:10 +03:00
|
|
|
}
|
2015-12-04 02:34:12 +03:00
|
|
|
}
|
|
|
|
|
2016-12-02 09:34:13 +03:00
|
|
|
// If all animations we added were not currently playing animations, don't
|
|
|
|
// send them to the compositor.
|
|
|
|
if (aMatches && !foundRunningAnimations) {
|
|
|
|
aMatches->Clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
MOZ_ASSERT(!foundRunningAnimations || !aMatches || !aMatches->IsEmpty(),
|
2015-12-10 00:28:10 +03:00
|
|
|
"If return value is true, matches array should be non-empty");
|
2016-10-05 08:26:25 +03:00
|
|
|
|
2016-12-02 09:34:13 +03:00
|
|
|
if (aMatches && foundRunningAnimations) {
|
2016-10-05 08:26:25 +03:00
|
|
|
aMatches->Sort(AnimationPtrComparator<RefPtr<dom::Animation>>());
|
|
|
|
}
|
2016-12-02 09:34:13 +03:00
|
|
|
|
|
|
|
return foundRunningAnimations;
|
2015-12-10 00:28:10 +03:00
|
|
|
}
|
|
|
|
|
2016-01-13 01:54:53 +03:00
|
|
|
void
|
|
|
|
EffectCompositor::RequestRestyle(dom::Element* aElement,
|
2016-02-17 23:37:00 +03:00
|
|
|
CSSPseudoElementType aPseudoType,
|
2016-01-13 01:54:53 +03:00
|
|
|
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-10-12 09:58:53 +03:00
|
|
|
// Ignore animations on orphaned elements.
|
|
|
|
if (!aElement->IsInComposedDoc()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-01-13 01:54:53 +03:00
|
|
|
auto& elementsToRestyle = mElementsToRestyle[aCascadeLevel];
|
2016-03-21 11:49:50 +03:00
|
|
|
PseudoElementHashEntry::KeyType key = { aElement, aPseudoType };
|
2016-01-13 01:54:53 +03:00
|
|
|
|
2016-01-18 22:54:00 +03:00
|
|
|
if (aRestyleType == RestyleType::Throttled) {
|
|
|
|
if (!elementsToRestyle.Contains(key)) {
|
|
|
|
elementsToRestyle.Put(key, false);
|
|
|
|
}
|
2017-02-10 05:42:28 +03:00
|
|
|
mPresContext->PresShell()->SetNeedThrottledAnimationFlush();
|
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) {
|
2017-03-09 12:15:08 +03:00
|
|
|
mPresContext->RestyleManager()->IncrementAnimationGeneration();
|
2016-01-13 01:54:54 +03:00
|
|
|
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,
|
2016-02-17 23:37:00 +03:00
|
|
|
CSSPseudoElementType aPseudoType,
|
2016-01-13 01:54:54 +03:00
|
|
|
CascadeLevel aCascadeLevel)
|
|
|
|
{
|
|
|
|
if (!mPresContext) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
dom::Element* element = GetElementToRestyle(aElement, aPseudoType);
|
|
|
|
if (!element) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsRestyleHint hint = aCascadeLevel == CascadeLevel::Transitions ?
|
|
|
|
eRestyle_CSSTransitions :
|
|
|
|
eRestyle_CSSAnimations;
|
2017-01-24 10:27:56 +03:00
|
|
|
|
2017-03-10 05:53:19 +03:00
|
|
|
if (mPresContext->StyleSet()->IsServo()) {
|
|
|
|
MOZ_ASSERT(NS_IsMainThread(),
|
|
|
|
"Restyle request during restyling should be requested only on "
|
|
|
|
"the main-thread. e.g. after the parallel traversal");
|
2017-05-11 08:08:35 +03:00
|
|
|
if (ServoStyleSet::IsInServoTraversal() || mIsInPreTraverse) {
|
2017-04-17 09:22:28 +03:00
|
|
|
MOZ_ASSERT(hint == eRestyle_CSSAnimations ||
|
|
|
|
hint == eRestyle_CSSTransitions);
|
2017-03-10 05:53:19 +03:00
|
|
|
|
|
|
|
// We can't call Servo_NoteExplicitHints here since AtomicRefCell does not
|
|
|
|
// allow us mutate ElementData of the |aElement| in SequentialTask.
|
2017-04-17 09:22:28 +03:00
|
|
|
// Instead we call Servo_NoteExplicitHints for the element in PreTraverse()
|
2017-03-10 05:53:19 +03:00
|
|
|
// which will be called right before the second traversal that we do for
|
|
|
|
// updating CSS animations.
|
|
|
|
// In that case PreTraverse() will return true so that we know to do the
|
|
|
|
// second traversal so we don't need to post any restyle requests to the
|
|
|
|
// PresShell.
|
|
|
|
return;
|
2017-03-10 05:53:19 +03:00
|
|
|
} else {
|
2017-03-27 00:17:08 +03:00
|
|
|
MOZ_ASSERT(!mPresContext->RestyleManager()->IsInStyleRefresh());
|
2017-03-10 05:53:19 +03:00
|
|
|
}
|
2017-01-24 10:27:56 +03:00
|
|
|
}
|
2016-01-13 01:54:54 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-27 13:43:04 +03:00
|
|
|
template<typename StyleType>
|
2016-04-01 03:31:51 +03:00
|
|
|
void
|
2017-06-02 03:38:54 +03:00
|
|
|
EffectCompositor::UpdateEffectProperties(StyleType* aStyleType,
|
2017-03-27 13:43:04 +03:00
|
|
|
Element* aElement,
|
2016-04-01 03:31:51 +03:00
|
|
|
CSSPseudoElementType aPseudoType)
|
|
|
|
{
|
|
|
|
EffectSet* effectSet = EffectSet::GetEffectSet(aElement, aPseudoType);
|
|
|
|
if (!effectSet) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-03-27 13:43:04 +03:00
|
|
|
// Style context (Gecko) or computed values (Stylo) change might cause CSS
|
|
|
|
// cascade level, e.g removing !important, so we should update the cascading
|
|
|
|
// result.
|
2016-07-11 11:28:15 +03:00
|
|
|
effectSet->MarkCascadeNeedsUpdate();
|
|
|
|
|
2016-04-01 03:31:51 +03:00
|
|
|
for (KeyframeEffectReadOnly* effect : *effectSet) {
|
2017-06-02 03:38:54 +03:00
|
|
|
effect->UpdateProperties(aStyleType);
|
2016-04-01 03:31:51 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-13 01:54:53 +03:00
|
|
|
void
|
|
|
|
EffectCompositor::MaybeUpdateAnimationRule(dom::Element* aElement,
|
2016-02-17 23:37:00 +03:00
|
|
|
CSSPseudoElementType aPseudoType,
|
2016-07-11 11:28:14 +03:00
|
|
|
CascadeLevel aCascadeLevel,
|
|
|
|
nsStyleContext* aStyleContext)
|
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.
|
2017-05-19 10:58:47 +03:00
|
|
|
MaybeUpdateCascadeResults(StyleBackendType::Gecko,
|
|
|
|
aElement, aPseudoType,
|
|
|
|
aStyleContext);
|
2016-01-13 01:54:55 +03:00
|
|
|
|
2016-01-13 01:54:53 +03:00
|
|
|
auto& elementsToRestyle = mElementsToRestyle[aCascadeLevel];
|
2016-03-21 11:49:50 +03:00
|
|
|
PseudoElementHashEntry::KeyType key = { aElement, aPseudoType };
|
2016-01-13 01:54:53 +03:00
|
|
|
|
2017-02-27 05:34:46 +03:00
|
|
|
if (!elementsToRestyle.Contains(key)) {
|
2016-01-13 01:54:53 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-02-27 05:34:46 +03:00
|
|
|
ComposeAnimationRule(aElement, aPseudoType, aCascadeLevel);
|
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,
|
2016-02-17 23:37:00 +03:00
|
|
|
CSSPseudoElementType aPseudoType,
|
2016-07-11 11:28:14 +03:00
|
|
|
CascadeLevel aCascadeLevel,
|
|
|
|
nsStyleContext* aStyleContext)
|
2016-01-13 01:54:55 +03:00
|
|
|
{
|
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-02-24 10:01:12 +03:00
|
|
|
MOZ_ASSERT(mPresContext->RestyleManager()->IsGecko(),
|
|
|
|
"stylo: Servo-backed style system should not be using "
|
|
|
|
"EffectCompositor");
|
|
|
|
if (mPresContext->RestyleManager()->AsGecko()->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;
|
|
|
|
}
|
|
|
|
|
2016-07-11 11:28:14 +03:00
|
|
|
MaybeUpdateAnimationRule(aElement, aPseudoType, aCascadeLevel, aStyleContext);
|
2016-01-13 01:54:55 +03:00
|
|
|
|
2016-01-15 09:15:47 +03:00
|
|
|
#ifdef DEBUG
|
|
|
|
{
|
|
|
|
auto& elementsToRestyle = mElementsToRestyle[aCascadeLevel];
|
2016-03-21 11:49:50 +03:00
|
|
|
PseudoElementHashEntry::KeyType key = { aElement, aPseudoType };
|
2016-01-15 09:15:47 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-03-17 07:24:42 +03:00
|
|
|
return effectSet->AnimationRule(aCascadeLevel);
|
2016-01-13 01:54:55 +03:00
|
|
|
}
|
|
|
|
|
2017-01-24 10:27:56 +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());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2017-03-17 07:23:21 +03:00
|
|
|
bool
|
|
|
|
EffectCompositor::GetServoAnimationRule(
|
|
|
|
const dom::Element* aElement,
|
|
|
|
CSSPseudoElementType aPseudoType,
|
|
|
|
CascadeLevel aCascadeLevel,
|
2017-05-01 12:45:41 +03:00
|
|
|
RawServoAnimationValueMapBorrowedMut aAnimationValues)
|
2017-01-24 10:27:56 +03:00
|
|
|
{
|
2017-03-17 07:23:21 +03:00
|
|
|
MOZ_ASSERT(aAnimationValues);
|
2017-03-17 06:48:34 +03:00
|
|
|
MOZ_ASSERT(mPresContext && mPresContext->IsDynamic(),
|
|
|
|
"Should not be in print preview");
|
2017-01-24 10:27:56 +03:00
|
|
|
|
|
|
|
EffectSet* effectSet = EffectSet::GetEffectSet(aElement, aPseudoType);
|
|
|
|
if (!effectSet) {
|
2017-03-17 07:23:21 +03:00
|
|
|
return false;
|
2017-01-24 10:27:56 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Get a list of effects sorted by composite order.
|
|
|
|
nsTArray<KeyframeEffectReadOnly*> sortedEffectList(effectSet->Count());
|
|
|
|
for (KeyframeEffectReadOnly* effect : *effectSet) {
|
|
|
|
sortedEffectList.AppendElement(effect);
|
|
|
|
}
|
|
|
|
sortedEffectList.Sort(EffectCompositeOrderComparator());
|
|
|
|
|
|
|
|
// If multiple animations affect the same property, animations with higher
|
|
|
|
// composite order (priority) override or add or animations with lower
|
|
|
|
// priority.
|
Bug 1339704 - Part 2 - Filter out unwanted CascadeLevel::Transitions. r=birtles,hiro
We always call TElement::get_animation_rules() for both cascade levels while
there are animations or transitions, so we want to handle the following cases:
1. Have both CascadeLevel::Animations and CascadeLevel::Transitions:
* We use EffectSet::mPropertiesForAnimationsLevel to filter out
CascadeLevel::Transitions because transitions are suppressed when both
are presented.
2. Have CascadeLevel::Animations, but don't have CascadeLevel::Transitions:
* We also use EffectSet::mPropertiesForAnimationsLevel to filter out
the unwanted CascadeLevel::Transitions.
3. Don't Have CascadeLevel::Animations, but have CascadeLevel::Transitions:
* EffectSet::mPropertiesForAnimationsLevel doesn't work for this case.
In Gecko, mElementsToRestyle can help us to filter out the unwanted
CascadeLevel::Animations, However, mElementsToRestyle is cleared in
Pre-Traversal, so now we rely on the cascade ordering of transitions to
override animations. I think we still need to optimize this eventually.
4. No animations:
* EffectSet helps us to check if there is any animations/transitions.
Therefore, we need to call MaybeUpdateCascadeResults(), which updates
mPropertiesForAnimationsLevel, in Pre-Traversal.
MozReview-Commit-ID: IHYw56EX7Ta
--HG--
extra : rebase_source : 4d5eb1335cdce9bf54c9646db1fba72ca3f2c70b
2017-03-13 16:09:50 +03:00
|
|
|
const nsCSSPropertyIDSet propertiesToSkip =
|
|
|
|
aCascadeLevel == CascadeLevel::Animations
|
|
|
|
? effectSet->PropertiesForAnimationsLevel().Inverse()
|
|
|
|
: effectSet->PropertiesForAnimationsLevel();
|
2017-01-24 10:27:56 +03:00
|
|
|
for (KeyframeEffectReadOnly* effect : sortedEffectList) {
|
2017-03-17 07:23:21 +03:00
|
|
|
effect->GetAnimation()->ComposeStyle(*aAnimationValues, propertiesToSkip);
|
2017-01-24 10:27:56 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
MOZ_ASSERT(effectSet == EffectSet::GetEffectSet(aElement, aPseudoType),
|
|
|
|
"EffectSet should not change while composing style");
|
|
|
|
|
2017-03-17 07:23:21 +03:00
|
|
|
return true;
|
2017-01-24 10:27:56 +03:00
|
|
|
}
|
|
|
|
|
2016-01-13 01:54:54 +03:00
|
|
|
/* static */ dom::Element*
|
|
|
|
EffectCompositor::GetElementToRestyle(dom::Element* aElement,
|
2016-02-17 23:37:00 +03:00
|
|
|
CSSPseudoElementType aPseudoType)
|
2016-01-13 01:54:54 +03:00
|
|
|
{
|
2016-02-17 01:07:00 +03:00
|
|
|
if (aPseudoType == CSSPseudoElementType::NotPseudo) {
|
2016-01-13 01:54:54 +03:00
|
|
|
return aElement;
|
|
|
|
}
|
|
|
|
|
2016-02-17 01:07:00 +03:00
|
|
|
if (aPseudoType == CSSPseudoElementType::before) {
|
2017-04-19 13:53:57 +03:00
|
|
|
return nsLayoutUtils::GetBeforePseudo(aElement);
|
2016-01-13 01:54:54 +03:00
|
|
|
}
|
2017-04-19 13:53:57 +03:00
|
|
|
|
|
|
|
if (aPseudoType == CSSPseudoElementType::after) {
|
|
|
|
return nsLayoutUtils::GetAfterPseudo(aElement);
|
2016-01-13 01:54:54 +03:00
|
|
|
}
|
2017-04-19 13:53:57 +03:00
|
|
|
|
|
|
|
NS_NOTREACHED("Should not try to get the element to restyle for a pseudo "
|
|
|
|
"other that :before or :after");
|
|
|
|
return nullptr;
|
2016-01-13 01:54:54 +03:00
|
|
|
}
|
|
|
|
|
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.
|
2016-03-21 11:49:50 +03:00
|
|
|
nsTArray<PseudoElementHashEntry::KeyType> elementsToRestyle(
|
|
|
|
elementSet.Count());
|
2016-01-13 01:54:55 +03:00
|
|
|
for (auto iter = elementSet.Iter(); !iter.Done(); iter.Next()) {
|
2016-10-12 09:58:53 +03:00
|
|
|
// Skip animations on elements that have been orphaned since they
|
|
|
|
// requested a restyle.
|
|
|
|
if (iter.Key().mElement->IsInComposedDoc()) {
|
|
|
|
elementsToRestyle.AppendElement(iter.Key());
|
|
|
|
}
|
2016-01-13 01:54:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
for (auto& pseudoElem : elementsToRestyle) {
|
2017-05-19 10:58:47 +03:00
|
|
|
MaybeUpdateCascadeResults(StyleBackendType::Gecko,
|
|
|
|
pseudoElem.mElement,
|
2016-07-11 11:28:19 +03:00
|
|
|
pseudoElem.mPseudoType,
|
|
|
|
nullptr);
|
2016-01-13 01:54:55 +03:00
|
|
|
|
|
|
|
ComposeAnimationRule(pseudoElem.mElement,
|
|
|
|
pseudoElem.mPseudoType,
|
2017-02-27 05:34:46 +03:00
|
|
|
cascadeLevel);
|
2016-01-13 01:54:55 +03:00
|
|
|
|
|
|
|
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,
|
2016-08-17 04:37:48 +03:00
|
|
|
nsCSSPropertyID aProperty)
|
2015-12-10 00:28:10 +03:00
|
|
|
{
|
|
|
|
return FindAnimationsForCompositor(aFrame, aProperty, nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* static */ nsTArray<RefPtr<dom::Animation>>
|
|
|
|
EffectCompositor::GetAnimationsForCompositor(const nsIFrame* aFrame,
|
2016-08-17 04:37:48 +03:00
|
|
|
nsCSSPropertyID aProperty)
|
2015-12-10 00:28:10 +03:00
|
|
|
{
|
|
|
|
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,
|
2016-08-17 04:37:48 +03:00
|
|
|
nsCSSPropertyID aProperty)
|
2016-01-13 01:54:56 +03:00
|
|
|
{
|
|
|
|
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
|
2017-05-19 10:58:47 +03:00
|
|
|
EffectCompositor::MaybeUpdateCascadeResults(StyleBackendType aBackendType,
|
|
|
|
Element* aElement,
|
2016-02-17 23:37:00 +03:00
|
|
|
CSSPseudoElementType aPseudoType,
|
2016-01-06 05:04:05 +03:00
|
|
|
nsStyleContext* aStyleContext)
|
|
|
|
{
|
|
|
|
EffectSet* effects = EffectSet::GetEffectSet(aElement, aPseudoType);
|
|
|
|
if (!effects || !effects->CascadeNeedsUpdate()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-05-19 10:58:47 +03:00
|
|
|
UpdateCascadeResults(aBackendType, *effects, aElement, aPseudoType,
|
|
|
|
aStyleContext);
|
2016-01-06 05:04:05 +03:00
|
|
|
|
|
|
|
MOZ_ASSERT(!effects->CascadeNeedsUpdate(), "Failed to update cascade state");
|
|
|
|
}
|
|
|
|
|
2016-03-21 11:49:50 +03:00
|
|
|
/* static */ Maybe<NonOwningAnimationTarget>
|
2016-01-06 05:04:04 +03:00
|
|
|
EffectCompositor::GetAnimationElementAndPseudoForFrame(const nsIFrame* aFrame)
|
|
|
|
{
|
|
|
|
// Always return the same object to benefit from return-value optimization.
|
2016-03-21 11:49:50 +03:00
|
|
|
Maybe<NonOwningAnimationTarget> result;
|
2016-01-06 05:04:04 +03:00
|
|
|
|
2016-06-01 10:24:34 +03:00
|
|
|
CSSPseudoElementType pseudoType =
|
|
|
|
aFrame->StyleContext()->GetPseudoType();
|
|
|
|
|
|
|
|
if (pseudoType != CSSPseudoElementType::NotPseudo &&
|
|
|
|
pseudoType != CSSPseudoElementType::before &&
|
|
|
|
pseudoType != CSSPseudoElementType::after) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-01-06 05:04:04 +03:00
|
|
|
nsIContent* content = aFrame->GetContent();
|
|
|
|
if (!content) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-06-01 10:24:34 +03:00
|
|
|
if (pseudoType == CSSPseudoElementType::before ||
|
|
|
|
pseudoType == CSSPseudoElementType::after) {
|
2016-01-06 05:04:04 +03:00
|
|
|
content = content->GetParent();
|
|
|
|
if (!content) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!content->IsElement()) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-03-21 11:49:50 +03:00
|
|
|
result.emplace(content->AsElement(), pseudoType);
|
2016-01-06 05:04:04 +03:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-01-06 05:04:06 +03:00
|
|
|
/* static */ void
|
|
|
|
EffectCompositor::ComposeAnimationRule(dom::Element* aElement,
|
2016-02-17 23:37:00 +03:00
|
|
|
CSSPseudoElementType aPseudoType,
|
2017-02-27 05:34:46 +03:00
|
|
|
CascadeLevel aCascadeLevel)
|
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");
|
|
|
|
|
2016-10-05 08:42:56 +03:00
|
|
|
// Get a list of effects sorted by composite order.
|
|
|
|
nsTArray<KeyframeEffectReadOnly*> sortedEffectList(effects->Count());
|
2016-01-06 05:04:06 +03:00
|
|
|
for (KeyframeEffectReadOnly* effect : *effects) {
|
2016-10-05 08:42:56 +03:00
|
|
|
sortedEffectList.AppendElement(effect);
|
2016-01-06 05:04:06 +03:00
|
|
|
}
|
|
|
|
sortedEffectList.Sort(EffectCompositeOrderComparator());
|
|
|
|
|
2017-03-17 07:24:42 +03:00
|
|
|
RefPtr<AnimValuesStyleRule>& animRule = effects->AnimationRule(aCascadeLevel);
|
|
|
|
animRule = nullptr;
|
2016-01-06 05:04:06 +03:00
|
|
|
|
2016-10-05 08:42:56 +03:00
|
|
|
// If multiple animations affect the same property, animations with higher
|
|
|
|
// composite order (priority) override or add or animations with lower
|
|
|
|
// priority except properties in propertiesToSkip.
|
|
|
|
const nsCSSPropertyIDSet& propertiesToSkip =
|
|
|
|
aCascadeLevel == CascadeLevel::Animations
|
2017-03-14 06:30:20 +03:00
|
|
|
? effects->PropertiesForAnimationsLevel().Inverse()
|
2016-10-05 08:42:56 +03:00
|
|
|
: effects->PropertiesForAnimationsLevel();
|
|
|
|
for (KeyframeEffectReadOnly* effect : sortedEffectList) {
|
2017-03-08 23:20:17 +03:00
|
|
|
effect->GetAnimation()->WillComposeStyle();
|
2017-03-17 07:24:42 +03:00
|
|
|
effect->GetAnimation()->ComposeStyle(animRule, propertiesToSkip);
|
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-06 05:04:06 +03:00
|
|
|
}
|
|
|
|
|
2017-05-19 10:58:47 +03:00
|
|
|
/* static */ nsCSSPropertyIDSet
|
|
|
|
EffectCompositor::GetOverriddenProperties(StyleBackendType aBackendType,
|
2016-01-06 05:04:05 +03:00
|
|
|
EffectSet& aEffectSet,
|
2017-05-19 10:58:47 +03:00
|
|
|
Element* aElement,
|
|
|
|
CSSPseudoElementType aPseudoType,
|
|
|
|
nsStyleContext* aStyleContext)
|
2016-01-06 05:04:04 +03:00
|
|
|
{
|
2017-05-19 10:58:47 +03:00
|
|
|
MOZ_ASSERT(aBackendType != StyleBackendType::Servo || aElement,
|
|
|
|
"Should have an element to get style data from if we are using"
|
|
|
|
" the Servo backend");
|
|
|
|
|
|
|
|
nsCSSPropertyIDSet result;
|
|
|
|
|
|
|
|
Element* elementToRestyle = GetElementToRestyle(aElement, aPseudoType);
|
|
|
|
if (aBackendType == StyleBackendType::Gecko && !aStyleContext) {
|
|
|
|
if (elementToRestyle) {
|
|
|
|
nsIFrame* frame = elementToRestyle->GetPrimaryFrame();
|
|
|
|
if (frame) {
|
|
|
|
aStyleContext = frame->StyleContext();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!aStyleContext) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
} else if (aBackendType == StyleBackendType::Servo && !elementToRestyle) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-08-17 04:37:48 +03:00
|
|
|
AutoTArray<nsCSSPropertyID, LayerAnimationInfo::kRecords> propertiesToTrack;
|
2016-01-06 05:04:05 +03:00
|
|
|
{
|
2016-08-17 04:46:58 +03:00
|
|
|
nsCSSPropertyIDSet propertiesToTrackAsSet;
|
2016-01-06 05:04:05 +03:00
|
|
|
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()) {
|
2017-05-19 10:58:47 +03:00
|
|
|
return result;
|
2016-01-06 05:04:05 +03:00
|
|
|
}
|
|
|
|
|
2017-05-19 10:58:47 +03:00
|
|
|
switch (aBackendType) {
|
|
|
|
case StyleBackendType::Servo:
|
|
|
|
Servo_GetProperties_Overriding_Animation(elementToRestyle,
|
|
|
|
&propertiesToTrack,
|
|
|
|
&result);
|
|
|
|
break;
|
|
|
|
case StyleBackendType::Gecko:
|
|
|
|
nsRuleNode::ComputePropertiesOverridingAnimation(propertiesToTrack,
|
|
|
|
aStyleContext,
|
|
|
|
result);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
MOZ_ASSERT_UNREACHABLE("Unsupported style backend");
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
2016-01-06 05:04:04 +03:00
|
|
|
}
|
|
|
|
|
2016-01-06 05:04:05 +03:00
|
|
|
/* static */ void
|
2017-05-19 10:58:47 +03:00
|
|
|
EffectCompositor::UpdateCascadeResults(StyleBackendType aBackendType,
|
|
|
|
EffectSet& aEffectSet,
|
2016-01-06 05:04:05 +03:00
|
|
|
Element* aElement,
|
2016-02-17 23:37:00 +03:00
|
|
|
CSSPseudoElementType aPseudoType,
|
2016-01-06 05:04:05 +03:00
|
|
|
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.
|
2016-10-05 08:42:56 +03:00
|
|
|
nsTArray<KeyframeEffectReadOnly*> sortedEffectList(aEffectSet.Count());
|
2016-01-06 05:04:05 +03:00
|
|
|
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.
|
2017-05-19 10:58:47 +03:00
|
|
|
nsCSSPropertyIDSet overriddenProperties =
|
|
|
|
GetOverriddenProperties(aBackendType,
|
|
|
|
aEffectSet,
|
|
|
|
aElement, aPseudoType,
|
|
|
|
aStyleContext);
|
2016-01-06 05:04:05 +03:00
|
|
|
|
2016-10-05 08:42:56 +03:00
|
|
|
// Returns a bitset the represents which properties from
|
|
|
|
// LayerAnimationInfo::sRecords are present in |aPropertySet|.
|
|
|
|
auto compositorPropertiesInSet =
|
|
|
|
[](nsCSSPropertyIDSet& aPropertySet) ->
|
|
|
|
std::bitset<LayerAnimationInfo::kRecords> {
|
|
|
|
std::bitset<LayerAnimationInfo::kRecords> result;
|
|
|
|
for (size_t i = 0; i < LayerAnimationInfo::kRecords; i++) {
|
|
|
|
if (aPropertySet.HasProperty(
|
|
|
|
LayerAnimationInfo::sRecords[i].mProperty)) {
|
|
|
|
result.set(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
};
|
|
|
|
|
|
|
|
nsCSSPropertyIDSet& propertiesWithImportantRules =
|
|
|
|
aEffectSet.PropertiesWithImportantRules();
|
|
|
|
nsCSSPropertyIDSet& propertiesForAnimationsLevel =
|
|
|
|
aEffectSet.PropertiesForAnimationsLevel();
|
|
|
|
|
|
|
|
// Record which compositor-animatable properties were originally set so we can
|
|
|
|
// compare for changes later.
|
|
|
|
std::bitset<LayerAnimationInfo::kRecords>
|
|
|
|
prevCompositorPropertiesWithImportantRules =
|
|
|
|
compositorPropertiesInSet(propertiesWithImportantRules);
|
|
|
|
std::bitset<LayerAnimationInfo::kRecords>
|
|
|
|
prevCompositorPropertiesForAnimationsLevel =
|
|
|
|
compositorPropertiesInSet(propertiesForAnimationsLevel);
|
|
|
|
|
|
|
|
propertiesWithImportantRules.Empty();
|
|
|
|
propertiesForAnimationsLevel.Empty();
|
|
|
|
|
|
|
|
bool hasCompositorPropertiesForTransition = false;
|
2016-01-06 05:04:05 +03:00
|
|
|
|
2016-10-05 08:48:05 +03:00
|
|
|
for (const KeyframeEffectReadOnly* effect : sortedEffectList) {
|
2016-01-06 05:04:05 +03:00
|
|
|
MOZ_ASSERT(effect->GetAnimation(),
|
|
|
|
"Effects on a target element should have an Animation");
|
2016-10-05 08:42:56 +03:00
|
|
|
CascadeLevel cascadeLevel = effect->GetAnimation()->CascadeLevel();
|
|
|
|
|
2016-10-05 08:48:05 +03:00
|
|
|
for (const AnimationProperty& prop : effect->Properties()) {
|
2016-10-05 08:42:56 +03:00
|
|
|
if (overriddenProperties.HasProperty(prop.mProperty)) {
|
|
|
|
propertiesWithImportantRules.AddProperty(prop.mProperty);
|
|
|
|
}
|
|
|
|
if (cascadeLevel == EffectCompositor::CascadeLevel::Animations) {
|
|
|
|
propertiesForAnimationsLevel.AddProperty(prop.mProperty);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nsCSSProps::PropHasFlags(prop.mProperty,
|
|
|
|
CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR) &&
|
|
|
|
cascadeLevel == EffectCompositor::CascadeLevel::Transitions) {
|
|
|
|
hasCompositorPropertiesForTransition = true;
|
|
|
|
}
|
2016-01-06 05:04:05 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
aEffectSet.MarkCascadeUpdated();
|
|
|
|
|
2017-03-13 18:09:49 +03:00
|
|
|
nsPresContext* presContext = nsContentUtils::GetContextForContent(aElement);
|
2016-10-05 08:42:56 +03:00
|
|
|
if (!presContext) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If properties for compositor are newly overridden by !important rules, or
|
|
|
|
// released from being overridden by !important rules, we need to update
|
|
|
|
// layers for animations level because it's a trigger to send animations to
|
|
|
|
// the compositor or pull animations back from the compositor.
|
|
|
|
if (prevCompositorPropertiesWithImportantRules !=
|
|
|
|
compositorPropertiesInSet(propertiesWithImportantRules)) {
|
|
|
|
presContext->EffectCompositor()->
|
|
|
|
RequestRestyle(aElement, aPseudoType,
|
|
|
|
EffectCompositor::RestyleType::Layer,
|
|
|
|
EffectCompositor::CascadeLevel::Animations);
|
|
|
|
}
|
|
|
|
// If we have transition properties for compositor and if the same propery
|
|
|
|
// for animations level is newly added or removed, we need to update layers
|
|
|
|
// for transitions level because composite order has been changed now.
|
|
|
|
if (hasCompositorPropertiesForTransition &&
|
|
|
|
prevCompositorPropertiesForAnimationsLevel !=
|
|
|
|
compositorPropertiesInSet(propertiesForAnimationsLevel)) {
|
|
|
|
presContext->EffectCompositor()->
|
|
|
|
RequestRestyle(aElement, aPseudoType,
|
|
|
|
EffectCompositor::RestyleType::Layer,
|
|
|
|
EffectCompositor::CascadeLevel::Transitions);
|
2016-01-06 05:04:05 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-04 00:36:36 +03:00
|
|
|
/* static */ void
|
2016-03-04 11:54:25 +03:00
|
|
|
EffectCompositor::SetPerformanceWarning(
|
|
|
|
const nsIFrame *aFrame,
|
2016-08-17 04:37:48 +03:00
|
|
|
nsCSSPropertyID aProperty,
|
2016-03-04 11:54:25 +03:00
|
|
|
const AnimationPerformanceWarning& aWarning)
|
2016-03-04 00:36:36 +03:00
|
|
|
{
|
|
|
|
EffectSet* effects = EffectSet::GetEffectSet(aFrame);
|
|
|
|
if (!effects) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (KeyframeEffectReadOnly* effect : *effects) {
|
2016-03-04 11:54:25 +03:00
|
|
|
effect->SetPerformanceWarning(aProperty, aWarning);
|
2016-03-04 00:36:36 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-10 05:53:19 +03:00
|
|
|
bool
|
2017-05-19 11:16:41 +03:00
|
|
|
EffectCompositor::PreTraverse(AnimationRestyleType aRestyleType)
|
2017-04-04 14:12:54 +03:00
|
|
|
{
|
2017-05-19 11:16:41 +03:00
|
|
|
return PreTraverseInSubtree(nullptr, aRestyleType);
|
2017-04-04 14:12:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2017-05-19 11:16:41 +03:00
|
|
|
EffectCompositor::PreTraverseInSubtree(Element* aRoot,
|
|
|
|
AnimationRestyleType aRestyleType)
|
2017-03-08 23:20:17 +03:00
|
|
|
{
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
MOZ_ASSERT(mPresContext->RestyleManager()->IsServo());
|
|
|
|
|
2017-05-11 08:08:35 +03:00
|
|
|
AutoRestore<bool> guard(mIsInPreTraverse);
|
|
|
|
mIsInPreTraverse = true;
|
|
|
|
|
2017-05-19 11:16:41 +03:00
|
|
|
// We need to force flush all throttled animations if we also have
|
|
|
|
// non-animation restyles (since we'll want the up-to-date animation style
|
|
|
|
// when we go to process them so we can trigger transitions correctly), and
|
|
|
|
// if we are currently flushing all throttled animation restyles.
|
|
|
|
bool flushThrottledRestyles =
|
|
|
|
(aRoot && aRoot->HasDirtyDescendantsForServo()) ||
|
|
|
|
aRestyleType == AnimationRestyleType::Full;
|
2017-05-17 06:41:43 +03:00
|
|
|
|
2017-04-28 09:00:13 +03:00
|
|
|
using ElementsToRestyleIterType =
|
|
|
|
nsDataHashtable<PseudoElementHashEntry, bool>::Iterator;
|
|
|
|
auto getNeededRestyleTarget = [&](const ElementsToRestyleIterType& aIter)
|
|
|
|
-> NonOwningAnimationTarget {
|
|
|
|
NonOwningAnimationTarget returnTarget;
|
|
|
|
|
2017-05-17 06:41:43 +03:00
|
|
|
// If aIter.Data() is false, the element only requested a throttled
|
|
|
|
// (skippable) restyle, so we can skip it if flushThrottledRestyles is not
|
|
|
|
// true.
|
|
|
|
if (!flushThrottledRestyles && !aIter.Data()) {
|
2017-04-28 09:00:13 +03:00
|
|
|
return returnTarget;
|
|
|
|
}
|
|
|
|
|
|
|
|
const NonOwningAnimationTarget& target = aIter.Key();
|
|
|
|
|
|
|
|
// Ignore restyles that aren't in the flattened tree subtree rooted at
|
|
|
|
// aRoot.
|
|
|
|
if (aRoot &&
|
|
|
|
!nsContentUtils::ContentIsFlattenedTreeDescendantOf(target.mElement,
|
|
|
|
aRoot)) {
|
|
|
|
return returnTarget;
|
|
|
|
}
|
|
|
|
|
|
|
|
returnTarget = target;
|
|
|
|
return returnTarget;
|
|
|
|
};
|
|
|
|
|
2017-03-10 05:53:19 +03:00
|
|
|
bool foundElementsNeedingRestyle = false;
|
2017-04-28 09:00:13 +03:00
|
|
|
|
|
|
|
nsTArray<NonOwningAnimationTarget> elementsWithCascadeUpdates;
|
2017-04-17 09:22:28 +03:00
|
|
|
for (size_t i = 0; i < kCascadeLevelCount; ++i) {
|
|
|
|
CascadeLevel cascadeLevel = CascadeLevel(i);
|
2017-04-28 09:00:13 +03:00
|
|
|
auto& elementSet = mElementsToRestyle[cascadeLevel];
|
|
|
|
for (auto iter = elementSet.Iter(); !iter.Done(); iter.Next()) {
|
|
|
|
const NonOwningAnimationTarget& target = getNeededRestyleTarget(iter);
|
|
|
|
if (!target.mElement) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
EffectSet* effects = EffectSet::GetEffectSet(target.mElement,
|
|
|
|
target.mPseudoType);
|
|
|
|
if (!effects || !effects->CascadeNeedsUpdate()) {
|
2017-03-08 23:20:17 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-04-28 09:00:13 +03:00
|
|
|
elementsWithCascadeUpdates.AppendElement(target);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const NonOwningAnimationTarget& target: elementsWithCascadeUpdates) {
|
2017-05-11 08:26:07 +03:00
|
|
|
MaybeUpdateCascadeResults(StyleBackendType::Servo,
|
|
|
|
target.mElement,
|
|
|
|
target.mPseudoType,
|
|
|
|
nullptr);
|
2017-04-28 09:00:13 +03:00
|
|
|
}
|
|
|
|
elementsWithCascadeUpdates.Clear();
|
2017-03-10 05:53:19 +03:00
|
|
|
|
2017-04-28 09:00:13 +03:00
|
|
|
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()) {
|
|
|
|
const NonOwningAnimationTarget& target = getNeededRestyleTarget(iter);
|
|
|
|
if (!target.mElement) {
|
2017-04-04 14:12:54 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-03-10 05:53:19 +03:00
|
|
|
// We need to post restyle hints even if the target is not in EffectSet to
|
|
|
|
// ensure the final restyling for removed animations.
|
|
|
|
// We can't call PostRestyleEvent directly here since we are still in the
|
|
|
|
// middle of the servo traversal.
|
|
|
|
mPresContext->RestyleManager()->AsServo()->
|
2017-04-17 09:22:28 +03:00
|
|
|
PostRestyleEventForAnimations(target.mElement,
|
2017-06-08 04:22:25 +03:00
|
|
|
target.mPseudoType,
|
2017-04-17 09:22:28 +03:00
|
|
|
cascadeLevel == CascadeLevel::Transitions
|
|
|
|
? eRestyle_CSSTransitions
|
|
|
|
: eRestyle_CSSAnimations);
|
2017-03-27 00:17:08 +03:00
|
|
|
|
2017-03-10 05:53:19 +03:00
|
|
|
foundElementsNeedingRestyle = true;
|
|
|
|
|
2017-04-28 09:00:13 +03:00
|
|
|
EffectSet* effects = EffectSet::GetEffectSet(target.mElement,
|
|
|
|
target.mPseudoType);
|
2017-03-08 23:20:17 +03:00
|
|
|
if (!effects) {
|
2017-03-10 05:53:19 +03:00
|
|
|
// Drop EffectSets that have been destroyed.
|
2017-03-08 23:20:17 +03:00
|
|
|
iter.Remove();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (KeyframeEffectReadOnly* effect : *effects) {
|
|
|
|
effect->GetAnimation()->WillComposeStyle();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove the element from the list of elements to restyle since we are
|
|
|
|
// about to restyle it.
|
|
|
|
iter.Remove();
|
|
|
|
}
|
|
|
|
}
|
2017-03-10 05:53:19 +03:00
|
|
|
return foundElementsNeedingRestyle;
|
2017-03-08 23:20:17 +03:00
|
|
|
}
|
|
|
|
|
2017-03-10 05:53:19 +03:00
|
|
|
bool
|
2017-05-14 19:39:22 +03:00
|
|
|
EffectCompositor::PreTraverse(dom::Element* aElement,
|
|
|
|
CSSPseudoElementType aPseudoType)
|
2017-03-08 23:20:17 +03:00
|
|
|
{
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
MOZ_ASSERT(mPresContext->RestyleManager()->IsServo());
|
|
|
|
|
2017-03-10 05:53:19 +03:00
|
|
|
bool found = false;
|
2017-05-14 19:39:22 +03:00
|
|
|
if (aPseudoType != CSSPseudoElementType::NotPseudo &&
|
|
|
|
aPseudoType != CSSPseudoElementType::before &&
|
|
|
|
aPseudoType != CSSPseudoElementType::after) {
|
2017-03-10 05:53:19 +03:00
|
|
|
return found;
|
2017-03-08 23:20:17 +03:00
|
|
|
}
|
|
|
|
|
2017-05-11 08:08:35 +03:00
|
|
|
AutoRestore<bool> guard(mIsInPreTraverse);
|
|
|
|
mIsInPreTraverse = true;
|
|
|
|
|
2017-05-14 19:39:22 +03:00
|
|
|
PseudoElementHashEntry::KeyType key = { aElement, aPseudoType };
|
2017-04-17 09:22:28 +03:00
|
|
|
|
2017-05-19 11:16:41 +03:00
|
|
|
// We need to flush all throttled animation restyles too if we also have
|
|
|
|
// non-animation restyles (since we'll want the up-to-date animation style
|
|
|
|
// when we go to process them so we can trigger transitions correctly).
|
2017-05-17 06:41:43 +03:00
|
|
|
Element* elementToRestyle = GetElementToRestyle(aElement, aPseudoType);
|
|
|
|
bool flushThrottledRestyles = elementToRestyle &&
|
|
|
|
elementToRestyle->HasDirtyDescendantsForServo();
|
|
|
|
|
2017-04-17 09:22:28 +03:00
|
|
|
for (size_t i = 0; i < kCascadeLevelCount; ++i) {
|
|
|
|
CascadeLevel cascadeLevel = CascadeLevel(i);
|
|
|
|
auto& elementSet = mElementsToRestyle[cascadeLevel];
|
|
|
|
|
2017-05-17 06:41:43 +03:00
|
|
|
// Skip if we don't have a restyle, or if we only have a throttled
|
|
|
|
// (skippable) restyle and we're not required to flush throttled restyles.
|
|
|
|
bool hasUnthrottledRestyle = false;
|
|
|
|
if (!elementSet.Get(key, &hasUnthrottledRestyle) ||
|
|
|
|
(!flushThrottledRestyles && !hasUnthrottledRestyle)) {
|
2017-03-08 23:20:17 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-03-10 05:53:19 +03:00
|
|
|
mPresContext->RestyleManager()->AsServo()->
|
2017-04-17 09:22:28 +03:00
|
|
|
PostRestyleEventForAnimations(aElement,
|
2017-06-08 04:22:25 +03:00
|
|
|
aPseudoType,
|
2017-04-17 09:22:28 +03:00
|
|
|
cascadeLevel == CascadeLevel::Transitions
|
|
|
|
? eRestyle_CSSTransitions
|
|
|
|
: eRestyle_CSSAnimations);
|
2017-03-10 05:53:19 +03:00
|
|
|
|
2017-05-14 19:39:22 +03:00
|
|
|
EffectSet* effects = EffectSet::GetEffectSet(aElement, aPseudoType);
|
2017-03-08 23:20:17 +03:00
|
|
|
if (effects) {
|
2017-05-11 08:26:07 +03:00
|
|
|
MaybeUpdateCascadeResults(StyleBackendType::Servo,
|
|
|
|
aElement, aPseudoType,
|
|
|
|
nullptr);
|
Bug 1339704 - Part 2 - Filter out unwanted CascadeLevel::Transitions. r=birtles,hiro
We always call TElement::get_animation_rules() for both cascade levels while
there are animations or transitions, so we want to handle the following cases:
1. Have both CascadeLevel::Animations and CascadeLevel::Transitions:
* We use EffectSet::mPropertiesForAnimationsLevel to filter out
CascadeLevel::Transitions because transitions are suppressed when both
are presented.
2. Have CascadeLevel::Animations, but don't have CascadeLevel::Transitions:
* We also use EffectSet::mPropertiesForAnimationsLevel to filter out
the unwanted CascadeLevel::Transitions.
3. Don't Have CascadeLevel::Animations, but have CascadeLevel::Transitions:
* EffectSet::mPropertiesForAnimationsLevel doesn't work for this case.
In Gecko, mElementsToRestyle can help us to filter out the unwanted
CascadeLevel::Animations, However, mElementsToRestyle is cleared in
Pre-Traversal, so now we rely on the cascade ordering of transitions to
override animations. I think we still need to optimize this eventually.
4. No animations:
* EffectSet helps us to check if there is any animations/transitions.
Therefore, we need to call MaybeUpdateCascadeResults(), which updates
mPropertiesForAnimationsLevel, in Pre-Traversal.
MozReview-Commit-ID: IHYw56EX7Ta
--HG--
extra : rebase_source : 4d5eb1335cdce9bf54c9646db1fba72ca3f2c70b
2017-03-13 16:09:50 +03:00
|
|
|
|
2017-03-08 23:20:17 +03:00
|
|
|
for (KeyframeEffectReadOnly* effect : *effects) {
|
|
|
|
effect->GetAnimation()->WillComposeStyle();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-17 09:22:28 +03:00
|
|
|
elementSet.Remove(key);
|
2017-03-10 05:53:19 +03:00
|
|
|
found = true;
|
2017-03-08 23:20:17 +03:00
|
|
|
}
|
2017-03-10 05:53:19 +03:00
|
|
|
return found;
|
2017-03-08 23:20:17 +03:00
|
|
|
}
|
|
|
|
|
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,
|
2016-02-17 01:07:00 +03:00
|
|
|
CSSPseudoElementType::NotPseudo,
|
2016-07-11 11:28:14 +03:00
|
|
|
mCascadeLevel,
|
|
|
|
nullptr);
|
2016-01-15 09:15:47 +03:00
|
|
|
if (rule) {
|
|
|
|
aData->mRuleWalker->Forward(rule);
|
|
|
|
aData->mRuleWalker->CurrentNode()->SetIsAnimationRule();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
EffectCompositor::AnimationStyleRuleProcessor::RulesMatching(
|
|
|
|
PseudoElementRuleProcessorData* aData)
|
|
|
|
{
|
2016-02-17 01:07:00 +03:00
|
|
|
if (aData->mPseudoType != CSSPseudoElementType::before &&
|
|
|
|
aData->mPseudoType != CSSPseudoElementType::after) {
|
2016-01-15 09:15:47 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIStyleRule *rule =
|
|
|
|
mCompositor->GetAnimationRule(aData->mElement,
|
|
|
|
aData->mPseudoType,
|
2016-07-11 11:28:14 +03:00
|
|
|
mCascadeLevel,
|
|
|
|
nullptr);
|
2016-01-15 09:15:47 +03:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2017-03-27 13:43:04 +03:00
|
|
|
template
|
|
|
|
void
|
2017-06-02 03:38:54 +03:00
|
|
|
EffectCompositor::UpdateEffectProperties(
|
|
|
|
nsStyleContext* aStyleContext,
|
2017-03-27 13:43:04 +03:00
|
|
|
Element* aElement,
|
|
|
|
CSSPseudoElementType aPseudoType);
|
|
|
|
|
|
|
|
template
|
|
|
|
void
|
2017-06-02 03:38:54 +03:00
|
|
|
EffectCompositor::UpdateEffectProperties(
|
|
|
|
const ServoComputedValues* aServoValues,
|
2017-03-27 13:43:04 +03:00
|
|
|
Element* aElement,
|
|
|
|
CSSPseudoElementType aPseudoType);
|
|
|
|
|
2015-12-04 02:34:12 +03:00
|
|
|
} // namespace mozilla
|