Bug 1415780 - Let AnimationEventDispatcher observe nsRefreshDriver. r=birtles

So that we can now ensure nsRefreshDriver ticks (i.e. nsRefreshDriver doesn't
stop its timer) for all queued events.
Before this patch, dispatching CSS animation/transition events relied on the
fact that DocumentTimeline observes nsRefreshDriver.  For this fact,
animationcancel or transitioncancel event did not dispatch properly in some
cases, i.e. the case where the animation was dropped from the DocumentTimeline.

MozReview-Commit-ID: 7JYro0MY2U2

--HG--
extra : rebase_source : e1963d9eef996cdf5d64c64f80eb1b93ac6fd18a
This commit is contained in:
Hiroyuki Ikezoe 2018-01-27 16:55:45 +09:00
Родитель 9e4cc95a63
Коммит 51d411f1b6
6 изменённых файлов: 88 добавлений и 25 удалений

Просмотреть файл

@ -7,6 +7,7 @@
#include "mozilla/AnimationEventDispatcher.h"
#include "mozilla/EventDispatcher.h"
#include "nsRefreshDriver.h"
namespace mozilla {
@ -26,5 +27,32 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(AnimationEventDispatcher, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(AnimationEventDispatcher, Release)
void
AnimationEventDispatcher::Disconnect()
{
if (mIsObserving) {
MOZ_ASSERT(mPresContext && mPresContext->RefreshDriver(),
"The pres context and the refresh driver should be still "
"alive if we haven't disassociated from the refresh driver");
mPresContext->RefreshDriver()->CancelPendingAnimationEvents(this);
mIsObserving = false;
}
mPresContext = nullptr;
}
void
AnimationEventDispatcher::QueueEvents(nsTArray<AnimationEventInfo>&& aEvents)
{
MOZ_ASSERT(mPresContext,
"The pres context should be valid");
mPendingEvents.AppendElements(Move(aEvents));
mIsSorted = false;
if (!mIsObserving) {
mPresContext->RefreshDriver()->ScheduleAnimationEventDispatch(this);
mIsObserving = true;
}
}
} // namespace mozilla

Просмотреть файл

@ -17,6 +17,7 @@
#include "nsCycleCollectionParticipant.h"
class nsPresContext;
class nsRefreshDriver;
namespace mozilla {
@ -100,26 +101,22 @@ public:
explicit AnimationEventDispatcher(nsPresContext* aPresContext)
: mPresContext(aPresContext)
, mIsSorted(true)
, mIsObserving(false)
{
}
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(AnimationEventDispatcher)
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(AnimationEventDispatcher)
void Disconnect() {
mPresContext = nullptr;
}
void Disconnect();
void QueueEvents(nsTArray<AnimationEventInfo>&& aEvents)
{
mPendingEvents.AppendElements(Move(aEvents));
mIsSorted = false;
}
void QueueEvents(nsTArray<AnimationEventInfo>&& aEvents);
// This will call SortEvents automatically if it has not already been
// called.
void DispatchEvents()
{
mIsObserving = false;
if (!mPresContext || mPendingEvents.IsEmpty()) {
return;
}
@ -154,7 +151,16 @@ public:
bool HasQueuedEvents() const { return !mPendingEvents.IsEmpty(); }
private:
#ifndef DEBUG
~AnimationEventDispatcher() = default;
#else
~AnimationEventDispatcher()
{
MOZ_ASSERT(!mIsObserving,
"AnimationEventDispatcher should have disassociated from "
"nsRefreshDriver");
}
#endif
class AnimationEventInfoLessThan
{
@ -195,6 +201,7 @@ private:
typedef nsTArray<AnimationEventInfo> EventArray;
EventArray mPendingEvents;
bool mIsSorted;
bool mIsObserving;
};
} // namespace mozilla

Просмотреть файл

@ -8,7 +8,6 @@
#include "mozilla/PresShell.h"
#include "mozilla/AnimationEventDispatcher.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Attributes.h"
#include "mozilla/StyleSheetInlines.h"
@ -1363,7 +1362,7 @@ PresShell::Destroy()
}
if (mPresContext) {
mPresContext->AnimationEventDispatcher()->ClearEventQueue();
rd->CancelPendingAnimationEvents(mPresContext->AnimationEventDispatcher());
}
// Revoke any pending events. We need to do this and cancel pending reflows

Просмотреть файл

@ -241,6 +241,7 @@ public:
{
return mAnimationEventDispatcher;
}
mozilla::EffectCompositor* EffectCompositor() { return mEffectCompositor; }
nsTransitionManager* TransitionManager() { return mTransitionManager; }
nsAnimationManager* AnimationManager() { return mAnimationManager; }

Просмотреть файл

@ -1429,6 +1429,7 @@ nsRefreshDriver::ObserverCount() const
// changes can trigger transitions which fire events when they complete, and
// layout changes can affect media queries on child documents, triggering
// style changes, etc.
sum += mAnimationEventFlushObservers.Length();
sum += mResizeEventFlushObservers.Length();
sum += mStyleFlushObservers.Length();
sum += mLayoutFlushObservers.Length();
@ -1452,6 +1453,7 @@ nsRefreshDriver::HasObservers() const
return mViewManagerFlushIsPending ||
!mStyleFlushObservers.IsEmpty() ||
!mLayoutFlushObservers.IsEmpty() ||
!mAnimationEventFlushObservers.IsEmpty() ||
!mResizeEventFlushObservers.IsEmpty() ||
!mPendingEvents.IsEmpty() ||
!mFrameRequestCallbackDocs.IsEmpty() ||
@ -1638,22 +1640,19 @@ nsRefreshDriver::DispatchAnimationEvents()
return;
}
AutoTArray<nsCOMPtr<nsIDocument>, 32> documents;
CollectDocuments(mPresContext->Document(), &documents);
// Hold all AnimationEventDispatcher in mAnimationEventFlushObservers as
// a RefPtr<> array since each AnimationEventDispatcher might be destroyed
// during processing the previous dispatcher.
size_t len = mAnimationEventFlushObservers.Length();
AutoTArray<RefPtr<AnimationEventDispatcher>, 16> dispatchers;
RefPtr<AnimationEventDispatcher>* elems = dispatchers.AppendElements(len);
for (size_t i = 0; i < len; i++) {
elems[i] = mAnimationEventFlushObservers[i];
}
mAnimationEventFlushObservers.Clear();
for (uint32_t i = 0; i < documents.Length(); ++i) {
nsIDocument* doc = documents[i];
nsIPresShell* shell = doc->GetShell();
if (!shell) {
continue;
}
RefPtr<nsPresContext> context = shell->GetPresContext();
if (!context || context->RefreshDriver() != this) {
continue;
}
context->AnimationEventDispatcher()->DispatchEvents();
for (auto& dispatcher : dispatchers) {
dispatcher->DispatchEvents();
}
}
@ -2412,6 +2411,14 @@ nsRefreshDriver::CancelPendingEvents(nsIDocument* aDocument)
}
}
void
nsRefreshDriver::CancelPendingAnimationEvents(AnimationEventDispatcher* aDispatcher)
{
MOZ_ASSERT(aDispatcher);
aDispatcher->ClearEventQueue();
mAnimationEventFlushObservers.RemoveElement(aDispatcher);
}
/* static */ TimeStamp
nsRefreshDriver::GetIdleDeadlineHint(TimeStamp aDefault)
{

Просмотреть файл

@ -22,6 +22,7 @@
#include "nsTObserverArray.h"
#include "nsClassHashtable.h"
#include "nsHashKeys.h"
#include "mozilla/AnimationEventDispatcher.h"
#include "mozilla/Attributes.h"
#include "mozilla/Maybe.h"
#include "mozilla/layers/TransactionIdAllocator.h"
@ -243,6 +244,24 @@ public:
*/
void CancelPendingEvents(nsIDocument* aDocument);
/**
* Queue new animation events to dispatch in next tick.
*/
void ScheduleAnimationEventDispatch(
mozilla::AnimationEventDispatcher* aDispatcher)
{
NS_ASSERTION(!mAnimationEventFlushObservers.Contains(aDispatcher),
"Double-adding animation event flush observer");
mAnimationEventFlushObservers.AppendElement(aDispatcher);
EnsureTimerStarted();
}
/**
* Cancel all pending animation events associated with |aDispatcher|.
*/
void CancelPendingAnimationEvents(
mozilla::AnimationEventDispatcher* aDispatcher);
/**
* Schedule a frame visibility update "soon", subject to the heuristics and
* throttling we apply to visibility updates.
@ -496,6 +515,8 @@ private:
nsTArray<nsIDocument*> mThrottledFrameRequestCallbackDocs;
nsTObserverArray<nsAPostRefreshObserver*> mPostRefreshObservers;
nsTArray<PendingEvent> mPendingEvents;
AutoTArray<mozilla::AnimationEventDispatcher*, 16>
mAnimationEventFlushObservers;
void BeginRefreshingImages(RequestTable& aEntries,
mozilla::TimeStamp aDesired);