[Web Animations] imported/w3c/web-platform-tests/web-animations/timing-model/timeline...
authorgraouts@webkit.org <graouts@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 7 May 2020 21:44:13 +0000 (21:44 +0000)
committergraouts@webkit.org <graouts@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 7 May 2020 21:44:13 +0000 (21:44 +0000)
https://bugs.webkit.org/show_bug.cgi?id=211232
<rdar://problem/62650227>

Reviewed by Dean Jackson.

The flakiness came from this porting of the test:

    animA.finished.then(() => { animB.cancel() });
    animB.finished.then(() => { animA.cancel() });

Sometimes, animA and animB would finish in different frames even though they were designed to finish at the
same time. If this happened, animA.finished would resolve and trigger animB.cancel, which then rejected
animB.finished. This happened because animB was attached to a DocumentTimeline created by script which isn't
the main DocumenTimeline accessed via document.timeline. Some curious code would handle syncing of the
various timelines such that they would use a shared timebase. This was in DocumentTimeline::currentTime():

    auto& mainDocumentTimeline = m_document->timeline();
    if (&mainDocumentTimeline != this) {
        if (auto mainDocumentTimelineCurrentTime = mainDocumentTimeline.currentTime())
            return *mainDocumentTimelineCurrentTime - m_originTime;
        return WTF::nullopt;
    }

We now move the currentTime caching at the DocumentTimelinesController level which ensures all DocumentTimeline
objects attached to a given Document use the exact same currentTime(). This prompted some overdue refactoring
where also all the related animation suspension code is moved from DocumentTimeline up to DocumentTimelinesController.

* animation/DocumentTimeline.cpp:
(WebCore::DocumentTimeline::detachFromDocument):
(WebCore::DocumentTimeline::suspendAnimations):
(WebCore::DocumentTimeline::resumeAnimations):
(WebCore::DocumentTimeline::animationsAreSuspended const):
(WebCore::DocumentTimeline::currentTime):
(WebCore::DocumentTimeline::scheduleAnimationResolution):
(WebCore::DocumentTimeline::documentWillUpdateAnimationsAndSendEvents):
(WebCore::DocumentTimeline::animationsAreSuspended): Deleted.
(WebCore::DocumentTimeline::liveCurrentTime const): Deleted.
(WebCore::DocumentTimeline::cacheCurrentTime): Deleted.
(WebCore::DocumentTimeline::maybeClearCachedCurrentTime): Deleted.
* animation/DocumentTimeline.h:
* animation/DocumentTimelinesController.cpp:
(WebCore::DocumentTimelinesController::detachFromDocument):
(WebCore::DocumentTimelinesController::updateAnimationsAndSendEvents):
(WebCore::DocumentTimelinesController::suspendAnimations):
(WebCore::DocumentTimelinesController::resumeAnimations):
(WebCore::DocumentTimelinesController::animationsAreSuspended const):
(WebCore::DocumentTimelinesController::liveCurrentTime const):
(WebCore::DocumentTimelinesController::currentTime):
(WebCore::DocumentTimelinesController::cacheCurrentTime):
(WebCore::DocumentTimelinesController::maybeClearCachedCurrentTime):
* animation/DocumentTimelinesController.h:
* dom/Document.cpp:
(WebCore::Document::didBecomeCurrentDocumentInFrame):
(WebCore::Document::resume):
* dom/Document.h:
* page/Frame.cpp:
(WebCore::Frame::clearTimers):
(WebCore::Frame::resumeActiveDOMObjectsAndAnimations):
* page/Page.cpp:
(WebCore::Page::setIsVisibleInternal):
(WebCore::Page::hiddenPageCSSAnimationSuspensionStateChanged):
* testing/Internals.cpp:
(WebCore::Internals::animationsAreSuspended const):
(WebCore::Internals::suspendAnimations const):
(WebCore::Internals::resumeAnimations const):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@261336 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/animation/DocumentTimeline.cpp
Source/WebCore/animation/DocumentTimeline.h
Source/WebCore/animation/DocumentTimelinesController.cpp
Source/WebCore/animation/DocumentTimelinesController.h
Source/WebCore/dom/Document.cpp
Source/WebCore/dom/Document.h
Source/WebCore/page/Frame.cpp
Source/WebCore/page/Page.cpp
Source/WebCore/testing/Internals.cpp

