зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1180125 part 1 - Queue and dispatch CSS animation events as a separate step; r=dbaron
This patch prepares the way for script-generated events by making event dispatch a separate process that happens after sampling animations. This will allow us to sample animations from their associated timeline (removing the need for a further manager to tracker script-generated animations). Furthermore, once we sample animations from timelines the order in which they are sampled is likely to be more or less random so by making event dispatch at separate step, we have an opportunity to sort the events and dispatch in a consistent and sensible order. It also ensures that event callbacks will not be run until all animations (including transitions) have been updated ensuring they see a consistent view of timing properties. This patch only affects event handling for CSS animations. Transitions will be dealt with in a subsequent patch.
This commit is contained in:
Родитель
8a25d40d2a
Коммит
880fd89013
|
@ -138,7 +138,7 @@ public:
|
|||
|
||||
virtual void CancelFromStyle() { DoCancel(); }
|
||||
|
||||
void Tick();
|
||||
virtual void Tick();
|
||||
|
||||
/**
|
||||
* Set the time to use for starting or pausing a pending animation.
|
||||
|
|
|
@ -1250,6 +1250,10 @@ PresShell::Destroy()
|
|||
}
|
||||
}
|
||||
|
||||
if (mPresContext) {
|
||||
mPresContext->AnimationManager()->ClearEventQueue();
|
||||
}
|
||||
|
||||
// Revoke any pending events. We need to do this and cancel pending reflows
|
||||
// before we destroy the frame manager, since apparently frame destruction
|
||||
// sometimes spins the event queue when plug-ins are involved(!).
|
||||
|
|
|
@ -64,6 +64,7 @@
|
|||
#include "nsThreadUtils.h"
|
||||
#include "mozilla/unused.h"
|
||||
#include "mozilla/TimelineConsumers.h"
|
||||
#include "nsAnimationManager.h"
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
#include "ipc/Nuwa.h"
|
||||
|
@ -1489,6 +1490,40 @@ nsRefreshDriver::DispatchPendingEvents()
|
|||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
DispatchAnimationEventsOnSubDocuments(nsIDocument* aDocument,
|
||||
void* aRefreshDriver)
|
||||
{
|
||||
nsIPresShell* shell = aDocument->GetShell();
|
||||
if (!shell) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsPresContext* context = shell->GetPresContext();
|
||||
if (!context || context->RefreshDriver() != aRefreshDriver) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> kungFuDeathGrip(aDocument);
|
||||
|
||||
context->AnimationManager()->DispatchEvents();
|
||||
aDocument->EnumerateSubDocuments(DispatchAnimationEventsOnSubDocuments,
|
||||
nullptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
nsRefreshDriver::DispatchAnimationEvents()
|
||||
{
|
||||
if (!mPresContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsIDocument* doc = mPresContext->Document();
|
||||
DispatchAnimationEventsOnSubDocuments(doc, this);
|
||||
}
|
||||
|
||||
void
|
||||
nsRefreshDriver::RunFrameRequestCallbacks(TimeStamp aNowTime)
|
||||
{
|
||||
|
@ -1664,6 +1699,7 @@ nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime)
|
|||
if (i == 0) {
|
||||
// This is the Flush_Style case.
|
||||
|
||||
DispatchAnimationEvents();
|
||||
DispatchPendingEvents();
|
||||
RunFrameRequestCallbacks(aNowTime);
|
||||
|
||||
|
|
|
@ -322,6 +322,7 @@ private:
|
|||
typedef nsClassHashtable<nsUint32HashKey, ImageStartData> ImageStartTable;
|
||||
|
||||
void DispatchPendingEvents();
|
||||
void DispatchAnimationEvents();
|
||||
void RunFrameRequestCallbacks(mozilla::TimeStamp aNowTime);
|
||||
|
||||
void Tick(int64_t aNowEpoch, mozilla::TimeStamp aNowTime);
|
||||
|
|
|
@ -995,4 +995,24 @@ AnimationCollection::HasCurrentAnimationsForProperties(
|
|||
return false;
|
||||
}
|
||||
|
||||
nsPresContext*
|
||||
OwningElementRef::GetRenderedPresContext() const
|
||||
{
|
||||
if (!mElement) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsIDocument* doc = mElement->GetComposedDoc();
|
||||
if (!doc) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsIPresShell* shell = doc->GetShell();
|
||||
if (!shell) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return shell->GetPresContext();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -506,6 +506,14 @@ public:
|
|||
|
||||
bool IsSet() const { return !!mElement; }
|
||||
|
||||
void GetElement(dom::Element*& aElement,
|
||||
nsCSSPseudoElements::Type& aPseudoType) const {
|
||||
aElement = mElement;
|
||||
aPseudoType = mPseudoType;
|
||||
}
|
||||
|
||||
nsPresContext* GetRenderedPresContext() const;
|
||||
|
||||
private:
|
||||
dom::Element* MOZ_NON_OWNING_REF mElement;
|
||||
nsCSSPseudoElements::Type mPseudoType;
|
||||
|
|
|
@ -113,6 +113,13 @@ CSSAnimation::PauseFromStyle()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
CSSAnimation::Tick()
|
||||
{
|
||||
Animation::Tick();
|
||||
QueueEvents();
|
||||
}
|
||||
|
||||
bool
|
||||
CSSAnimation::HasLowerCompositeOrderThan(const Animation& aOther) const
|
||||
{
|
||||
|
@ -158,12 +165,40 @@ CSSAnimation::HasLowerCompositeOrderThan(const Animation& aOther) const
|
|||
}
|
||||
|
||||
void
|
||||
CSSAnimation::QueueEvents(EventArray& aEventsToDispatch)
|
||||
CSSAnimation::QueueEvents()
|
||||
{
|
||||
if (!mEffect) {
|
||||
return;
|
||||
}
|
||||
|
||||
// CSS animations dispatch events at their owning element. This allows
|
||||
// script to repurpose a CSS animation to target a different element,
|
||||
// to use a group effect (which has no obvious "target element"), or
|
||||
// to remove the animation effect altogether whilst still getting
|
||||
// animation events.
|
||||
//
|
||||
// It does mean, however, that for a CSS animation that has no owning
|
||||
// element (e.g. it was created using the CSSAnimation constructor or
|
||||
// disassociated from CSS) no events are fired. If it becomes desirable
|
||||
// for these animations to still fire events we should spec the concept
|
||||
// of the "original owning element" or "event target" and allow script
|
||||
// to set it when creating a CSSAnimation object.
|
||||
if (!mOwningElement.IsSet()) {
|
||||
return;
|
||||
}
|
||||
|
||||
dom::Element* owningElement;
|
||||
nsCSSPseudoElements::Type owningPseudoType;
|
||||
mOwningElement.GetElement(owningElement, owningPseudoType);
|
||||
MOZ_ASSERT(owningElement, "Owning element should be set");
|
||||
|
||||
// Get the nsAnimationManager so we can queue events on it
|
||||
nsPresContext* presContext = mOwningElement.GetRenderedPresContext();
|
||||
if (!presContext) {
|
||||
return;
|
||||
}
|
||||
nsAnimationManager* manager = presContext->AnimationManager();
|
||||
|
||||
ComputedTiming computedTiming = mEffect->GetComputedTiming();
|
||||
|
||||
if (computedTiming.mPhase == ComputedTiming::AnimationPhase_Null) {
|
||||
|
@ -200,10 +235,6 @@ CSSAnimation::QueueEvents(EventArray& aEventsToDispatch)
|
|||
mPreviousPhaseOrIteration = PREVIOUS_PHASE_AFTER;
|
||||
}
|
||||
|
||||
dom::Element* target;
|
||||
nsCSSPseudoElements::Type targetPseudoType;
|
||||
mEffect->GetTarget(target, targetPseudoType);
|
||||
|
||||
uint32_t message;
|
||||
|
||||
if (!wasActive && isActive) {
|
||||
|
@ -218,10 +249,10 @@ CSSAnimation::QueueEvents(EventArray& aEventsToDispatch)
|
|||
StickyTimeDuration elapsedTime =
|
||||
std::min(StickyTimeDuration(mEffect->InitialAdvance()),
|
||||
computedTiming.mActiveDuration);
|
||||
AnimationEventInfo ei(target, mAnimationName, NS_ANIMATION_START,
|
||||
AnimationEventInfo ei(owningElement, mAnimationName, NS_ANIMATION_START,
|
||||
elapsedTime,
|
||||
PseudoTypeAsString(targetPseudoType));
|
||||
aEventsToDispatch.AppendElement(ei);
|
||||
PseudoTypeAsString(owningPseudoType));
|
||||
manager->QueueEvent(ei);
|
||||
// Then have the shared code below append an 'animationend':
|
||||
message = NS_ANIMATION_END;
|
||||
} else {
|
||||
|
@ -241,9 +272,9 @@ CSSAnimation::QueueEvents(EventArray& aEventsToDispatch)
|
|||
elapsedTime = computedTiming.mActiveDuration;
|
||||
}
|
||||
|
||||
AnimationEventInfo ei(target, mAnimationName, message, elapsedTime,
|
||||
PseudoTypeAsString(targetPseudoType));
|
||||
aEventsToDispatch.AppendElement(ei);
|
||||
AnimationEventInfo ei(owningElement, mAnimationName, message, elapsedTime,
|
||||
PseudoTypeAsString(owningPseudoType));
|
||||
manager->QueueEvent(ei);
|
||||
}
|
||||
|
||||
CommonAnimationManager*
|
||||
|
@ -270,26 +301,6 @@ CSSAnimation::PseudoTypeAsString(nsCSSPseudoElements::Type aPseudoType)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsAnimationManager::UpdateStyleAndEvents(AnimationCollection* aCollection,
|
||||
TimeStamp aRefreshTime,
|
||||
EnsureStyleRuleFlags aFlags)
|
||||
{
|
||||
aCollection->EnsureStyleRuleFor(aRefreshTime, aFlags);
|
||||
QueueEvents(aCollection, mPendingEvents);
|
||||
}
|
||||
|
||||
void
|
||||
nsAnimationManager::QueueEvents(AnimationCollection* aCollection,
|
||||
EventArray& aEventsToDispatch)
|
||||
{
|
||||
for (size_t animIdx = aCollection->mAnimations.Length(); animIdx-- != 0; ) {
|
||||
CSSAnimation* anim = aCollection->mAnimations[animIdx]->AsCSSAnimation();
|
||||
MOZ_ASSERT(anim, "Expected a collection of CSS Animations");
|
||||
anim->QueueEvents(aEventsToDispatch);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsAnimationManager::MaybeUpdateCascadeResults(AnimationCollection* aCollection)
|
||||
{
|
||||
|
@ -503,8 +514,7 @@ nsAnimationManager::CheckAnimationRule(nsStyleContext* aStyleContext,
|
|||
UpdateCascadeResults(aStyleContext, collection);
|
||||
|
||||
TimeStamp refreshTime = mPresContext->RefreshDriver()->MostRecentRefresh();
|
||||
UpdateStyleAndEvents(collection, refreshTime,
|
||||
EnsureStyleRule_IsNotThrottled);
|
||||
collection->EnsureStyleRuleFor(refreshTime, EnsureStyleRule_IsNotThrottled);
|
||||
// We don't actually dispatch the mPendingEvents now. We'll either
|
||||
// dispatch them the next time we get a refresh driver notification
|
||||
// or the next time somebody calls
|
||||
|
@ -516,6 +526,12 @@ nsAnimationManager::CheckAnimationRule(nsStyleContext* aStyleContext,
|
|||
return GetAnimationRule(aElement, aStyleContext->GetPseudoType());
|
||||
}
|
||||
|
||||
void
|
||||
nsAnimationManager::QueueEvent(AnimationEventInfo& aEventInfo)
|
||||
{
|
||||
mPendingEvents.AppendElement(aEventInfo);
|
||||
}
|
||||
|
||||
struct KeyframeData {
|
||||
float mKey;
|
||||
uint32_t mIndex; // store original order since sort algorithm is not stable
|
||||
|
@ -968,9 +984,9 @@ nsAnimationManager::FlushAnimations(FlushFlags aFlags)
|
|||
collection->CanThrottleAnimation(now);
|
||||
|
||||
nsRefPtr<css::AnimValuesStyleRule> oldStyleRule = collection->mStyleRule;
|
||||
UpdateStyleAndEvents(collection, now, canThrottleTick
|
||||
? EnsureStyleRule_IsThrottled
|
||||
: EnsureStyleRule_IsNotThrottled);
|
||||
collection->EnsureStyleRuleFor(now, canThrottleTick
|
||||
? EnsureStyleRule_IsThrottled
|
||||
: EnsureStyleRule_IsNotThrottled);
|
||||
if (oldStyleRule != collection->mStyleRule) {
|
||||
collection->PostRestyleForAnimation(mPresContext);
|
||||
} else {
|
||||
|
@ -983,8 +999,6 @@ nsAnimationManager::FlushAnimations(FlushFlags aFlags)
|
|||
}
|
||||
|
||||
MaybeStartOrStopObservingRefreshDriver();
|
||||
|
||||
DispatchEvents(); // may destroy us
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -992,6 +1006,7 @@ nsAnimationManager::DoDispatchEvents()
|
|||
{
|
||||
EventArray events;
|
||||
mPendingEvents.SwapElements(events);
|
||||
// FIXME: Sort events here in timeline order, then document order
|
||||
for (uint32_t i = 0, i_end = events.Length(); i < i_end; ++i) {
|
||||
AnimationEventInfo &info = events[i];
|
||||
EventDispatcher::Dispatch(info.mElement, mPresContext, &info.mEvent);
|
||||
|
|
|
@ -101,6 +101,8 @@ public:
|
|||
MOZ_ASSERT(mSequenceNum == kUnsequenced);
|
||||
}
|
||||
|
||||
void Tick() override;
|
||||
|
||||
bool IsStylePaused() const { return mIsStylePaused; }
|
||||
|
||||
bool HasLowerCompositeOrderThan(const Animation& aOther) const override;
|
||||
|
@ -121,8 +123,6 @@ public:
|
|||
mSequenceNum = aOther.mSequenceNum;
|
||||
}
|
||||
|
||||
void QueueEvents(EventArray& aEventsToDispatch);
|
||||
|
||||
// Returns the element or pseudo-element whose animation-name property
|
||||
// this CSSAnimation corresponds to (if any). This is used for determining
|
||||
// the relative composite order of animations generated from CSS markup.
|
||||
|
@ -165,6 +165,8 @@ protected:
|
|||
}
|
||||
virtual css::CommonAnimationManager* GetAnimationManager() const override;
|
||||
|
||||
void QueueEvents();
|
||||
|
||||
static nsString PseudoTypeAsString(nsCSSPseudoElements::Type aPseudoType);
|
||||
|
||||
nsString mAnimationName;
|
||||
|
@ -246,12 +248,6 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
void UpdateStyleAndEvents(mozilla::AnimationCollection* aEA,
|
||||
mozilla::TimeStamp aRefreshTime,
|
||||
mozilla::EnsureStyleRuleFlags aFlags);
|
||||
void QueueEvents(mozilla::AnimationCollection* aEA,
|
||||
mozilla::EventArray &aEventsToDispatch);
|
||||
|
||||
void MaybeUpdateCascadeResults(mozilla::AnimationCollection* aCollection);
|
||||
|
||||
// nsIStyleRuleProcessor (parts)
|
||||
|
@ -279,6 +275,11 @@ public:
|
|||
nsIStyleRule* CheckAnimationRule(nsStyleContext* aStyleContext,
|
||||
mozilla::dom::Element* aElement);
|
||||
|
||||
/**
|
||||
* Add a pending event.
|
||||
*/
|
||||
void QueueEvent(mozilla::AnimationEventInfo& aEventInfo);
|
||||
|
||||
/**
|
||||
* Dispatch any pending events. We accumulate animationend and
|
||||
* animationiteration events only during refresh driver notifications
|
||||
|
@ -293,7 +294,11 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void ClearEventQueue() { mPendingEvents.Clear(); }
|
||||
|
||||
protected:
|
||||
virtual ~nsAnimationManager() {}
|
||||
|
||||
virtual nsIAtom* GetAnimationsAtom() override {
|
||||
return nsGkAtoms::animationsProperty;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче