Bug 1958970 - Change animation event set-up to match the spec better. r=smaug,firefox-animation-reviewers,boris
Deal with events per-document like the spec. This also ensures that view transition rendering suppressions and such work for this rendering phase. Differential Revision: https://phabricator.services.mozilla.com/D244670
This commit is contained in:
@@ -100,35 +100,36 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AnimationEventDispatcher)
|
|||||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||||
|
|
||||||
void AnimationEventDispatcher::Disconnect() {
|
void AnimationEventDispatcher::Disconnect() {
|
||||||
if (mIsObserving) {
|
ClearEventQueue();
|
||||||
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;
|
mPresContext = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimationEventDispatcher::QueueEvent(AnimationEventInfo&& aEvent) {
|
void AnimationEventDispatcher::QueueEvent(AnimationEventInfo&& aEvent) {
|
||||||
|
const bool wasEmpty = mPendingEvents.IsEmpty();
|
||||||
mPendingEvents.AppendElement(std::move(aEvent));
|
mPendingEvents.AppendElement(std::move(aEvent));
|
||||||
mIsSorted = false;
|
mIsSorted = !wasEmpty;
|
||||||
ScheduleDispatch();
|
if (wasEmpty) {
|
||||||
|
ScheduleDispatch();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimationEventDispatcher::QueueEvents(
|
void AnimationEventDispatcher::QueueEvents(
|
||||||
nsTArray<AnimationEventInfo>&& aEvents) {
|
nsTArray<AnimationEventInfo>&& aEvents) {
|
||||||
|
if (aEvents.IsEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const bool wasEmpty = mPendingEvents.IsEmpty();
|
||||||
mPendingEvents.AppendElements(std::move(aEvents));
|
mPendingEvents.AppendElements(std::move(aEvents));
|
||||||
mIsSorted = false;
|
mIsSorted = false;
|
||||||
ScheduleDispatch();
|
if (wasEmpty) {
|
||||||
|
ScheduleDispatch();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimationEventDispatcher::ScheduleDispatch() {
|
void AnimationEventDispatcher::ScheduleDispatch() {
|
||||||
MOZ_ASSERT(mPresContext, "The pres context should be valid");
|
MOZ_ASSERT(mPresContext, "The pres context should be valid");
|
||||||
if (!mIsObserving) {
|
mPresContext->RefreshDriver()->ScheduleRenderingPhase(
|
||||||
mPresContext->RefreshDriver()->ScheduleAnimationEventDispatch(this);
|
RenderingPhase::UpdateAnimationsAndSendEvents);
|
||||||
mIsObserving = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimationEventInfo::MaybeAddMarker() const {
|
void AnimationEventInfo::MaybeAddMarker() const {
|
||||||
|
|||||||
@@ -236,7 +236,7 @@ struct AnimationEventInfo {
|
|||||||
class AnimationEventDispatcher final {
|
class AnimationEventDispatcher final {
|
||||||
public:
|
public:
|
||||||
explicit AnimationEventDispatcher(nsPresContext* aPresContext)
|
explicit AnimationEventDispatcher(nsPresContext* aPresContext)
|
||||||
: mPresContext(aPresContext), mIsSorted(true), mIsObserving(false) {}
|
: mPresContext(aPresContext), mIsSorted(true) {}
|
||||||
|
|
||||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(AnimationEventDispatcher)
|
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(AnimationEventDispatcher)
|
||||||
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(AnimationEventDispatcher)
|
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(AnimationEventDispatcher)
|
||||||
@@ -249,7 +249,6 @@ class AnimationEventDispatcher final {
|
|||||||
// This will call SortEvents automatically if it has not already been
|
// This will call SortEvents automatically if it has not already been
|
||||||
// called.
|
// called.
|
||||||
void DispatchEvents() {
|
void DispatchEvents() {
|
||||||
mIsObserving = false;
|
|
||||||
if (!mPresContext || mPendingEvents.IsEmpty()) {
|
if (!mPresContext || mPendingEvents.IsEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -288,15 +287,7 @@ class AnimationEventDispatcher final {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
#ifndef DEBUG
|
|
||||||
~AnimationEventDispatcher() = default;
|
~AnimationEventDispatcher() = default;
|
||||||
#else
|
|
||||||
~AnimationEventDispatcher() {
|
|
||||||
MOZ_ASSERT(!mIsObserving,
|
|
||||||
"AnimationEventDispatcher should have disassociated from "
|
|
||||||
"nsRefreshDriver");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Sort all pending CSS animation/transition events by scheduled event time
|
// Sort all pending CSS animation/transition events by scheduled event time
|
||||||
// and composite order.
|
// and composite order.
|
||||||
@@ -324,7 +315,6 @@ class AnimationEventDispatcher final {
|
|||||||
using EventArray = nsTArray<AnimationEventInfo>;
|
using EventArray = nsTArray<AnimationEventInfo>;
|
||||||
EventArray mPendingEvents;
|
EventArray mPendingEvents;
|
||||||
bool mIsSorted;
|
bool mIsSorted;
|
||||||
bool mIsObserving;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|||||||
@@ -7416,7 +7416,7 @@ bool Document::IsRenderingSuppressed() const {
|
|||||||
}
|
}
|
||||||
// The user agent believes that updating the rendering of doc's node navigable
|
// The user agent believes that updating the rendering of doc's node navigable
|
||||||
// would have no visible effect.
|
// would have no visible effect.
|
||||||
if (!IsEventHandlingEnabled()) {
|
if (!IsEventHandlingEnabled() && !IsBeingUsedAsImage()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!mPresShell || !mPresShell->DidInitialize()) {
|
if (!mPresShell || !mPresShell->DidInitialize()) {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#include "gfxUtils.h"
|
#include "gfxUtils.h"
|
||||||
#include "MobileViewportManager.h"
|
#include "MobileViewportManager.h"
|
||||||
#include "mozilla/AccessibleCaretEventHub.h"
|
#include "mozilla/AccessibleCaretEventHub.h"
|
||||||
|
#include "mozilla/AnimationEventDispatcher.h"
|
||||||
#include "mozilla/ArrayUtils.h"
|
#include "mozilla/ArrayUtils.h"
|
||||||
#include "mozilla/Assertions.h"
|
#include "mozilla/Assertions.h"
|
||||||
#include "mozilla/Attributes.h"
|
#include "mozilla/Attributes.h"
|
||||||
@@ -1349,7 +1350,7 @@ void PresShell::Destroy() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mPresContext) {
|
if (mPresContext) {
|
||||||
rd->CancelPendingAnimationEvents(mPresContext->AnimationEventDispatcher());
|
mPresContext->AnimationEventDispatcher()->ClearEventQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Revoke any pending events. We need to do this and cancel pending reflows
|
// Revoke any pending events. We need to do this and cancel pending reflows
|
||||||
|
|||||||
@@ -1860,8 +1860,7 @@ bool nsRefreshDriver::HasObservers() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (mViewManagerFlushIsPending && !mThrottled) ||
|
return (mViewManagerFlushIsPending && !mThrottled) ||
|
||||||
!mStyleFlushObservers.IsEmpty() ||
|
!mStyleFlushObservers.IsEmpty() || !mEarlyRunners.IsEmpty();
|
||||||
!mEarlyRunners.IsEmpty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void nsRefreshDriver::AppendObserverDescriptionsToString(
|
void nsRefreshDriver::AppendObserverDescriptionsToString(
|
||||||
@@ -2126,7 +2125,7 @@ void nsRefreshDriver::DetermineProximityToViewportAndNotifyResizeObservers() {
|
|||||||
Filter);
|
Filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
static CallState UpdateAndReduceAnimations(Document& aDocument) {
|
static void UpdateAndReduceAnimations(Document& aDocument) {
|
||||||
for (DocumentTimeline* tl :
|
for (DocumentTimeline* tl :
|
||||||
ToTArray<AutoTArray<RefPtr<DocumentTimeline>, 32>>(
|
ToTArray<AutoTArray<RefPtr<DocumentTimeline>, 32>>(
|
||||||
aDocument.Timelines())) {
|
aDocument.Timelines())) {
|
||||||
@@ -2138,39 +2137,6 @@ static CallState UpdateAndReduceAnimations(Document& aDocument) {
|
|||||||
pc->EffectCompositor()->ReduceAnimations();
|
pc->EffectCompositor()->ReduceAnimations();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
aDocument.EnumerateSubDocuments(UpdateAndReduceAnimations);
|
|
||||||
return CallState::Continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nsRefreshDriver::UpdateAnimationsAndSendEvents() {
|
|
||||||
if (!mPresContext) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// Animation updates may queue Promise resolution microtasks. We shouldn't
|
|
||||||
// run these, however, until we have fully updated the animation state. As
|
|
||||||
// per the "update animations and send events" procedure[1], we should
|
|
||||||
// remove replaced animations and then run these microtasks before
|
|
||||||
// dispatching the corresponding animation events.
|
|
||||||
//
|
|
||||||
// [1]:
|
|
||||||
// https://drafts.csswg.org/web-animations-1/#update-animations-and-send-events
|
|
||||||
nsAutoMicroTask mt;
|
|
||||||
RefPtr doc = mPresContext->Document();
|
|
||||||
UpdateAndReduceAnimations(*doc);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hold all AnimationEventDispatcher in mAnimationEventFlushObservers as
|
|
||||||
// a RefPtr<> array since each AnimationEventDispatcher might be destroyed
|
|
||||||
// during processing the previous dispatcher.
|
|
||||||
AutoTArray<RefPtr<AnimationEventDispatcher>, 16> dispatchers;
|
|
||||||
dispatchers.AppendElements(mAnimationEventFlushObservers);
|
|
||||||
mAnimationEventFlushObservers.Clear();
|
|
||||||
|
|
||||||
for (auto& dispatcher : dispatchers) {
|
|
||||||
dispatcher->DispatchEvents();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void nsRefreshDriver::RunVideoFrameCallbacks(
|
void nsRefreshDriver::RunVideoFrameCallbacks(
|
||||||
@@ -2569,10 +2535,27 @@ void nsRefreshDriver::Tick(VsyncId aId, TimeStamp aNowTime,
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Step 11. For each doc of docs, update animations and send events for doc.
|
// Step 11. For each doc of docs, update animations and send events for doc.
|
||||||
RunRenderingPhaseLegacy(RenderingPhase::UpdateAnimationsAndSendEvents,
|
RunRenderingPhase(RenderingPhase::UpdateAnimationsAndSendEvents,
|
||||||
[&]() MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA {
|
[&](Document& aDoc) MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA {
|
||||||
UpdateAnimationsAndSendEvents();
|
{
|
||||||
});
|
// Animation updates may queue Promise resolution
|
||||||
|
// microtasks. We shouldn't run these, however, until we
|
||||||
|
// have fully updated the animation state. As per the
|
||||||
|
// "update animations and send events" procedure[1], we
|
||||||
|
// should remove replaced animations and then run these
|
||||||
|
// microtasks before dispatching the corresponding
|
||||||
|
// animation events.
|
||||||
|
//
|
||||||
|
// [1]:
|
||||||
|
// https://drafts.csswg.org/web-animations-1/#update-animations-and-send-events
|
||||||
|
nsAutoMicroTask mt;
|
||||||
|
UpdateAndReduceAnimations(aDoc);
|
||||||
|
}
|
||||||
|
if (RefPtr pc = aDoc.GetPresContext()) {
|
||||||
|
RefPtr dispatcher = pc->AnimationEventDispatcher();
|
||||||
|
dispatcher->DispatchEvents();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Step 12. For each doc of docs, run the fullscreen steps for doc.
|
// Step 12. For each doc of docs, run the fullscreen steps for doc.
|
||||||
RunRenderingPhaseLegacy(
|
RunRenderingPhaseLegacy(
|
||||||
@@ -3078,13 +3061,6 @@ void nsRefreshDriver::CancelPendingFullscreenEvents(Document* aDocument) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void nsRefreshDriver::CancelPendingAnimationEvents(
|
|
||||||
AnimationEventDispatcher* aDispatcher) {
|
|
||||||
MOZ_ASSERT(aDispatcher);
|
|
||||||
aDispatcher->ClearEventQueue();
|
|
||||||
mAnimationEventFlushObservers.RemoveElement(aDispatcher);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
TimeStamp nsRefreshDriver::GetIdleDeadlineHint(TimeStamp aDefault,
|
TimeStamp nsRefreshDriver::GetIdleDeadlineHint(TimeStamp aDefault,
|
||||||
IdleCheck aCheckType) {
|
IdleCheck aCheckType) {
|
||||||
|
|||||||
@@ -192,24 +192,6 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
|
|||||||
*/
|
*/
|
||||||
void CancelPendingFullscreenEvents(Document* aDocument);
|
void CancelPendingFullscreenEvents(Document* 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);
|
|
||||||
ScheduleRenderingPhase(
|
|
||||||
mozilla::RenderingPhase::UpdateAnimationsAndSendEvents);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cancel all pending animation events associated with |aDispatcher|.
|
|
||||||
*/
|
|
||||||
void CancelPendingAnimationEvents(
|
|
||||||
mozilla::AnimationEventDispatcher* aDispatcher);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Schedule a frame visibility update "soon", subject to the heuristics and
|
* Schedule a frame visibility update "soon", subject to the heuristics and
|
||||||
* throttling we apply to visibility updates.
|
* throttling we apply to visibility updates.
|
||||||
@@ -441,7 +423,6 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
|
|||||||
};
|
};
|
||||||
using ObserverArray = nsTObserverArray<ObserverData>;
|
using ObserverArray = nsTObserverArray<ObserverData>;
|
||||||
void RunFullscreenSteps();
|
void RunFullscreenSteps();
|
||||||
void UpdateAnimationsAndSendEvents();
|
|
||||||
|
|
||||||
MOZ_CAN_RUN_SCRIPT
|
MOZ_CAN_RUN_SCRIPT
|
||||||
void RunVideoAndFrameRequestCallbacks(mozilla::TimeStamp aNowTime);
|
void RunVideoAndFrameRequestCallbacks(mozilla::TimeStamp aNowTime);
|
||||||
@@ -645,8 +626,6 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
|
|||||||
nsTObserverArray<nsAPostRefreshObserver*> mPostRefreshObservers;
|
nsTObserverArray<nsAPostRefreshObserver*> mPostRefreshObservers;
|
||||||
nsTArray<mozilla::UniquePtr<mozilla::PendingFullscreenEvent>>
|
nsTArray<mozilla::UniquePtr<mozilla::PendingFullscreenEvent>>
|
||||||
mPendingFullscreenEvents;
|
mPendingFullscreenEvents;
|
||||||
AutoTArray<mozilla::AnimationEventDispatcher*, 16>
|
|
||||||
mAnimationEventFlushObservers;
|
|
||||||
|
|
||||||
// nsPresContexts which `NotifyContentfulPaint` have been called,
|
// nsPresContexts which `NotifyContentfulPaint` have been called,
|
||||||
// however the corresponding paint doesn't come from a regular
|
// however the corresponding paint doesn't come from a regular
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
[no-css-animation-while-render-blocked.html]
|
|
||||||
expected:
|
|
||||||
if (os == "mac") and debug: [CRASH, OK]
|
|
||||||
[OK, CRASH]
|
|
||||||
[CSS animation is blocked until prepare callback]
|
|
||||||
expected:
|
|
||||||
if (processor == "x86") and (os == "win") and not debug: [PASS, FAIL]
|
|
||||||
FAIL
|
|
||||||
Reference in New Issue
Block a user