index bccde05..58d4fff 100644 (file)
@@ -1,3 +1,72 @@
+2020-05-07  Antoine Quint  <graouts@apple.com>
+
+        [Web Animations] imported/w3c/web-platform-tests/web-animations/timing-model/timelines/update-and-send-events.html is a flaky failure
+        https://bugs.webkit.org/show_bug.cgi?id=211232
+        <rdar://problem/62650227>
+
+        Reviewed by Dean Jackson.
+
+        The flakiness came from this porting of the test:
+        
+            animA.finished.then(() => { animB.cancel() });
+            animB.finished.then(() => { animA.cancel() });
+
+        Sometimes, animA and animB would finish in different frames even though they were designed to finish at the
+        same time. If this happened, animA.finished would resolve and trigger animB.cancel, which then rejected
+        animB.finished. This happened because animB was attached to a DocumentTimeline created by script which isn't
+        the main DocumenTimeline accessed via document.timeline. Some curious code would handle syncing of the
+        various timelines such that they would use a shared timebase. This was in DocumentTimeline::currentTime():
+
+            auto& mainDocumentTimeline = m_document->timeline();
+            if (&mainDocumentTimeline != this) {
+                if (auto mainDocumentTimelineCurrentTime = mainDocumentTimeline.currentTime())
+                    return *mainDocumentTimelineCurrentTime - m_originTime;
+                return WTF::nullopt;
+            }
+
+        We now move the currentTime caching at the DocumentTimelinesController level which ensures all DocumentTimeline
+        objects attached to a given Document use the exact same currentTime(). This prompted some overdue refactoring
+        where also all the related animation suspension code is moved from DocumentTimeline up to DocumentTimelinesController.
+
+        * animation/DocumentTimeline.cpp:
+        (WebCore::DocumentTimeline::detachFromDocument):
+        (WebCore::DocumentTimeline::suspendAnimations):
+        (WebCore::DocumentTimeline::resumeAnimations):
+        (WebCore::DocumentTimeline::animationsAreSuspended const):
+        (WebCore::DocumentTimeline::currentTime):
+        (WebCore::DocumentTimeline::scheduleAnimationResolution):
+        (WebCore::DocumentTimeline::documentWillUpdateAnimationsAndSendEvents):
+        (WebCore::DocumentTimeline::animationsAreSuspended): Deleted.
+        (WebCore::DocumentTimeline::liveCurrentTime const): Deleted.
+        (WebCore::DocumentTimeline::cacheCurrentTime): Deleted.
+        (WebCore::DocumentTimeline::maybeClearCachedCurrentTime): Deleted.
+        * animation/DocumentTimeline.h:
+        * animation/DocumentTimelinesController.cpp:
+        (WebCore::DocumentTimelinesController::detachFromDocument):
+        (WebCore::DocumentTimelinesController::updateAnimationsAndSendEvents):
+        (WebCore::DocumentTimelinesController::suspendAnimations):
+        (WebCore::DocumentTimelinesController::resumeAnimations):
+        (WebCore::DocumentTimelinesController::animationsAreSuspended const):
+        (WebCore::DocumentTimelinesController::liveCurrentTime const):
+        (WebCore::DocumentTimelinesController::currentTime):
+        (WebCore::DocumentTimelinesController::cacheCurrentTime):
+        (WebCore::DocumentTimelinesController::maybeClearCachedCurrentTime):
+        * animation/DocumentTimelinesController.h:
+        * dom/Document.cpp:
+        (WebCore::Document::didBecomeCurrentDocumentInFrame):
+        (WebCore::Document::resume):
+        * dom/Document.h:
+        * page/Frame.cpp:
+        (WebCore::Frame::clearTimers):
+        (WebCore::Frame::resumeActiveDOMObjectsAndAnimations):
+        * page/Page.cpp:
+        (WebCore::Page::setIsVisibleInternal):
+        (WebCore::Page::hiddenPageCSSAnimationSuspensionStateChanged):
+        * testing/Internals.cpp:
+        (WebCore::Internals::animationsAreSuspended const):
+        (WebCore::Internals::suspendAnimations const):
+        (WebCore::Internals::resumeAnimations const):
+
 2020-05-07  Simon Fraser  <simon.fraser@apple.com>
 
         REGRESSION (r252161): Animation of box-shadow with border-radius set is flashy
