2018-01-27 15:17:27 +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/. */
|
|
|
|
|
|
|
|
#ifndef mozilla_AnimationEventDispatcher_h
|
|
|
|
#define mozilla_AnimationEventDispatcher_h
|
|
|
|
|
|
|
|
#include <algorithm> // For <std::stable_sort>
|
|
|
|
#include "mozilla/AnimationComparator.h"
|
2018-01-27 15:17:27 +03:00
|
|
|
#include "mozilla/Assertions.h"
|
|
|
|
#include "mozilla/ContentEvents.h"
|
2018-01-27 15:17:27 +03:00
|
|
|
#include "mozilla/EventDispatcher.h"
|
2018-01-27 15:17:27 +03:00
|
|
|
#include "mozilla/Variant.h"
|
2018-07-03 05:05:23 +03:00
|
|
|
#include "mozilla/dom/AnimationPlaybackEvent.h"
|
2018-01-27 15:17:27 +03:00
|
|
|
#include "nsCSSProps.h"
|
2018-01-27 15:17:27 +03:00
|
|
|
#include "nsCycleCollectionParticipant.h"
|
2020-06-02 13:31:46 +03:00
|
|
|
#include "nsPresContext.h"
|
2018-01-27 15:17:27 +03:00
|
|
|
|
2018-01-27 15:17:27 +03:00
|
|
|
class nsRefreshDriver;
|
2018-01-27 15:17:27 +03:00
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
|
2018-01-27 15:17:27 +03:00
|
|
|
struct AnimationEventInfo {
|
2018-07-03 03:25:02 +03:00
|
|
|
RefPtr<dom::EventTarget> mTarget;
|
2018-01-27 15:17:27 +03:00
|
|
|
RefPtr<dom::Animation> mAnimation;
|
2018-07-05 00:13:18 +03:00
|
|
|
TimeStamp mScheduledEventTimeStamp;
|
2018-01-27 15:17:27 +03:00
|
|
|
|
2018-07-03 05:05:23 +03:00
|
|
|
typedef Variant<InternalTransitionEvent, InternalAnimationEvent,
|
|
|
|
RefPtr<dom::AnimationPlaybackEvent>>
|
|
|
|
EventVariant;
|
2018-01-27 15:17:27 +03:00
|
|
|
EventVariant mEvent;
|
|
|
|
|
|
|
|
// For CSS animation events
|
|
|
|
AnimationEventInfo(nsAtom* aAnimationName,
|
|
|
|
const NonOwningAnimationTarget& aTarget,
|
|
|
|
EventMessage aMessage, double aElapsedTime,
|
2018-07-05 00:13:18 +03:00
|
|
|
const TimeStamp& aScheduledEventTimeStamp,
|
2018-01-27 15:17:27 +03:00
|
|
|
dom::Animation* aAnimation)
|
2018-07-03 03:25:02 +03:00
|
|
|
: mTarget(aTarget.mElement),
|
2018-01-27 15:17:27 +03:00
|
|
|
mAnimation(aAnimation),
|
2018-07-05 00:13:18 +03:00
|
|
|
mScheduledEventTimeStamp(aScheduledEventTimeStamp),
|
2018-01-27 15:17:27 +03:00
|
|
|
mEvent(EventVariant(InternalAnimationEvent(true, aMessage))) {
|
|
|
|
InternalAnimationEvent& event = mEvent.as<InternalAnimationEvent>();
|
|
|
|
|
|
|
|
aAnimationName->ToString(event.mAnimationName);
|
|
|
|
// XXX Looks like nobody initialize WidgetEvent::time
|
|
|
|
event.mElapsedTime = aElapsedTime;
|
|
|
|
event.mPseudoElement =
|
|
|
|
nsCSSPseudoElements::PseudoTypeAsString(aTarget.mPseudoType);
|
|
|
|
}
|
|
|
|
|
|
|
|
// For CSS transition events
|
|
|
|
AnimationEventInfo(nsCSSPropertyID aProperty,
|
|
|
|
const NonOwningAnimationTarget& aTarget,
|
|
|
|
EventMessage aMessage, double aElapsedTime,
|
2018-07-05 00:13:18 +03:00
|
|
|
const TimeStamp& aScheduledEventTimeStamp,
|
2018-01-27 15:17:27 +03:00
|
|
|
dom::Animation* aAnimation)
|
2018-07-03 03:25:02 +03:00
|
|
|
: mTarget(aTarget.mElement),
|
2018-01-27 15:17:27 +03:00
|
|
|
mAnimation(aAnimation),
|
2018-07-05 00:13:18 +03:00
|
|
|
mScheduledEventTimeStamp(aScheduledEventTimeStamp),
|
2018-01-27 15:17:27 +03:00
|
|
|
mEvent(EventVariant(InternalTransitionEvent(true, aMessage))) {
|
|
|
|
InternalTransitionEvent& event = mEvent.as<InternalTransitionEvent>();
|
|
|
|
|
|
|
|
event.mPropertyName =
|
|
|
|
NS_ConvertUTF8toUTF16(nsCSSProps::GetStringValue(aProperty));
|
|
|
|
// XXX Looks like nobody initialize WidgetEvent::time
|
|
|
|
event.mElapsedTime = aElapsedTime;
|
|
|
|
event.mPseudoElement =
|
|
|
|
nsCSSPseudoElements::PseudoTypeAsString(aTarget.mPseudoType);
|
|
|
|
}
|
|
|
|
|
2018-07-03 05:05:23 +03:00
|
|
|
// For web animation events
|
|
|
|
AnimationEventInfo(const nsAString& aName,
|
|
|
|
RefPtr<dom::AnimationPlaybackEvent>&& aEvent,
|
2018-07-05 00:13:18 +03:00
|
|
|
TimeStamp&& aScheduledEventTimeStamp,
|
2018-07-03 05:05:23 +03:00
|
|
|
dom::Animation* aAnimation)
|
|
|
|
: mTarget(aAnimation),
|
|
|
|
mAnimation(aAnimation),
|
2018-07-05 00:13:18 +03:00
|
|
|
mScheduledEventTimeStamp(std::move(aScheduledEventTimeStamp)),
|
2018-07-03 05:05:23 +03:00
|
|
|
mEvent(std::move(aEvent)) {}
|
|
|
|
|
2018-01-30 03:26:48 +03:00
|
|
|
AnimationEventInfo(const AnimationEventInfo& aOther) = delete;
|
|
|
|
AnimationEventInfo& operator=(const AnimationEventInfo& aOther) = delete;
|
|
|
|
AnimationEventInfo(AnimationEventInfo&& aOther) = default;
|
|
|
|
AnimationEventInfo& operator=(AnimationEventInfo&& aOther) = default;
|
2018-01-27 15:17:27 +03:00
|
|
|
|
2018-07-03 05:05:23 +03:00
|
|
|
bool IsWebAnimationEvent() const {
|
|
|
|
return mEvent.is<RefPtr<dom::AnimationPlaybackEvent>>();
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
bool IsStale() const {
|
|
|
|
const WidgetEvent* widgetEvent = AsWidgetEvent();
|
|
|
|
return widgetEvent->mFlags.mIsBeingDispatched ||
|
|
|
|
widgetEvent->mFlags.mDispatchedAtLeastOnce;
|
|
|
|
}
|
|
|
|
|
|
|
|
const WidgetEvent* AsWidgetEvent() const {
|
|
|
|
return const_cast<AnimationEventInfo*>(this)->AsWidgetEvent();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2018-01-27 15:17:27 +03:00
|
|
|
WidgetEvent* AsWidgetEvent() {
|
|
|
|
if (mEvent.is<InternalTransitionEvent>()) {
|
|
|
|
return &mEvent.as<InternalTransitionEvent>();
|
|
|
|
}
|
|
|
|
if (mEvent.is<InternalAnimationEvent>()) {
|
|
|
|
return &mEvent.as<InternalAnimationEvent>();
|
|
|
|
}
|
2018-07-03 05:05:23 +03:00
|
|
|
if (mEvent.is<RefPtr<dom::AnimationPlaybackEvent>>()) {
|
|
|
|
return mEvent.as<RefPtr<dom::AnimationPlaybackEvent>>()->WidgetEventPtr();
|
|
|
|
}
|
2018-01-27 15:17:27 +03:00
|
|
|
|
|
|
|
MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected event type");
|
|
|
|
return nullptr;
|
|
|
|
}
|
2018-07-03 05:05:23 +03:00
|
|
|
|
|
|
|
void Dispatch(nsPresContext* aPresContext) {
|
|
|
|
if (mEvent.is<RefPtr<dom::AnimationPlaybackEvent>>()) {
|
|
|
|
EventDispatcher::DispatchDOMEvent(
|
|
|
|
mTarget, nullptr /* WidgetEvent */,
|
|
|
|
mEvent.as<RefPtr<dom::AnimationPlaybackEvent>>(), aPresContext,
|
|
|
|
nullptr /* nsEventStatus */);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
MOZ_ASSERT(mEvent.is<InternalTransitionEvent>() ||
|
|
|
|
mEvent.is<InternalAnimationEvent>());
|
|
|
|
|
|
|
|
EventDispatcher::Dispatch(mTarget, aPresContext, AsWidgetEvent());
|
|
|
|
}
|
2018-01-27 15:17:27 +03:00
|
|
|
};
|
|
|
|
|
2018-01-27 15:17:27 +03:00
|
|
|
class AnimationEventDispatcher final {
|
|
|
|
public:
|
2018-01-27 15:17:27 +03:00
|
|
|
explicit AnimationEventDispatcher(nsPresContext* aPresContext)
|
|
|
|
: mPresContext(aPresContext), mIsSorted(true), mIsObserving(false) {}
|
2018-01-27 15:17:27 +03:00
|
|
|
|
2018-01-27 15:17:27 +03:00
|
|
|
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(AnimationEventDispatcher)
|
|
|
|
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(AnimationEventDispatcher)
|
|
|
|
|
2018-01-27 15:17:27 +03:00
|
|
|
void Disconnect();
|
2018-01-27 15:17:27 +03:00
|
|
|
|
2018-07-03 03:25:02 +03:00
|
|
|
void QueueEvent(AnimationEventInfo&& aEvent);
|
2018-01-27 15:17:27 +03:00
|
|
|
void QueueEvents(nsTArray<AnimationEventInfo>&& aEvents);
|
2018-01-27 15:17:27 +03:00
|
|
|
|
|
|
|
// This will call SortEvents automatically if it has not already been
|
|
|
|
// called.
|
2018-01-27 15:17:27 +03:00
|
|
|
void DispatchEvents() {
|
2018-01-27 15:17:27 +03:00
|
|
|
mIsObserving = false;
|
2018-01-27 15:17:27 +03:00
|
|
|
if (!mPresContext || mPendingEvents.IsEmpty()) {
|
2018-01-27 15:17:27 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
SortEvents();
|
|
|
|
|
2020-08-04 14:27:07 +03:00
|
|
|
EventArray events = std::move(mPendingEvents);
|
2018-01-27 15:17:27 +03:00
|
|
|
// mIsSorted will be set to true by SortEvents above, and we leave it
|
|
|
|
// that way since mPendingEvents is now empty
|
2018-01-27 15:17:27 +03:00
|
|
|
for (AnimationEventInfo& info : events) {
|
2018-07-03 05:05:23 +03:00
|
|
|
MOZ_ASSERT(!info.IsStale(), "The event shouldn't be stale");
|
|
|
|
info.Dispatch(mPresContext);
|
2018-01-27 15:17:27 +03:00
|
|
|
|
2018-01-27 15:17:27 +03:00
|
|
|
// Bail out if our mPresContext was nullified due to destroying the pres
|
|
|
|
// context.
|
|
|
|
if (!mPresContext) {
|
2018-01-27 15:17:27 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClearEventQueue() {
|
|
|
|
mPendingEvents.Clear();
|
|
|
|
mIsSorted = true;
|
|
|
|
}
|
|
|
|
bool HasQueuedEvents() const { return !mPendingEvents.IsEmpty(); }
|
|
|
|
|
2018-01-27 15:17:27 +03:00
|
|
|
private:
|
2018-01-27 15:17:27 +03:00
|
|
|
#ifndef DEBUG
|
2018-01-27 15:17:27 +03:00
|
|
|
~AnimationEventDispatcher() = default;
|
2018-01-27 15:17:27 +03:00
|
|
|
#else
|
|
|
|
~AnimationEventDispatcher() {
|
|
|
|
MOZ_ASSERT(!mIsObserving,
|
|
|
|
"AnimationEventDispatcher should have disassociated from "
|
|
|
|
"nsRefreshDriver");
|
|
|
|
}
|
|
|
|
#endif
|
2018-01-27 15:17:27 +03:00
|
|
|
|
2018-01-27 15:17:27 +03:00
|
|
|
class AnimationEventInfoLessThan {
|
2018-01-27 15:17:27 +03:00
|
|
|
public:
|
2018-07-05 00:13:18 +03:00
|
|
|
bool operator()(const AnimationEventInfo& a,
|
|
|
|
const AnimationEventInfo& b) const {
|
|
|
|
if (a.mScheduledEventTimeStamp != b.mScheduledEventTimeStamp) {
|
2018-01-27 15:17:27 +03:00
|
|
|
// Null timestamps sort first
|
2018-07-05 00:13:18 +03:00
|
|
|
if (a.mScheduledEventTimeStamp.IsNull() ||
|
|
|
|
b.mScheduledEventTimeStamp.IsNull()) {
|
|
|
|
return a.mScheduledEventTimeStamp.IsNull();
|
2018-01-27 15:17:27 +03:00
|
|
|
} else {
|
2018-07-05 00:13:18 +03:00
|
|
|
return a.mScheduledEventTimeStamp < b.mScheduledEventTimeStamp;
|
2018-01-27 15:17:27 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-03 05:05:23 +03:00
|
|
|
// Events in the Web Animations spec are prior to CSS events.
|
|
|
|
if (a.IsWebAnimationEvent() != b.IsWebAnimationEvent()) {
|
|
|
|
return a.IsWebAnimationEvent();
|
|
|
|
}
|
|
|
|
|
2018-01-27 15:17:27 +03:00
|
|
|
AnimationPtrComparator<RefPtr<dom::Animation>> comparator;
|
|
|
|
return comparator.LessThan(a.mAnimation, b.mAnimation);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-01-27 15:17:27 +03:00
|
|
|
// Sort all pending CSS animation/transition events by scheduled event time
|
|
|
|
// and composite order.
|
|
|
|
// https://drafts.csswg.org/web-animations/#update-animations-and-send-events
|
|
|
|
void SortEvents() {
|
|
|
|
if (mIsSorted) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-09-05 00:31:57 +03:00
|
|
|
for (auto& pending : mPendingEvents) {
|
|
|
|
pending.mAnimation->CachedChildIndexRef() = -1;
|
|
|
|
}
|
|
|
|
|
2018-01-27 15:17:27 +03:00
|
|
|
// FIXME: Replace with mPendingEvents.StableSort when bug 1147091 is
|
|
|
|
// fixed.
|
|
|
|
std::stable_sort(mPendingEvents.begin(), mPendingEvents.end(),
|
|
|
|
AnimationEventInfoLessThan());
|
|
|
|
mIsSorted = true;
|
|
|
|
}
|
2018-07-03 03:25:02 +03:00
|
|
|
void ScheduleDispatch();
|
2018-01-27 15:17:27 +03:00
|
|
|
|
2018-01-27 15:17:27 +03:00
|
|
|
nsPresContext* mPresContext;
|
2018-01-27 15:17:27 +03:00
|
|
|
typedef nsTArray<AnimationEventInfo> EventArray;
|
2018-01-27 15:17:27 +03:00
|
|
|
EventArray mPendingEvents;
|
|
|
|
bool mIsSorted;
|
2018-01-27 15:17:27 +03:00
|
|
|
bool mIsObserving;
|
2018-01-27 15:17:27 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace mozilla
|
|
|
|
|
|
|
|
#endif // mozilla_AnimationEventDispatcher_h
|