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
|
||||
|
||||
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;
|
||||
}
|
||||
ClearEventQueue();
|
||||
mPresContext = nullptr;
|
||||
}
|
||||
|
||||
void AnimationEventDispatcher::QueueEvent(AnimationEventInfo&& aEvent) {
|
||||
const bool wasEmpty = mPendingEvents.IsEmpty();
|
||||
mPendingEvents.AppendElement(std::move(aEvent));
|
||||
mIsSorted = false;
|
||||
ScheduleDispatch();
|
||||
mIsSorted = !wasEmpty;
|
||||
if (wasEmpty) {
|
||||
ScheduleDispatch();
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationEventDispatcher::QueueEvents(
|
||||
nsTArray<AnimationEventInfo>&& aEvents) {
|
||||
if (aEvents.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
const bool wasEmpty = mPendingEvents.IsEmpty();
|
||||
mPendingEvents.AppendElements(std::move(aEvents));
|
||||
mIsSorted = false;
|
||||
ScheduleDispatch();
|
||||
if (wasEmpty) {
|
||||
ScheduleDispatch();
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationEventDispatcher::ScheduleDispatch() {
|
||||
MOZ_ASSERT(mPresContext, "The pres context should be valid");
|
||||
if (!mIsObserving) {
|
||||
mPresContext->RefreshDriver()->ScheduleAnimationEventDispatch(this);
|
||||
mIsObserving = true;
|
||||
}
|
||||
mPresContext->RefreshDriver()->ScheduleRenderingPhase(
|
||||
RenderingPhase::UpdateAnimationsAndSendEvents);
|
||||
}
|
||||
|
||||
void AnimationEventInfo::MaybeAddMarker() const {
|
||||
|
||||
@@ -236,7 +236,7 @@ struct AnimationEventInfo {
|
||||
class AnimationEventDispatcher final {
|
||||
public:
|
||||
explicit AnimationEventDispatcher(nsPresContext* aPresContext)
|
||||
: mPresContext(aPresContext), mIsSorted(true), mIsObserving(false) {}
|
||||
: mPresContext(aPresContext), mIsSorted(true) {}
|
||||
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(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
|
||||
// called.
|
||||
void DispatchEvents() {
|
||||
mIsObserving = false;
|
||||
if (!mPresContext || mPendingEvents.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
@@ -288,15 +287,7 @@ class AnimationEventDispatcher final {
|
||||
}
|
||||
|
||||
private:
|
||||
#ifndef DEBUG
|
||||
~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
|
||||
// and composite order.
|
||||
@@ -324,7 +315,6 @@ class AnimationEventDispatcher final {
|
||||
using EventArray = nsTArray<AnimationEventInfo>;
|
||||
EventArray mPendingEvents;
|
||||
bool mIsSorted;
|
||||
bool mIsObserving;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -7416,7 +7416,7 @@ bool Document::IsRenderingSuppressed() const {
|
||||
}
|
||||
// The user agent believes that updating the rendering of doc's node navigable
|
||||
// would have no visible effect.
|
||||
if (!IsEventHandlingEnabled()) {
|
||||
if (!IsEventHandlingEnabled() && !IsBeingUsedAsImage()) {
|
||||
return true;
|
||||
}
|
||||
if (!mPresShell || !mPresShell->DidInitialize()) {
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "gfxUtils.h"
|
||||
#include "MobileViewportManager.h"
|
||||
#include "mozilla/AccessibleCaretEventHub.h"
|
||||
#include "mozilla/AnimationEventDispatcher.h"
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
@@ -1349,7 +1350,7 @@ void PresShell::Destroy() {
|
||||
}
|
||||
|
||||
if (mPresContext) {
|
||||
rd->CancelPendingAnimationEvents(mPresContext->AnimationEventDispatcher());
|
||||
mPresContext->AnimationEventDispatcher()->ClearEventQueue();
|
||||
}
|
||||
|
||||
// Revoke any pending events. We need to do this and cancel pending reflows
|
||||
|
||||
@@ -1860,8 +1860,7 @@ bool nsRefreshDriver::HasObservers() const {
|
||||
}
|
||||
|
||||
return (mViewManagerFlushIsPending && !mThrottled) ||
|
||||
!mStyleFlushObservers.IsEmpty() ||
|
||||
!mEarlyRunners.IsEmpty();
|
||||
!mStyleFlushObservers.IsEmpty() || !mEarlyRunners.IsEmpty();
|
||||
}
|
||||
|
||||
void nsRefreshDriver::AppendObserverDescriptionsToString(
|
||||
@@ -2126,7 +2125,7 @@ void nsRefreshDriver::DetermineProximityToViewportAndNotifyResizeObservers() {
|
||||
Filter);
|
||||
}
|
||||
|
||||
static CallState UpdateAndReduceAnimations(Document& aDocument) {
|
||||
static void UpdateAndReduceAnimations(Document& aDocument) {
|
||||
for (DocumentTimeline* tl :
|
||||
ToTArray<AutoTArray<RefPtr<DocumentTimeline>, 32>>(
|
||||
aDocument.Timelines())) {
|
||||
@@ -2138,39 +2137,6 @@ static CallState UpdateAndReduceAnimations(Document& aDocument) {
|
||||
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(
|
||||
@@ -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.
|
||||
RunRenderingPhaseLegacy(RenderingPhase::UpdateAnimationsAndSendEvents,
|
||||
[&]() MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA {
|
||||
UpdateAnimationsAndSendEvents();
|
||||
});
|
||||
RunRenderingPhase(RenderingPhase::UpdateAnimationsAndSendEvents,
|
||||
[&](Document& aDoc) MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA {
|
||||
{
|
||||
// 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.
|
||||
RunRenderingPhaseLegacy(
|
||||
@@ -3078,13 +3061,6 @@ void nsRefreshDriver::CancelPendingFullscreenEvents(Document* aDocument) {
|
||||
}
|
||||
}
|
||||
|
||||
void nsRefreshDriver::CancelPendingAnimationEvents(
|
||||
AnimationEventDispatcher* aDispatcher) {
|
||||
MOZ_ASSERT(aDispatcher);
|
||||
aDispatcher->ClearEventQueue();
|
||||
mAnimationEventFlushObservers.RemoveElement(aDispatcher);
|
||||
}
|
||||
|
||||
/* static */
|
||||
TimeStamp nsRefreshDriver::GetIdleDeadlineHint(TimeStamp aDefault,
|
||||
IdleCheck aCheckType) {
|
||||
|
||||
@@ -192,24 +192,6 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
|
||||
*/
|
||||
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
|
||||
* throttling we apply to visibility updates.
|
||||
@@ -441,7 +423,6 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
|
||||
};
|
||||
using ObserverArray = nsTObserverArray<ObserverData>;
|
||||
void RunFullscreenSteps();
|
||||
void UpdateAnimationsAndSendEvents();
|
||||
|
||||
MOZ_CAN_RUN_SCRIPT
|
||||
void RunVideoAndFrameRequestCallbacks(mozilla::TimeStamp aNowTime);
|
||||
@@ -645,8 +626,6 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
|
||||
nsTObserverArray<nsAPostRefreshObserver*> mPostRefreshObservers;
|
||||
nsTArray<mozilla::UniquePtr<mozilla::PendingFullscreenEvent>>
|
||||
mPendingFullscreenEvents;
|
||||
AutoTArray<mozilla::AnimationEventDispatcher*, 16>
|
||||
mAnimationEventFlushObservers;
|
||||
|
||||
// nsPresContexts which `NotifyContentfulPaint` have been called,
|
||||
// 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