index b48b97a..e48e0f6 100644 (file)
@@ -29,7 +29,6 @@
 #include "AnimationEventBase.h"
 #include "CSSAnimation.h"
 #include "CSSTransition.h"
-#include "DOMWindow.h"
 #include "DeclarativeAnimation.h"
 #include "Document.h"
 #include "DocumentTimelinesController.h"
@@ -44,7 +43,6 @@
 #include "RenderLayer.h"
 #include "RenderLayerBacking.h"
 #include "Settings.h"
-#include <JavaScriptCore/VM.h>
 
 namespace WebCore {
 
@@ -92,7 +90,6 @@ void DocumentTimeline::detachFromDocument()
         controller->removeTimeline(*this);
 
     m_pendingAnimationEvents.clear();
-    m_currentTimeClearingTaskQueue.close();
     m_elementsWithRunningAcceleratedAnimations.clear();
 
     auto& animationsToRemove = m_animations;
@@ -220,17 +217,9 @@ Seconds DocumentTimeline::animationInterval() const
 
 void DocumentTimeline::suspendAnimations()
 {
-    if (animationsAreSuspended())
-        return;
-
-    if (!m_cachedCurrentTime)
-        m_cachedCurrentTime = liveCurrentTime();
-
     for (const auto& animation : m_animations)
         animation->setSuspended(true);
 
-    m_isSuspended = true;
-
     applyPendingAcceleratedAnimations();
 
     clearTickScheduleTimer();
@@ -238,22 +227,15 @@ void DocumentTimeline::suspendAnimations()
 
 void DocumentTimeline::resumeAnimations()
 {
-    if (!animationsAreSuspended())
-        return;
-
-    m_cachedCurrentTime = WTF::nullopt;
-
-    m_isSuspended = false;
-
     for (const auto& animation : m_animations)
         animation->setSuspended(false);
 
     scheduleAnimationResolution();
 }
 
-bool DocumentTimeline::animationsAreSuspended()
+bool DocumentTimeline::animationsAreSuspended() const
 {
-    return m_isSuspended;
+    return controller() && controller()->animationsAreSuspended();
 }
 
 unsigned DocumentTimeline::numberOfActiveAnimationsForTesting() const
@@ -266,54 +248,14 @@ unsigned DocumentTimeline::numberOfActiveAnimationsForTesting() const
     return count;
 }
 
-ReducedResolutionSeconds DocumentTimeline::liveCurrentTime() const
-{
-    return m_document->domWindow()->nowTimestamp();
-}
-
 Optional<Seconds> DocumentTimeline::currentTime()
 {
-    if (!m_document || !m_document->domWindow())
-        return AnimationTimeline::currentTime();
-
-    auto& mainDocumentTimeline = m_document->timeline();
-    if (&mainDocumentTimeline != this) {
-        if (auto mainDocumentTimelineCurrentTime = mainDocumentTimeline.currentTime())
-            return *mainDocumentTimelineCurrentTime - m_originTime;
+    if (auto* controller = this->controller()) {
+        if (auto currentTime = controller->currentTime())
+            return *currentTime - m_originTime;
         return WTF::nullopt;
     }
-
-    if (!m_cachedCurrentTime)
-        cacheCurrentTime(liveCurrentTime());
-    
-    return m_cachedCurrentTime.value() - m_originTime;
-}
-
-void DocumentTimeline::cacheCurrentTime(ReducedResolutionSeconds newCurrentTime)
-{
-    ASSERT(m_document);
-
-    m_cachedCurrentTime = newCurrentTime;
-    // We want to be sure to keep this time cached until we've both finished running JS and finished updating
-    // animations, so we schedule the invalidation task and register a whenIdle callback on the VM, which will
-    // fire syncronously if no JS is running.
-    m_waitingOnVMIdle = true;
-    if (!m_currentTimeClearingTaskQueue.hasPendingTasks())
-        m_currentTimeClearingTaskQueue.enqueueTask(std::bind(&DocumentTimeline::maybeClearCachedCurrentTime, this));
-    m_document->vm().whenIdle([this, protectedThis = makeRefPtr(this)]() {
-        m_waitingOnVMIdle = false;
-        maybeClearCachedCurrentTime();
-    });
-}
-
-void DocumentTimeline::maybeClearCachedCurrentTime()
-{
-    // We want to make sure we only clear the cached current time if we're not currently running
-    // JS or waiting on all current animation updating code to have completed. This is so that
-    // we're guaranteed to have a consistent current time reported for all work happening in a given
-    // JS frame or throughout updating animations in WebCore.
-    if (!m_isSuspended && !m_waitingOnVMIdle && !m_currentTimeClearingTaskQueue.hasPendingTasks())
-        m_cachedCurrentTime = WTF::nullopt;
+    return AnimationTimeline::currentTime();
 }
 
 void DocumentTimeline::animationTimingDidChange(WebAnimation& animation)
@@ -332,7 +274,7 @@ void DocumentTimeline::removeAnimation(WebAnimation& animation)
 
 void DocumentTimeline::scheduleAnimationResolution()
 {
-    if (m_isSuspended || m_animationResolutionScheduled || !m_document || !m_document->page())
+    if (animationsAreSuspended() || m_animationResolutionScheduled || !m_document || !m_document->page())
         return;
 
     // We need some relevant animations or pending events to proceed.
@@ -353,19 +295,13 @@ bool DocumentTimeline::shouldRunUpdateAnimationsAndSendEventsIgnoringSuspensionS
     return !m_animations.isEmpty() || !m_pendingAnimationEvents.isEmpty() || !m_acceleratedAnimationsPendingRunningStateChange.isEmpty();
 }
 
-DocumentTimeline::ShouldUpdateAnimationsAndSendEvents DocumentTimeline::documentWillUpdateAnimationsAndSendEvents(ReducedResolutionSeconds timestamp)
+DocumentTimeline::ShouldUpdateAnimationsAndSendEvents DocumentTimeline::documentWillUpdateAnimationsAndSendEvents()
 {
-    // We need to freeze the current time even if no animation is running.
-    // document.timeline.currentTime may be called from a rAF callback and
-    // it has to match the rAF timestamp.
-    if (!m_isSuspended || !shouldRunUpdateAnimationsAndSendEventsIgnoringSuspensionState())
-        cacheCurrentTime(timestamp);
-
     // Updating animations and sending events may invalidate the timing of some animations, so we must set the m_animationResolutionScheduled
     // flag to false prior to running that procedure to allow animation with timing model updates to schedule updates.
     bool wasAnimationResolutionScheduled = std::exchange(m_animationResolutionScheduled, false);
 
-    if (!wasAnimationResolutionScheduled || m_isSuspended || !shouldRunUpdateAnimationsAndSendEventsIgnoringSuspensionState())
+    if (!wasAnimationResolutionScheduled || animationsAreSuspended() || !shouldRunUpdateAnimationsAndSendEventsIgnoringSuspensionState())
         return DocumentTimeline::ShouldUpdateAnimationsAndSendEvents::No;
 
     m_numberOfAnimationTimelineInvalidationsForTesting++;
index a09a435..50792ba 100644 (file)
 
 #include "AnimationTimeline.h"
 #include "DocumentTimelineOptions.h"
-#include "GenericTaskQueue.h"
-#include "ReducedResolutionSeconds.h"
 #include "Timer.h"
-#include <wtf/Markable.h>
 #include <wtf/Ref.h>
 #include <wtf/WeakPtr.h>
 
@@ -75,15 +72,15 @@ public:
     void enqueueAnimationEvent(AnimationEventBase&);
     
     enum class ShouldUpdateAnimationsAndSendEvents : uint8_t { Yes, No };
-    ShouldUpdateAnimationsAndSendEvents documentWillUpdateAnimationsAndSendEvents(ReducedResolutionSeconds);
+    ShouldUpdateAnimationsAndSendEvents documentWillUpdateAnimationsAndSendEvents();
     void removeReplacedAnimations();
     AnimationEvents prepareForPendingAnimationEventsDispatch();
     void documentDidUpdateAnimationsAndSendEvents();
 
     WEBCORE_EXPORT Seconds animationInterval() const;
-    WEBCORE_EXPORT void suspendAnimations();
-    WEBCORE_EXPORT void resumeAnimations();
-    WEBCORE_EXPORT bool animationsAreSuspended();
+    void suspendAnimations();
+    void resumeAnimations();
+    bool animationsAreSuspended() const;
     WEBCORE_EXPORT unsigned numberOfActiveAnimationsForTesting() const;
     WEBCORE_EXPORT Vector<std::pair<String, double>> acceleratedAnimationsForElement(Element&) const;    
     WEBCORE_EXPORT unsigned numberOfAnimationTimelineInvalidationsForTesting() const;
@@ -92,10 +89,7 @@ private:
     DocumentTimeline(Document&, Seconds);
 
     DocumentTimelinesController* controller() const;
-    ReducedResolutionSeconds liveCurrentTime() const;
     void applyPendingAcceleratedAnimations();
-    void cacheCurrentTime(ReducedResolutionSeconds);
-    void maybeClearCachedCurrentTime();
     void scheduleInvalidationTaskIfNeeded();
     void scheduleAnimationResolution();
     void clearTickScheduleTimer();
@@ -106,16 +100,12 @@ private:
     bool shouldRunUpdateAnimationsAndSendEventsIgnoringSuspensionState() const;
 
     Timer m_tickScheduleTimer;
-    GenericTaskQueue<Timer> m_currentTimeClearingTaskQueue;
     HashSet<RefPtr<WebAnimation>> m_acceleratedAnimationsPendingRunningStateChange;
     HashSet<Element*> m_elementsWithRunningAcceleratedAnimations;
     AnimationEvents m_pendingAnimationEvents;
     WeakPtr<Document> m_document;
-    Markable<Seconds, Seconds::MarkableTraits> m_cachedCurrentTime;
     Seconds m_originTime;
     unsigned m_numberOfAnimationTimelineInvalidationsForTesting { 0 };
-    bool m_isSuspended { false };
-    bool m_waitingOnVMIdle { false };
     bool m_animationResolutionScheduled { false };
     bool m_shouldScheduleAnimationResolutionForNewPendingEvents { true };
 };
index 3abd37e..67fc4ff 100644 (file)
 
 #include "AnimationEventBase.h"
 #include "CSSTransition.h"
+#include "DOMWindow.h"
 #include "Document.h"
 #include "DocumentTimeline.h"
 #include "EventLoop.h"
 #include "WebAnimation.h"
 #include "WebAnimationTypes.h"
+#include <JavaScriptCore/VM.h>
 
 namespace WebCore {
 
@@ -57,6 +59,8 @@ void DocumentTimelinesController::removeTimeline(DocumentTimeline& timeline)
 
 void DocumentTimelinesController::detachFromDocument()
 {
+    m_currentTimeClearingTaskQueue.close();
+
     while (!m_timelines.computesEmpty())
         m_timelines.begin()->detachFromDocument();
 }
@@ -70,12 +74,18 @@ void DocumentTimelinesController::updateAnimationsAndSendEvents(ReducedResolutio
     for (auto& timeline : m_timelines)
         protectedTimelines.append(&timeline);
 
+    // We need to freeze the current time even if no animation is running.
+    // document.timeline.currentTime may be called from a rAF callback and
+    // it has to match the rAF timestamp.
+    if (!m_isSuspended)
+        cacheCurrentTime(timestamp);
+
     // 1. Update the current time of all timelines associated with doc passing now as the timestamp.
     Vector<DocumentTimeline*> timelinesToUpdate;
     Vector<RefPtr<WebAnimation>> animationsToRemove;
     Vector<RefPtr<CSSTransition>> completedTransitions;
     for (auto& timeline : protectedTimelines) {
-        auto shouldUpdateAnimationsAndSendEvents = timeline->documentWillUpdateAnimationsAndSendEvents(timestamp);
+        auto shouldUpdateAnimationsAndSendEvents = timeline->documentWillUpdateAnimationsAndSendEvents();
         if (shouldUpdateAnimationsAndSendEvents == DocumentTimeline::ShouldUpdateAnimationsAndSendEvents::No)
             continue;
 
@@ -156,5 +166,80 @@ void DocumentTimelinesController::updateAnimationsAndSendEvents(ReducedResolutio
         timeline->documentDidUpdateAnimationsAndSendEvents();
 }
 
+void DocumentTimelinesController::suspendAnimations()
+{
+    if (m_isSuspended)
+        return;
+
+    if (!m_cachedCurrentTime)
+        m_cachedCurrentTime = liveCurrentTime();
+
+    for (auto& timeline : m_timelines)
+        timeline.suspendAnimations();
+
+    m_isSuspended = true;
+}
+
+void DocumentTimelinesController::resumeAnimations()
+{
+    if (!m_isSuspended)
+        return;
+
+    m_cachedCurrentTime = WTF::nullopt;
+
+    m_isSuspended = false;
+
+    for (auto& timeline : m_timelines)
+        timeline.resumeAnimations();
+}
+
+bool DocumentTimelinesController::animationsAreSuspended() const
+{
+    return m_isSuspended;
+}
+
+ReducedResolutionSeconds DocumentTimelinesController::liveCurrentTime() const
+{
+    return m_document.domWindow()->nowTimestamp();
+}
+
+Optional<Seconds> DocumentTimelinesController::currentTime()
+{
+    if (!m_document.domWindow())
+        return WTF::nullopt;
+
+    if (!m_cachedCurrentTime)
+        cacheCurrentTime(liveCurrentTime());
+
+    return *m_cachedCurrentTime;
+}
+
+void DocumentTimelinesController::cacheCurrentTime(ReducedResolutionSeconds newCurrentTime)
+{
+    m_cachedCurrentTime = newCurrentTime;
+    // We want to be sure to keep this time cached until we've both finished running JS and finished updating
+    // animations, so we schedule the invalidation task and register a whenIdle callback on the VM, which will
+    // fire syncronously if no JS is running.
+    m_waitingOnVMIdle = true;
+    if (!m_currentTimeClearingTaskQueue.hasPendingTasks())
+        m_currentTimeClearingTaskQueue.enqueueTask(std::bind(&DocumentTimelinesController::maybeClearCachedCurrentTime, this));
+    // We extent the associated Document's lifecycle until the VM became idle since the DocumentTimelinesController
+    // is owned by the Document.
+    m_document.vm().whenIdle([this, protectedDocument = makeRefPtr(m_document)]() {
+        m_waitingOnVMIdle = false;
+        maybeClearCachedCurrentTime();
+    });
+}
+
+void DocumentTimelinesController::maybeClearCachedCurrentTime()
+{
+    // We want to make sure we only clear the cached current time if we're not currently running
+    // JS or waiting on all current animation updating code to have completed. This is so that
+    // we're guaranteed to have a consistent current time reported for all work happening in a given
+    // JS frame or throughout updating animations in WebCore.
+    if (!m_isSuspended && !m_waitingOnVMIdle && !m_currentTimeClearingTaskQueue.hasPendingTasks())
+        m_cachedCurrentTime = WTF::nullopt;
+}
+
 } // namespace WebCore
 
index 906c17d..d749a06 100644 (file)
 
 #pragma once
 
+#include "GenericTaskQueue.h"
 #include "ReducedResolutionSeconds.h"
+#include "Timer.h"
+#include <wtf/Markable.h>
+#include <wtf/Seconds.h>
 #include <wtf/WeakHashSet.h>
 
 namespace WebCore {
@@ -46,14 +50,28 @@ public:
     void detachFromDocument();
     void updateAnimationsAndSendEvents(ReducedResolutionSeconds);
 
+    Optional<Seconds> currentTime();
+
+    WEBCORE_EXPORT void suspendAnimations();
+    WEBCORE_EXPORT void resumeAnimations();
+    WEBCORE_EXPORT bool animationsAreSuspended() const;
+
 private:
     struct AnimationsToProcess {
         Vector<RefPtr<WebAnimation>> animationsToRemove;
         Vector<RefPtr<CSSTransition>> completedTransitions;
     };
 
+    ReducedResolutionSeconds liveCurrentTime() const;
+    void cacheCurrentTime(ReducedResolutionSeconds);
+    void maybeClearCachedCurrentTime();
+
     WeakHashSet<DocumentTimeline> m_timelines;
+    GenericTaskQueue<Timer> m_currentTimeClearingTaskQueue;
     Document& m_document;
+    Markable<Seconds, Seconds::MarkableTraits> m_cachedCurrentTime;
+    bool m_isSuspended { false };
+    bool m_waitingOnVMIdle { false };
 };
 
 } // namespace WebCore
index 2cc9a86..e01ad5c 100644 (file)
@@ -2403,16 +2403,16 @@ void Document::didBecomeCurrentDocumentInFrame()
     // back/forward cache, or simply newly created).
     if (m_frame->activeDOMObjectsAndAnimationsSuspended()) {
         if (RuntimeEnabledFeatures::sharedFeatures().webAnimationsCSSIntegrationEnabled()) {
-            if (auto* timeline = existingTimeline())
-                timeline->suspendAnimations();
+            if (m_timelinesController)
+                m_timelinesController->suspendAnimations();
         } else
             m_frame->legacyAnimation().suspendAnimationsForDocument(this);
         suspendScheduledTasks(ReasonForSuspension::PageWillBeSuspended);
     } else {
         resumeScheduledTasks(ReasonForSuspension::PageWillBeSuspended);
         if (RuntimeEnabledFeatures::sharedFeatures().webAnimationsCSSIntegrationEnabled()) {
-            if (auto* timeline = existingTimeline())
-                timeline->resumeAnimations();
+            if (m_timelinesController)
+                m_timelinesController->resumeAnimations();
         } else
             m_frame->legacyAnimation().resumeAnimationsForDocument(this);
     }
@@ -5399,8 +5399,8 @@ void Document::resume(ReasonForSuspension reason)
     m_frame->loader().client().dispatchDidBecomeFrameset(isFrameSet());
 
     if (RuntimeEnabledFeatures::sharedFeatures().webAnimationsCSSIntegrationEnabled()) {
-        if (auto* timeline = existingTimeline())
-            timeline->resumeAnimations();  
+        if (m_timelinesController)
+            m_timelinesController->resumeAnimations();  
     } else
         m_frame->legacyAnimation().resumeAnimationsForDocument(this);
 
index d8280a7..77ecb7a 100644 (file)
@@ -1496,7 +1496,7 @@ public:
     Vector<RefPtr<WebAnimation>> getAnimations();
     Vector<RefPtr<WebAnimation>> matchingAnimations(const WTF::Function<bool(Element&)>&);
     DocumentTimelinesController* timelinesController() const { return m_timelinesController.get(); }
-    DocumentTimelinesController& ensureTimelinesController();
+    WEBCORE_EXPORT DocumentTimelinesController& ensureTimelinesController();
 
 #if ENABLE(ATTACHMENT_ELEMENT)
     void registerAttachmentIdentifier(const String&);
index 39dea4d..76066f5 100644 (file)
@@ -718,8 +718,8 @@ void Frame::clearTimers(FrameView *view, Document *document)
     if (view) {
         view->layoutContext().unscheduleLayout();
         if (RuntimeEnabledFeatures::sharedFeatures().webAnimationsCSSIntegrationEnabled()) {
-            if (auto* timeline = document->existingTimeline())
-                timeline->suspendAnimations();
+            if (auto* timelines = document->timelinesController())
+                timelines->suspendAnimations();
         } else
             view->frame().legacyAnimation().suspendAnimationsForDocument(document);
         view->frame().eventHandler().stopAutoscrollTimer();
@@ -1013,8 +1013,8 @@ void Frame::resumeActiveDOMObjectsAndAnimations()
     // Frame::clearTimers() suspended animations and pending relayouts.
 
     if (RuntimeEnabledFeatures::sharedFeatures().webAnimationsCSSIntegrationEnabled()) {
-        if (auto* timeline = m_doc->existingTimeline())
-            timeline->resumeAnimations();
+        if (auto* timelines = m_doc->timelinesController())
+            timelines->resumeAnimations();
     } else
         legacyAnimation().resumeAnimationsForDocument(m_doc.get());
     if (m_view)
index bb6e03c..06818c8 100644 (file)
@@ -2052,8 +2052,8 @@ void Page::setIsVisibleInternal(bool isVisible)
         if (m_settings->hiddenPageCSSAnimationSuspensionEnabled()) {
             if (RuntimeEnabledFeatures::sharedFeatures().webAnimationsCSSIntegrationEnabled()) {
                 forEachDocument([] (Document& document) {
-                    if (auto* timeline = document.existingTimeline())
-                        timeline->resumeAnimations();
+                    if (auto* timelines = document.timelinesController())
+                        timelines->resumeAnimations();
                 });
             } else
                 mainFrame().legacyAnimation().resumeAnimations();
@@ -2076,8 +2076,8 @@ void Page::setIsVisibleInternal(bool isVisible)
         if (m_settings->hiddenPageCSSAnimationSuspensionEnabled()) {
             if (RuntimeEnabledFeatures::sharedFeatures().webAnimationsCSSIntegrationEnabled()) {
                 forEachDocument([] (Document& document) {
-                    if (auto* timeline = document.existingTimeline())
-                        timeline->suspendAnimations();
+                    if (auto* timelines = document.timelinesController())
+                        timelines->suspendAnimations();
                 });
             } else
                 mainFrame().legacyAnimation().suspendAnimations();
@@ -2427,11 +2427,11 @@ void Page::hiddenPageCSSAnimationSuspensionStateChanged()
     if (!isVisible()) {
         if (RuntimeEnabledFeatures::sharedFeatures().webAnimationsCSSIntegrationEnabled()) {
             forEachDocument([&] (Document& document) {
-                if (auto* timeline = document.existingTimeline()) {
+                if (auto* timelines = document.timelinesController()) {
                     if (m_settings->hiddenPageCSSAnimationSuspensionEnabled())
-                        timeline->suspendAnimations();
+                        timelines->suspendAnimations();
                     else
-                        timeline->resumeAnimations();
+                        timelines->resumeAnimations();
                 }
             });
         } else {
index dd12cee..fa95109 100644 (file)
@@ -1071,7 +1071,7 @@ ExceptionOr<bool> Internals::animationsAreSuspended() const
         return Exception { InvalidAccessError };
 
     if (RuntimeEnabledFeatures::sharedFeatures().webAnimationsCSSIntegrationEnabled())
-        return document->timeline().animationsAreSuspended();
+        return document->ensureTimelinesController().animationsAreSuspended();
     return document->frame()->legacyAnimation().animationsAreSuspendedForDocument(document);
 }
 
@@ -1099,10 +1099,10 @@ ExceptionOr<void> Internals::suspendAnimations() const
         return Exception { InvalidAccessError };
 
     if (RuntimeEnabledFeatures::sharedFeatures().webAnimationsCSSIntegrationEnabled()) {
-        document->timeline().suspendAnimations();
+        document->ensureTimelinesController().suspendAnimations();
         for (Frame* frame = document->frame(); frame; frame = frame->tree().traverseNext()) {
             if (Document* document = frame->document())
-                document->timeline().suspendAnimations();
+                document->ensureTimelinesController().suspendAnimations();
         }
     } else {
         document->frame()->legacyAnimation().suspendAnimationsForDocument(document);
@@ -1123,10 +1123,10 @@ ExceptionOr<void> Internals::resumeAnimations() const
         return Exception { InvalidAccessError };
 
     if (RuntimeEnabledFeatures::sharedFeatures().webAnimationsCSSIntegrationEnabled()) {
-        document->timeline().resumeAnimations();
+        document->ensureTimelinesController().resumeAnimations();
         for (Frame* frame = document->frame(); frame; frame = frame->tree().traverseNext()) {
             if (Document* document = frame->document())
-                document->timeline().resumeAnimations();
+                document->ensureTimelinesController().resumeAnimations();
         }
     } else {
         document->frame()->legacyAnimation().resumeAnimationsForDocument(document);