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
parent 9e4cc95a63
commit 51d411f1b6
6 changed files with 88 additions and 25 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

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

View File

@ -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)
{

View File

@ -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);