WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
[patch]
Patch
bug-203586-20191030153944.patch (text/plain), 56.12 KB, created by
Daniel Bates
on 2019-10-30 15:39:45 PDT
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Daniel Bates
Created:
2019-10-30 15:39:45 PDT
Size:
56.12 KB
patch
obsolete
>Subversion Revision: 251813 >diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog >index c07239d247152cb331ee06c7e9ad39732c2e3eb4..8536d51d9c5bfe7cb898ede8fe02df7e6f3091fa 100644 >--- a/Source/WebCore/ChangeLog >+++ b/Source/WebCore/ChangeLog >@@ -1,3 +1,145 @@ >+2019-10-29 Daniel Bates <dabates@apple.com> >+ >+ Events with the same firing time can fire in the wrong order after page suspend and resume >+ https://bugs.webkit.org/show_bug.cgi?id=203586 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ This patch does two things: >+ 1. Patches SuspendableTimer so that it can save and restore the timer's insertion order >+ in the timer heap. >+ 2. Patches ActiveDOMObject::suspend() to take a MonotonicTime object that represents the >+ time of suspension. >+ >+ Together these changes ensure that timers suspended with the same firing time are resumed in >+ a way that makes their suspension unobservable. That is, they have the same relative order in >+ the timer heap on resume as they did on suspend. >+ >+ Back story: >+ >+ Pausing of DOM timers ensured correct firing order on resume as far as I can tell by code >+ inspection up to r12129. (I think internal WebCore timers may not have, but I didn't check). >+ Revision r12129 changed a QMap, whose iteration was sorted by key â the timer's timeoutID, >+ to a HashMap, whose iteration order is non-deterministic. (Keep in mind that the timeoutID is >+ a monontonically increasing number). Over time the code for pausing and resuming timers was >+ significantly refactored to what it is today: an ActiveDOMObject-derived object called a >+ SuspendableTimer and the script execution context maintains a HashSet of these objects. Neither >+ the SuspendableTimer nor the HashSet preserves the timer's insertion order in the timer heap. >+ Although the latter could be changed to a ListHashSet to make up for SuspendableTimer not saving >+ the timer's insertion order on suspend it would only fix the issue with respect to the >+ script execution context. That is, any other code, present or future, that maintains a >+ collection of timers (or abstractly ActiveDOMObjects) that are suspended and resumed would >+ also need to use an ordered collection. Moreover, additional work would be necesessary to ensure >+ collections that represents a subset of timers correctly suspend and resume with respect to all >+ timers. For these reasons, I chose to to have SuspendableTimer save and restore the timer's >+ insertion order. At the time of writing there is a theoretical window for equal firing time >+ timers to still be resumed out-of-order: when the insertion order counter stored in ThreadTimers >+ wraps around. The counter is represented as an unsigned. Such a situtation is unlikely to >+ happen as it requires two timers be scheduled: one with a future time and another scheduled >+ exactly at that future time plus the counter had to have wrapped around before then. This >+ theoretical window could be practically addressed by increasing the counter's width to a 64-bit type, uint64_t. >+ >+ Test: fast/dom/simultaneously-registered-timer-fire-order-after-navigation-back.html >+ >+ * Modules/geolocation/Geolocation.cpp: >+ (WebCore::Geolocation::suspend): >+ * Modules/geolocation/Geolocation.h: >+ * Modules/mediastream/RTCPeerConnection.cpp: >+ (WebCore::RTCPeerConnection::suspend): >+ * Modules/mediastream/RTCPeerConnection.h: >+ * Modules/notifications/Notification.cpp: >+ (WebCore::Notification::suspend): >+ * Modules/notifications/Notification.h: >+ * Modules/webaudio/AudioContext.cpp: >+ (WebCore::AudioContext::suspend): >+ * Modules/webaudio/AudioContext.h: >+ * Modules/websockets/WebSocket.cpp: >+ (WebCore::WebSocket::suspend): >+ * Modules/websockets/WebSocket.h: >+ * dom/ActiveDOMObject.cpp: >+ (WebCore::ActiveDOMObject::suspendIfNeeded): >+ (WebCore::ActiveDOMObject::suspend): >+ * dom/ActiveDOMObject.h: >+ * dom/Document.cpp: >+ (WebCore::Document::suspendActiveDOMObjects): >+ (WebCore::Document::suspend): >+ (WebCore::Document::suspendScheduledTasks): >+ * dom/Document.h: >+ * dom/GenericEventQueue.cpp: >+ (WebCore::GenericEventQueueBase<T>::suspend): >+ * dom/GenericEventQueue.h: >+ * dom/ScriptExecutionContext.cpp: >+ (WebCore::ScriptExecutionContext::suspendActiveDOMObjects): >+ (WebCore::ScriptExecutionContext::suspendActiveDOMObjectIfNeeded): >+ * dom/ScriptExecutionContext.h: >+ * history/CachedFrame.cpp: >+ (WebCore::CachedFrame::CachedFrame): >+ * history/CachedFrame.h: >+ * history/CachedPage.cpp: >+ (WebCore::CachedPage::CachedPage): Compute the suspension begin time once. Use this time to start >+ the expiration timer and to pass to the CachedFrame so that it can suspend its active DOM objects >+ and child frames's active DOM objects with respect to this time. >+ * html/HTMLMarqueeElement.cpp: >+ (WebCore::HTMLMarqueeElement::suspend): >+ * html/HTMLMarqueeElement.h: >+ * html/HTMLMediaElement.cpp: >+ (WebCore::HTMLMediaElement::suspend): >+ * html/HTMLMediaElement.h: >+ * html/HTMLSourceElement.cpp: >+ (WebCore::HTMLSourceElement::suspend): >+ * html/HTMLSourceElement.h: >+ * html/canvas/WebGLRenderingContextBase.cpp: >+ (WebCore::WebGLRenderingContextBase::suspend): >+ * html/canvas/WebGLRenderingContextBase.h: >+ * page/EventSource.cpp: >+ (WebCore::EventSource::suspend): >+ * page/EventSource.h: >+ * page/SuspendableTimer.cpp: >+ (WebCore::SuspendableTimerBase::suspend): >+ (WebCore::SuspendableTimerBase::resume): >+ (WebCore::SuspendableTimerBase::startRepeating): >+ (WebCore::SuspendableTimerBase::startOneShot): >+ * page/SuspendableTimer.h: >+ * page/ios/DOMTimerHoldingTank.cpp: >+ (WebCore::DOMTimerHoldingTank::add): >+ * page/ios/DOMTimerHoldingTank.h: >+ * platform/SuspendableTaskQueue.cpp: >+ (WebCore::SuspendableTaskQueue::suspend): >+ * platform/SuspendableTaskQueue.h: >+ * platform/Timer.cpp: >+ (WebCore::TimerBase::start): Expose a protected variant that takes an optional insertion order. >+ SuspendableTimer makes use of this to resume with the the insertion order it saved on suspend. >+ (WebCore::TimerBase::setNextFireTime): >+ (WebCore::TimerBase::nextUnalignedFireInterval const): Modified to take a time to compute the unaligned >+ fire interval with respect to. Currently it computes with respect to MonotonicTime::now(), but >+ this is a bug because its possible for a timer scheduled in the future to return a smaller value >+ than a timer scheduled in the present if the timers are suspended in the opposite order (present, future) >+ and enough time has elapsed such that by the time this function is called to save the "future" timer >+ its at or past its firing time. Concrete example: Suppose time starts ticking at 0 and you have timers >+ A, B where A is scheduled to fire at time 5 and B is scheduled to fire at time 6. A is suspended at time >+ 3 and B is suspended at time 6, this can happen due to context switching or if there is enough code between >+ the suspension of these timers. If we resume at time 10 then that means we need to resume A such that it fires >+ from time 10 + (5 - 3) = 10 + 2 = 12 and resume B such that it fires at time 10 + (6 - 6) = 10 + 0 = 10 >+ if we want to give the illusion that the timers actually paused. But then B will fire before A, which >+ is not correct. Therefore, the caller must pass the suspension begin time to use when computing >+ the result of this function. >+ * platform/Timer.h: >+ (WebCore::TimerBase::insertionOrder const): Added. >+ (WebCore::TimerBase::start): Modified to be an inline function that turns around and calls the ::start() Optional<>- >+ taking variant passing a WTF::nullopt to keep the current behavior and share code. >+ * workers/Worker.cpp: >+ (WebCore::Worker::suspend): >+ * workers/Worker.h: >+ * workers/service/ServiceWorker.cpp: >+ (WebCore::ServiceWorker::suspend): >+ * workers/service/ServiceWorker.h: >+ * workers/service/ServiceWorkerRegistration.cpp: >+ (WebCore::ServiceWorkerRegistration::suspend): >+ * workers/service/ServiceWorkerRegistration.h: >+ * xml/XMLHttpRequest.cpp: >+ (WebCore::XMLHttpRequest::suspend): >+ * xml/XMLHttpRequest.h: >+ > 2019-09-13 Daniel Bates <dabates@apple.com> > > Support building WebKit for Mac Catalyst from Xcode >diff --git a/Source/WebCore/Modules/geolocation/Geolocation.cpp b/Source/WebCore/Modules/geolocation/Geolocation.cpp >index a256d2f8a42548c42ae3c955d9502cc0ad893d91..7d7a802ff8ffd6011ac666119b3e1817021586fe 100644 >--- a/Source/WebCore/Modules/geolocation/Geolocation.cpp >+++ b/Source/WebCore/Modules/geolocation/Geolocation.cpp >@@ -158,7 +158,7 @@ Page* Geolocation::page() const > return document() ? document()->page() : nullptr; > } > >-void Geolocation::suspend(ReasonForSuspension reason) >+void Geolocation::suspend(ReasonForSuspension reason, MonotonicTime beginTime) > { > if (reason == ReasonForSuspension::BackForwardCache) { > stop(); >@@ -171,7 +171,7 @@ void Geolocation::suspend(ReasonForSuspension reason) > > m_isSuspended = true; > m_resumeTimer.stop(); >- ActiveDOMObject::suspend(reason); >+ ActiveDOMObject::suspend(reason, beginTime); > } > > void Geolocation::resume() >diff --git a/Source/WebCore/Modules/geolocation/Geolocation.h b/Source/WebCore/Modules/geolocation/Geolocation.h >index ad2d3698a482766438aec35ac5a717842881b88f..990eea1af86eea22c0a01844cb476d126c7f5f62 100644 >--- a/Source/WebCore/Modules/geolocation/Geolocation.h >+++ b/Source/WebCore/Modules/geolocation/Geolocation.h >@@ -83,7 +83,7 @@ private: > > // ActiveDOMObject > void stop() override; >- void suspend(ReasonForSuspension) override; >+ void suspend(ReasonForSuspension, MonotonicTime) override; > void resume() override; > const char* activeDOMObjectName() const override; > >diff --git a/Source/WebCore/Modules/mediastream/RTCPeerConnection.cpp b/Source/WebCore/Modules/mediastream/RTCPeerConnection.cpp >index 9cca6b4c3811f656ce819771588266b1dc32cc1f..38a9b5138615798ba6a0da0c48537a8f576ee5fb 100644 >--- a/Source/WebCore/Modules/mediastream/RTCPeerConnection.cpp >+++ b/Source/WebCore/Modules/mediastream/RTCPeerConnection.cpp >@@ -504,7 +504,7 @@ bool RTCPeerConnection::shouldPreventEnteringBackForwardCache_DEPRECATED() const > return m_iceConnectionState == RTCIceConnectionState::Completed || m_iceConnectionState == RTCIceConnectionState::Connected; > } > >-void RTCPeerConnection::suspend(ReasonForSuspension reason) >+void RTCPeerConnection::suspend(ReasonForSuspension reason, MonotonicTime) > { > if (reason != ReasonForSuspension::BackForwardCache) > return; >diff --git a/Source/WebCore/Modules/mediastream/RTCPeerConnection.h b/Source/WebCore/Modules/mediastream/RTCPeerConnection.h >index 9690b9b73791e93dafbecdb692c4315a141824ea..8233b4788e46bafa2d1238bf45e2e15415c3674b 100644 >--- a/Source/WebCore/Modules/mediastream/RTCPeerConnection.h >+++ b/Source/WebCore/Modules/mediastream/RTCPeerConnection.h >@@ -212,7 +212,7 @@ private: > WEBCORE_EXPORT void stop() final; > const char* activeDOMObjectName() const final; > bool shouldPreventEnteringBackForwardCache_DEPRECATED() const final; >- void suspend(ReasonForSuspension) final; >+ void suspend(ReasonForSuspension, MonotonicTime) final; > void resume() final; > > void updateConnectionState(); >diff --git a/Source/WebCore/Modules/notifications/Notification.cpp b/Source/WebCore/Modules/notifications/Notification.cpp >index f432590205af7a8a08389b8947cc4f818bf6b5d5..d7daec93160eaf10c5480bce7414d86a500f8ec4 100644 >--- a/Source/WebCore/Modules/notifications/Notification.cpp >+++ b/Source/WebCore/Modules/notifications/Notification.cpp >@@ -133,7 +133,7 @@ void Notification::stop() > NotificationController::from(page)->client().notificationObjectDestroyed(this); > } > >-void Notification::suspend(ReasonForSuspension) >+void Notification::suspend(ReasonForSuspension, MonotonicTime) > { > close(); > } >diff --git a/Source/WebCore/Modules/notifications/Notification.h b/Source/WebCore/Modules/notifications/Notification.h >index 509117046f7e998fcec98d72ef948a9fe7d63ee7..81fb771cf16caacc2abba39233ff12de1486866c 100644 >--- a/Source/WebCore/Modules/notifications/Notification.h >+++ b/Source/WebCore/Modules/notifications/Notification.h >@@ -100,7 +100,7 @@ private: > > // ActiveDOMObject > const char* activeDOMObjectName() const final; >- void suspend(ReasonForSuspension); >+ void suspend(ReasonForSuspension, MonotonicTime); > void stop() final; > > void refEventTarget() final { ref(); } >diff --git a/Source/WebCore/Modules/webaudio/AudioContext.cpp b/Source/WebCore/Modules/webaudio/AudioContext.cpp >index d615c979bb575401e77832e49c5638d7998e6374..bfae5affdd152f6656efd79299572211ae20b36e 100644 >--- a/Source/WebCore/Modules/webaudio/AudioContext.cpp >+++ b/Source/WebCore/Modules/webaudio/AudioContext.cpp >@@ -340,7 +340,7 @@ void AudioContext::stop() > clear(); > } > >-void AudioContext::suspend(ReasonForSuspension) >+void AudioContext::suspend(ReasonForSuspension, MonotonicTime) > { > if (state() == State::Running) { > m_mediaSession->beginInterruption(PlatformMediaSession::PlaybackSuspended); >diff --git a/Source/WebCore/Modules/webaudio/AudioContext.h b/Source/WebCore/Modules/webaudio/AudioContext.h >index c20e39669785e972fbfc2829df2d386fc5174e1f..a9bc7df2353a16a1afca4154f1fbe28b27f89ee8 100644 >--- a/Source/WebCore/Modules/webaudio/AudioContext.h >+++ b/Source/WebCore/Modules/webaudio/AudioContext.h >@@ -335,7 +335,7 @@ private: > void derefNode(AudioNode&); > > // ActiveDOMObject API. >- void suspend(ReasonForSuspension) final; >+ void suspend(ReasonForSuspension, MonotonicTime) final; > void resume() final; > void stop() override; > const char* activeDOMObjectName() const override; >diff --git a/Source/WebCore/Modules/websockets/WebSocket.cpp b/Source/WebCore/Modules/websockets/WebSocket.cpp >index 94403f25b76a164627d5a4986875337826b39704..98e38e042db30fa838ad19f971013b031ed17f3b 100644 >--- a/Source/WebCore/Modules/websockets/WebSocket.cpp >+++ b/Source/WebCore/Modules/websockets/WebSocket.cpp >@@ -495,7 +495,7 @@ void WebSocket::contextDestroyed() > ActiveDOMObject::contextDestroyed(); > } > >-void WebSocket::suspend(ReasonForSuspension reason) >+void WebSocket::suspend(ReasonForSuspension reason, MonotonicTime) > { > if (m_resumeTimer.isActive()) > m_resumeTimer.stop(); >diff --git a/Source/WebCore/Modules/websockets/WebSocket.h b/Source/WebCore/Modules/websockets/WebSocket.h >index abd225be58fc98d2629d6697cbdd8daef856f3b3..d1ca0f06234b996a37e457955263b2329bce5594 100644 >--- a/Source/WebCore/Modules/websockets/WebSocket.h >+++ b/Source/WebCore/Modules/websockets/WebSocket.h >@@ -106,7 +106,7 @@ private: > void dispatchOrQueueEvent(Ref<Event>&&); > > void contextDestroyed() final; >- void suspend(ReasonForSuspension) final; >+ void suspend(ReasonForSuspension, MonotonicTime) final; > void resume() final; > void stop() final; > const char* activeDOMObjectName() const final; >diff --git a/Source/WebCore/dom/ActiveDOMObject.cpp b/Source/WebCore/dom/ActiveDOMObject.cpp >index 6347ea204811d54a487448efe3d70fef14c65d4c..305faae178aa14fe01dedbefeda33441b4964f23 100644 >--- a/Source/WebCore/dom/ActiveDOMObject.cpp >+++ b/Source/WebCore/dom/ActiveDOMObject.cpp >@@ -82,7 +82,7 @@ ActiveDOMObject::~ActiveDOMObject() > m_scriptExecutionContext->willDestroyActiveDOMObject(*this); > } > >-void ActiveDOMObject::suspendIfNeeded() >+void ActiveDOMObject::suspendIfNeeded(MonotonicTime beginTime) > { > #if !ASSERT_DISABLED > ASSERT(!m_suspendIfNeededWasCalled); >@@ -91,7 +91,7 @@ void ActiveDOMObject::suspendIfNeeded() > if (!m_scriptExecutionContext) > return; > >- m_scriptExecutionContext->suspendActiveDOMObjectIfNeeded(*this); >+ m_scriptExecutionContext->suspendActiveDOMObjectIfNeeded(*this, beginTime); > } > > #if !ASSERT_DISABLED >@@ -108,7 +108,7 @@ bool ActiveDOMObject::hasPendingActivity() const > return m_pendingActivityCount; > } > >-void ActiveDOMObject::suspend(ReasonForSuspension) >+void ActiveDOMObject::suspend(ReasonForSuspension, MonotonicTime) > { > } > >diff --git a/Source/WebCore/dom/ActiveDOMObject.h b/Source/WebCore/dom/ActiveDOMObject.h >index 76b5de68a2ca4936a82f6b4e2dee6e39c8064fb8..18e26bb9e5389e452f040c1dbdcae869ae2950ad 100644 >--- a/Source/WebCore/dom/ActiveDOMObject.h >+++ b/Source/WebCore/dom/ActiveDOMObject.h >@@ -29,6 +29,7 @@ > #include "ContextDestructionObserver.h" > #include <wtf/Assertions.h> > #include <wtf/Forward.h> >+#include <wtf/MonotonicTime.h> > #include <wtf/RefCounted.h> > #include <wtf/Threading.h> > >@@ -47,7 +48,7 @@ class WEBCORE_EXPORT ActiveDOMObject : public ContextDestructionObserver { > public: > // The suspendIfNeeded must be called exactly once after object construction to update > // the suspended state to match that of the ScriptExecutionContext. >- void suspendIfNeeded(); >+ void suspendIfNeeded(MonotonicTime = MonotonicTime::now()); > void assertSuspendIfNeededWasCalled() const; > > virtual bool hasPendingActivity() const; >@@ -61,7 +62,7 @@ public: > // These three functions must not have a side effect of creating or destroying > // any ActiveDOMObject. That means they must not result in calls to arbitrary JavaScript. > virtual bool shouldPreventEnteringBackForwardCache_DEPRECATED() const { return false; } // Please do not add new overrides for this function. >- virtual void suspend(ReasonForSuspension); >+ virtual void suspend(ReasonForSuspension, MonotonicTime beginTime); > virtual void resume(); > > // This function must not have a side effect of creating an ActiveDOMObject. >diff --git a/Source/WebCore/dom/Document.cpp b/Source/WebCore/dom/Document.cpp >index 1a99f8bdaa32dd1fdccf60ea2a1559c147b6e5b7..1d62107c8812674a62eb4e5a3d096b02193575b4 100644 >--- a/Source/WebCore/dom/Document.cpp >+++ b/Source/WebCore/dom/Document.cpp >@@ -2632,9 +2632,9 @@ void Document::platformSuspendOrStopActiveDOMObjects() > #endif > } > >-void Document::suspendActiveDOMObjects(ReasonForSuspension why) >+void Document::suspendActiveDOMObjects(ReasonForSuspension why, MonotonicTime beginTime) > { >- ScriptExecutionContext::suspendActiveDOMObjects(why); >+ ScriptExecutionContext::suspendActiveDOMObjects(why, beginTime); > suspendDeviceMotionAndOrientationUpdates(); > platformSuspendOrStopActiveDOMObjects(); > } >@@ -5135,7 +5135,7 @@ void Document::documentWillBecomeInactive() > renderView()->setIsInWindow(false); > } > >-void Document::suspend(ReasonForSuspension reason) >+void Document::suspend(ReasonForSuspension reason, MonotonicTime beginTime) > { > if (m_isSuspended) > return; >@@ -5172,7 +5172,7 @@ void Document::suspend(ReasonForSuspension reason) > setServiceWorkerConnection(nullptr); > #endif > >- suspendScheduledTasks(reason); >+ suspendScheduledTasks(reason, beginTime); > > ASSERT(m_frame); > m_frame->clearTimers(); >@@ -6141,7 +6141,7 @@ AbstractEventLoop& Document::eventLoop() > > } > >-void Document::suspendScheduledTasks(ReasonForSuspension reason) >+void Document::suspendScheduledTasks(ReasonForSuspension reason, MonotonicTime beginTime) > { > if (m_scheduledTasksAreSuspended) { > // A page may subsequently suspend DOM objects, say as part of handling a scroll or zoom gesture, after the >@@ -6152,7 +6152,7 @@ void Document::suspendScheduledTasks(ReasonForSuspension reason) > } > > suspendScriptedAnimationControllerCallbacks(); >- suspendActiveDOMObjects(reason); >+ suspendActiveDOMObjects(reason, beginTime); > scriptRunner().suspend(); > m_pendingTasksTimer.stop(); > >diff --git a/Source/WebCore/dom/Document.h b/Source/WebCore/dom/Document.h >index 732e91bcaed86ddcd0a28fcfd076834587be62e2..0b444bbc69db72302998243b91ac8da87b665475 100644 >--- a/Source/WebCore/dom/Document.h >+++ b/Source/WebCore/dom/Document.h >@@ -624,7 +624,7 @@ public: > > // Override ScriptExecutionContext methods to do additional work > WEBCORE_EXPORT bool shouldBypassMainWorldContentSecurityPolicy() const final; >- void suspendActiveDOMObjects(ReasonForSuspension) final; >+ void suspendActiveDOMObjects(ReasonForSuspension, MonotonicTime = MonotonicTime::now()) final; > void resumeActiveDOMObjects(ReasonForSuspension) final; > void stopActiveDOMObjects() final; > >@@ -1085,7 +1085,7 @@ public: > void unregisterForDocumentSuspensionCallbacks(Element&); > > void documentWillBecomeInactive(); >- void suspend(ReasonForSuspension); >+ void suspend(ReasonForSuspension, MonotonicTime); > void resume(ReasonForSuspension); > > void registerForMediaVolumeCallbacks(Element&); >@@ -1281,7 +1281,7 @@ public: > > bool isInDocumentWrite() { return m_writeRecursionDepth > 0; } > >- void suspendScheduledTasks(ReasonForSuspension); >+ void suspendScheduledTasks(ReasonForSuspension, MonotonicTime = MonotonicTime::now()); > void resumeScheduledTasks(ReasonForSuspension); > > #if ENABLE(CSS_DEVICE_ADAPTATION) >diff --git a/Source/WebCore/dom/GenericEventQueue.cpp b/Source/WebCore/dom/GenericEventQueue.cpp >index 82c2b8fbd1bb0c25664747aea00f3080ff15d24c..20932adc8c7f64897a3114fa32e385d1f8324c5a 100644 >--- a/Source/WebCore/dom/GenericEventQueue.cpp >+++ b/Source/WebCore/dom/GenericEventQueue.cpp >@@ -133,7 +133,7 @@ void GenericEventQueueBase<T>::setPaused(bool shouldPause) > } > > template<typename T> >-void GenericEventQueueBase<T>::suspend(ReasonForSuspension) >+void GenericEventQueueBase<T>::suspend(ReasonForSuspension, MonotonicTime) > { > if (m_isSuspended) > return; >diff --git a/Source/WebCore/dom/GenericEventQueue.h b/Source/WebCore/dom/GenericEventQueue.h >index 5375d55c535e5fc38d4f0bed235771196f956d36..6b8ebe671bc32373cc3290f71784a690bc42f572 100644 >--- a/Source/WebCore/dom/GenericEventQueue.h >+++ b/Source/WebCore/dom/GenericEventQueue.h >@@ -62,7 +62,7 @@ private: > > const char* activeDOMObjectName() const final; > void stop() final; >- void suspend(ReasonForSuspension) final; >+ void suspend(ReasonForSuspension, MonotonicTime) final; > void resume() final; > > void rescheduleAllEventsIfNeeded(); >diff --git a/Source/WebCore/dom/ScriptExecutionContext.cpp b/Source/WebCore/dom/ScriptExecutionContext.cpp >index 63c854216cddc3fb668564239de09a7f7a979717..76b4396689f4abd53bbf851f6fb8eeccc7735dab 100644 >--- a/Source/WebCore/dom/ScriptExecutionContext.cpp >+++ b/Source/WebCore/dom/ScriptExecutionContext.cpp >@@ -273,7 +273,7 @@ void ScriptExecutionContext::forEachActiveDOMObject(const Function<ShouldContinu > } > } > >-void ScriptExecutionContext::suspendActiveDOMObjects(ReasonForSuspension why) >+void ScriptExecutionContext::suspendActiveDOMObjects(ReasonForSuspension why, MonotonicTime beginTime) > { > checkConsistency(); > >@@ -287,8 +287,8 @@ void ScriptExecutionContext::suspendActiveDOMObjects(ReasonForSuspension why) > > m_activeDOMObjectsAreSuspended = true; > >- forEachActiveDOMObject([why](auto& activeDOMObject) { >- activeDOMObject.suspend(why); >+ forEachActiveDOMObject([&] (auto& activeDOMObject) { >+ activeDOMObject.suspend(why, beginTime); > return ShouldContinue::Yes; > }); > >@@ -328,11 +328,11 @@ void ScriptExecutionContext::stopActiveDOMObjects() > }); > } > >-void ScriptExecutionContext::suspendActiveDOMObjectIfNeeded(ActiveDOMObject& activeDOMObject) >+void ScriptExecutionContext::suspendActiveDOMObjectIfNeeded(ActiveDOMObject& activeDOMObject, MonotonicTime beginTime) > { > ASSERT(m_activeDOMObjects.contains(&activeDOMObject)); > if (m_activeDOMObjectsAreSuspended) >- activeDOMObject.suspend(m_reasonForSuspendingActiveDOMObjects); >+ activeDOMObject.suspend(m_reasonForSuspendingActiveDOMObjects, beginTime); > if (m_activeDOMObjectsAreStopped) > activeDOMObject.stop(); > } >diff --git a/Source/WebCore/dom/ScriptExecutionContext.h b/Source/WebCore/dom/ScriptExecutionContext.h >index a63347b8279d0efa48c354aa08169f4735cc487b..715504ba95d588cae561619feea387752353f6a0 100644 >--- a/Source/WebCore/dom/ScriptExecutionContext.h >+++ b/Source/WebCore/dom/ScriptExecutionContext.h >@@ -130,7 +130,7 @@ public: > > // Active objects can be asked to suspend even if canSuspendActiveDOMObjectsForDocumentSuspension() returns 'false' - > // step-by-step JS debugging is one example. >- virtual void suspendActiveDOMObjects(ReasonForSuspension); >+ virtual void suspendActiveDOMObjects(ReasonForSuspension, MonotonicTime beginTime); > virtual void resumeActiveDOMObjects(ReasonForSuspension); > virtual void stopActiveDOMObjects(); > >@@ -142,7 +142,7 @@ public: > void willDestroyActiveDOMObject(ActiveDOMObject&); > > // Called after the construction of an ActiveDOMObject to synchronize suspend state. >- void suspendActiveDOMObjectIfNeeded(ActiveDOMObject&); >+ void suspendActiveDOMObjectIfNeeded(ActiveDOMObject&, MonotonicTime beginTime); > > void didCreateDestructionObserver(ContextDestructionObserver&); > void willDestroyDestructionObserver(ContextDestructionObserver&); >diff --git a/Source/WebCore/history/CachedFrame.cpp b/Source/WebCore/history/CachedFrame.cpp >index 1e8c02ec7a89aa26163d15b6336a50c2155b4873..a7bcf73b502d168fcc0ed0be20d2b887da543db1 100644 >--- a/Source/WebCore/history/CachedFrame.cpp >+++ b/Source/WebCore/history/CachedFrame.cpp >@@ -145,7 +145,7 @@ void CachedFrameBase::restore() > frame->view()->didRestoreFromBackForwardCache(); > } > >-CachedFrame::CachedFrame(Frame& frame) >+CachedFrame::CachedFrame(Frame& frame, MonotonicTime beginTime) > : CachedFrameBase(frame) > { > #ifndef NDEBUG >@@ -166,14 +166,14 @@ CachedFrame::CachedFrame(Frame& frame) > > // Create the CachedFrames for all Frames in the FrameTree. > for (Frame* child = frame.tree().firstChild(); child; child = child->tree().nextSibling()) >- m_childFrames.append(makeUnique<CachedFrame>(*child)); >+ m_childFrames.append(makeUnique<CachedFrame>(*child, beginTime)); > > RELEASE_ASSERT(m_document->domWindow()); > RELEASE_ASSERT(m_document->frame()); > RELEASE_ASSERT(m_document->domWindow()->frame()); > > // Active DOM objects must be suspended before we cache the frame script data. >- m_document->suspend(ReasonForSuspension::BackForwardCache); >+ m_document->suspend(ReasonForSuspension::BackForwardCache, beginTime); > > if (auto* eventLoop = m_document->eventLoopIfExists()) > eventLoop->suspend(*m_document); >diff --git a/Source/WebCore/history/CachedFrame.h b/Source/WebCore/history/CachedFrame.h >index 1468c2bda44d3e45d32e8846d60ce417d0bc1bb5..69d034ed8ae40f9a109ec5b3f002c36da073b521 100644 >--- a/Source/WebCore/history/CachedFrame.h >+++ b/Source/WebCore/history/CachedFrame.h >@@ -70,7 +70,7 @@ protected: > class CachedFrame : private CachedFrameBase { > WTF_MAKE_FAST_ALLOCATED; > public: >- explicit CachedFrame(Frame&); >+ explicit CachedFrame(Frame&, MonotonicTime beginTime); > > void open(); > void clear(); >diff --git a/Source/WebCore/history/CachedPage.cpp b/Source/WebCore/history/CachedPage.cpp >index 08af39d45365c1b3f2faf2b7f81acebf829fb343..f9f25887b54d8477f7918af7670e15f276e60e1a 100644 >--- a/Source/WebCore/history/CachedPage.cpp >+++ b/Source/WebCore/history/CachedPage.cpp >@@ -55,9 +55,10 @@ DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, cachedPageCounter, ("Cached > > CachedPage::CachedPage(Page& page) > : m_page(page) >- , m_expirationTime(MonotonicTime::now() + page.settings().backForwardCacheExpirationInterval()) >- , m_cachedMainFrame(makeUnique<CachedFrame>(page.mainFrame())) > { >+ auto suspensionBeginTime = MonotonicTime::now(); >+ m_expirationTime = suspensionBeginTime + page.settings().backForwardCacheExpirationInterval(); >+ m_cachedMainFrame = makeUnique<CachedFrame>(page.mainFrame(), suspensionBeginTime); > #ifndef NDEBUG > cachedPageCounter.increment(); > #endif >diff --git a/Source/WebCore/html/HTMLMarqueeElement.cpp b/Source/WebCore/html/HTMLMarqueeElement.cpp >index 0adf7d164e2e4e2b0fb113f03a1bd3a73c271275..55575ea52e472a264cfd9663cbb6edb2b391c876 100644 >--- a/Source/WebCore/html/HTMLMarqueeElement.cpp >+++ b/Source/WebCore/html/HTMLMarqueeElement.cpp >@@ -159,7 +159,7 @@ ExceptionOr<void> HTMLMarqueeElement::setLoop(int loop) > return { }; > } > >-void HTMLMarqueeElement::suspend(ReasonForSuspension) >+void HTMLMarqueeElement::suspend(ReasonForSuspension, MonotonicTime) > { > if (RenderMarquee* marqueeRenderer = renderMarquee()) > marqueeRenderer->suspend(); >diff --git a/Source/WebCore/html/HTMLMarqueeElement.h b/Source/WebCore/html/HTMLMarqueeElement.h >index a2f2e544e95bb469741d4b344e136272d5ca8a04..f562cc12ce91f9b7256ec08e1a47f284c2661312 100644 >--- a/Source/WebCore/html/HTMLMarqueeElement.h >+++ b/Source/WebCore/html/HTMLMarqueeElement.h >@@ -57,7 +57,7 @@ private: > bool isPresentationAttribute(const QualifiedName&) const final; > void collectStyleForPresentationAttribute(const QualifiedName&, const AtomString&, MutableStyleProperties&) final; > >- void suspend(ReasonForSuspension) final; >+ void suspend(ReasonForSuspension, MonotonicTime) final; > void resume() final; > const char* activeDOMObjectName() const final { return "HTMLMarqueeElement"; } > >diff --git a/Source/WebCore/html/HTMLMediaElement.cpp b/Source/WebCore/html/HTMLMediaElement.cpp >index e0994839a2e90ea6882b01c46dd727a23cb36228..68b026f0cef0c437a8b5db4175f83a8d43237cff 100644 >--- a/Source/WebCore/html/HTMLMediaElement.cpp >+++ b/Source/WebCore/html/HTMLMediaElement.cpp >@@ -5747,7 +5747,7 @@ void HTMLMediaElement::stop() > m_mediaSession->stopSession(); > } > >-void HTMLMediaElement::suspend(ReasonForSuspension reason) >+void HTMLMediaElement::suspend(ReasonForSuspension reason, MonotonicTime) > { > INFO_LOG(LOGIDENTIFIER); > Ref<HTMLMediaElement> protectedThis(*this); >diff --git a/Source/WebCore/html/HTMLMediaElement.h b/Source/WebCore/html/HTMLMediaElement.h >index a69cebb2ff33c1307c3e0bd006835c176ac23464..6d39eeac81c8b35bf415440b802a88615690cab4 100644 >--- a/Source/WebCore/html/HTMLMediaElement.h >+++ b/Source/WebCore/html/HTMLMediaElement.h >@@ -645,7 +645,7 @@ private: > > // ActiveDOMObject API. > const char* activeDOMObjectName() const override; >- void suspend(ReasonForSuspension) override; >+ void suspend(ReasonForSuspension, MonotonicTime) override; > void resume() override; > void stop() override; > void stopWithoutDestroyingMediaPlayer(); >diff --git a/Source/WebCore/html/HTMLSourceElement.cpp b/Source/WebCore/html/HTMLSourceElement.cpp >index 4216e976b4820b78c1345b364f8ed40727be010f..e1441fdca1e4e86f01cb038a4847b7c98b062a0f 100644 >--- a/Source/WebCore/html/HTMLSourceElement.cpp >+++ b/Source/WebCore/html/HTMLSourceElement.cpp >@@ -127,7 +127,7 @@ const char* HTMLSourceElement::activeDOMObjectName() const > return "HTMLSourceElement"; > } > >-void HTMLSourceElement::suspend(ReasonForSuspension reason) >+void HTMLSourceElement::suspend(ReasonForSuspension reason, MonotonicTime) > { > // FIXME: Shouldn't this also stop the timer for PageWillBeSuspended? > if (reason == ReasonForSuspension::BackForwardCache) { >diff --git a/Source/WebCore/html/HTMLSourceElement.h b/Source/WebCore/html/HTMLSourceElement.h >index 4df0af542d353c4211b0388a82563d45bf12cd46..ab2d77483b3295fecece909bb5f7513a48de3c43 100644 >--- a/Source/WebCore/html/HTMLSourceElement.h >+++ b/Source/WebCore/html/HTMLSourceElement.h >@@ -52,7 +52,7 @@ private: > > // ActiveDOMObject. > const char* activeDOMObjectName() const final; >- void suspend(ReasonForSuspension) final; >+ void suspend(ReasonForSuspension, MonotonicTime) final; > void resume() final; > void stop() final; > >diff --git a/Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp b/Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp >index 14a70956001cbb9972b6e607bd707156bdb9c05a..b7982db7005710f4f52f8c7686c9b484e93ccb69 100644 >--- a/Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp >+++ b/Source/WebCore/html/canvas/WebGLRenderingContextBase.cpp >@@ -5209,7 +5209,7 @@ const char* WebGLRenderingContextBase::activeDOMObjectName() const > return "WebGLRenderingContext"; > } > >-void WebGLRenderingContextBase::suspend(ReasonForSuspension) >+void WebGLRenderingContextBase::suspend(ReasonForSuspension, MonotonicTime) > { > m_isSuspended = true; > } >diff --git a/Source/WebCore/html/canvas/WebGLRenderingContextBase.h b/Source/WebCore/html/canvas/WebGLRenderingContextBase.h >index 4b5eedc66c55f155ebbd8affd5ab3b640a7bd47a..29a74820b0a6364f549413e8d3390ea496cc1928 100644 >--- a/Source/WebCore/html/canvas/WebGLRenderingContextBase.h >+++ b/Source/WebCore/html/canvas/WebGLRenderingContextBase.h >@@ -399,7 +399,7 @@ protected: > // ActiveDOMObject > void stop() override; > const char* activeDOMObjectName() const override; >- void suspend(ReasonForSuspension) override; >+ void suspend(ReasonForSuspension, MonotonicTime) override; > void resume() override; > > void addSharedObject(WebGLSharedObject&); >diff --git a/Source/WebCore/page/EventSource.cpp b/Source/WebCore/page/EventSource.cpp >index 757765c3b4872d5a6f92b34e3f2ac67eaaf2dd82..9fafb725827b530c479dabacf9fd76899a0d6bfc 100644 >--- a/Source/WebCore/page/EventSource.cpp >+++ b/Source/WebCore/page/EventSource.cpp >@@ -403,7 +403,7 @@ const char* EventSource::activeDOMObjectName() const > return "EventSource"; > } > >-void EventSource::suspend(ReasonForSuspension reason) >+void EventSource::suspend(ReasonForSuspension reason, MonotonicTime) > { > if (reason != ReasonForSuspension::BackForwardCache) > return; >diff --git a/Source/WebCore/page/EventSource.h b/Source/WebCore/page/EventSource.h >index ce2530fc13e9fa2411b92be1d7a6f0ce4831510a..308dd4d17384fc057a354ea54ca6c20a6db77400 100644 >--- a/Source/WebCore/page/EventSource.h >+++ b/Source/WebCore/page/EventSource.h >@@ -91,7 +91,7 @@ private: > // ActiveDOMObject > void stop() final; > const char* activeDOMObjectName() const final; >- void suspend(ReasonForSuspension) final; >+ void suspend(ReasonForSuspension, MonotonicTime) final; > void resume() final; > > void connect(); >diff --git a/Source/WebCore/page/SuspendableTimer.cpp b/Source/WebCore/page/SuspendableTimer.cpp >index 844e001aca6c46f7ef5c4d739f805209fb097132..60661abd88c5df859896c91a45df976968eaab7f 100644 >--- a/Source/WebCore/page/SuspendableTimer.cpp >+++ b/Source/WebCore/page/SuspendableTimer.cpp >@@ -56,15 +56,16 @@ void SuspendableTimerBase::stop() > didStop(); > } > >-void SuspendableTimerBase::suspend(ReasonForSuspension) >+void SuspendableTimerBase::suspend(ReasonForSuspension, MonotonicTime beginTime) > { > ASSERT(!m_suspended); > m_suspended = true; > > m_savedIsActive = TimerBase::isActive(); > if (m_savedIsActive) { >- m_savedNextFireInterval = TimerBase::nextUnalignedFireInterval(); >+ m_savedNextFireInterval = TimerBase::nextUnalignedFireInterval(beginTime); > m_savedRepeatInterval = TimerBase::repeatInterval(); >+ m_savedInsertionOrder = TimerBase::insertionOrder(); > TimerBase::stop(); > } > } >@@ -75,7 +76,7 @@ void SuspendableTimerBase::resume() > m_suspended = false; > > if (m_savedIsActive) >- start(m_savedNextFireInterval, m_savedRepeatInterval); >+ start(m_savedNextFireInterval, m_savedRepeatInterval, m_savedInsertionOrder); > } > > void SuspendableTimerBase::didStop() >@@ -98,6 +99,7 @@ void SuspendableTimerBase::startRepeating(Seconds repeatInterval) > m_savedIsActive = true; > m_savedNextFireInterval = repeatInterval; > m_savedRepeatInterval = repeatInterval; >+ m_savedInsertionOrder = 0; > } > } > >@@ -109,6 +111,7 @@ void SuspendableTimerBase::startOneShot(Seconds interval) > m_savedIsActive = true; > m_savedNextFireInterval = interval; > m_savedRepeatInterval = 0_s; >+ m_savedInsertionOrder = 0; > } > } > >diff --git a/Source/WebCore/page/SuspendableTimer.h b/Source/WebCore/page/SuspendableTimer.h >index 3be62e4f0a5bec73ff2757849fe05ee367c111d6..698f6379d7fa1af60710853e56138f03b0397e4c 100644 >--- a/Source/WebCore/page/SuspendableTimer.h >+++ b/Source/WebCore/page/SuspendableTimer.h >@@ -56,6 +56,7 @@ public: > void augmentRepeatInterval(Seconds delta); > > using TimerBase::didChangeAlignmentInterval; >+ using TimerBase::insertionOrder; > using TimerBase::operator new; > using TimerBase::operator delete; > >@@ -67,11 +68,12 @@ private: > // ActiveDOMObject API. > bool hasPendingActivity() const final; > void stop() final; >- void suspend(ReasonForSuspension) final; >+ void suspend(ReasonForSuspension, MonotonicTime) final; > void resume() final; > > Seconds m_savedNextFireInterval; > Seconds m_savedRepeatInterval; >+ unsigned m_savedInsertionOrder; > > bool m_suspended { false }; > bool m_savedIsActive { false }; >diff --git a/Source/WebCore/page/ios/DOMTimerHoldingTank.cpp b/Source/WebCore/page/ios/DOMTimerHoldingTank.cpp >index 3883c3b09696b072b52f56726554cc5b4f12d79c..b528f23dba70eacc4c07930127943044b1819bcb 100644 >--- a/Source/WebCore/page/ios/DOMTimerHoldingTank.cpp >+++ b/Source/WebCore/page/ios/DOMTimerHoldingTank.cpp >@@ -29,6 +29,7 @@ > #if PLATFORM(IOS_FAMILY) > > #include <wtf/HashSet.h> >+#include <wtf/MonotonicTime.h> > > namespace WebCore { > >@@ -49,6 +50,8 @@ DOMTimerHoldingTank::~DOMTimerHoldingTank() = default; > > void DOMTimerHoldingTank::add(const DOMTimer& timer) > { >+ if (m_timers.isEmpty()) >+ m_suspensionBeginTime = MonotonicTime::now(); > m_timers.add(&timer); > if (!m_exceededMaximumHoldTimer.isActive()) > m_exceededMaximumHoldTimer.startOneShot(maximumHoldTimeLimit); >diff --git a/Source/WebCore/page/ios/DOMTimerHoldingTank.h b/Source/WebCore/page/ios/DOMTimerHoldingTank.h >index bbf7945aaa499a76b7e12ab3c75d594421e45ef7..3173ce5a42fb06bacdc5302ea2b1e6384ac0b1ef 100644 >--- a/Source/WebCore/page/ios/DOMTimerHoldingTank.h >+++ b/Source/WebCore/page/ios/DOMTimerHoldingTank.h >@@ -50,6 +50,7 @@ private: > > HashSet<const DOMTimer*> m_timers; > Timer m_exceededMaximumHoldTimer; >+ MonotonicTime m_suspensionBeginTime; > }; > > class DeferDOMTimersForScope { >diff --git a/Source/WebCore/platform/SuspendableTaskQueue.cpp b/Source/WebCore/platform/SuspendableTaskQueue.cpp >index 046aaab0efdb23827c61b06636408ab75b870001..c97108ccf77d70c1bfd68238593e8dd5ccf66c37 100644 >--- a/Source/WebCore/platform/SuspendableTaskQueue.cpp >+++ b/Source/WebCore/platform/SuspendableTaskQueue.cpp >@@ -86,7 +86,7 @@ void SuspendableTaskQueue::stop() > close(); > } > >-void SuspendableTaskQueue::suspend(ReasonForSuspension) >+void SuspendableTaskQueue::suspend(ReasonForSuspension, MonotonicTime) > { > m_isSuspended = true; > m_taskQueue.cancelAllTasks(); >diff --git a/Source/WebCore/platform/SuspendableTaskQueue.h b/Source/WebCore/platform/SuspendableTaskQueue.h >index 5ba2cb0861cff4db1d749ad1e7be62805fa0d195..f924aa61931d4878bbfe099166828d95db847914 100644 >--- a/Source/WebCore/platform/SuspendableTaskQueue.h >+++ b/Source/WebCore/platform/SuspendableTaskQueue.h >@@ -71,7 +71,7 @@ private: > // ActiveDOMObject. > const char* activeDOMObjectName() const final; > void stop() final; >- void suspend(ReasonForSuspension) final; >+ void suspend(ReasonForSuspension, MonotonicTime) final; > void resume() final; > > GenericTaskQueue<ScriptExecutionContext> m_taskQueue; >diff --git a/Source/WebCore/platform/Timer.cpp b/Source/WebCore/platform/Timer.cpp >index add48e65159ab6d9a3aca22b76a08c9de458fa0f..9321e316e60c7a80d055c92465d4fbe9acc7ce9a 100644 >--- a/Source/WebCore/platform/Timer.cpp >+++ b/Source/WebCore/platform/Timer.cpp >@@ -267,12 +267,12 @@ TimerBase::~TimerBase() > m_unalignedNextFireTime = MonotonicTime::nan(); > } > >-void TimerBase::start(Seconds nextFireInterval, Seconds repeatInterval) >+void TimerBase::start(Seconds nextFireInterval, Seconds repeatInterval, Optional<unsigned> insertionOrder) > { > ASSERT(canAccessThreadLocalDataForThread(m_thread.get())); > > m_repeatInterval = repeatInterval; >- setNextFireTime(MonotonicTime::now() + nextFireInterval); >+ setNextFireTime(MonotonicTime::now() + nextFireInterval, insertionOrder); > } > > void TimerBase::stop() >@@ -459,7 +459,7 @@ void TimerBase::updateHeapIfNeeded(MonotonicTime oldTime) > ASSERT(!inHeap() || hasValidHeapPosition()); > } > >-void TimerBase::setNextFireTime(MonotonicTime newTime) >+void TimerBase::setNextFireTime(MonotonicTime newTime, Optional<unsigned> insertionOrder) > { > ASSERT(canAccessThreadLocalDataForThread(m_thread.get())); > RELEASE_ASSERT(canAccessThreadLocalDataForThread(m_thread.get()) || shouldSuppressThreadSafetyCheck()); >@@ -480,7 +480,7 @@ void TimerBase::setNextFireTime(MonotonicTime newTime) > } > > if (oldTime != newTime) { >- auto newOrder = threadGlobalData().threadTimers().nextHeapInsertionCount(); >+ auto newOrder = insertionOrder ? *insertionOrder : threadGlobalData().threadTimers().nextHeapInsertionCount(); > > if (!m_heapItem) > m_heapItem = ThreadTimerHeapItem::create(*this, newTime, 0); >@@ -511,10 +511,10 @@ void TimerBase::didChangeAlignmentInterval() > setNextFireTime(m_unalignedNextFireTime); > } > >-Seconds TimerBase::nextUnalignedFireInterval() const >+Seconds TimerBase::nextUnalignedFireInterval(MonotonicTime beginTime) const > { > ASSERT(isActive()); >- auto result = std::max(m_unalignedNextFireTime - MonotonicTime::now(), 0_s); >+ auto result = std::max(m_unalignedNextFireTime - beginTime, 0_s); > RELEASE_ASSERT(std::isfinite(result)); > return result; > } >diff --git a/Source/WebCore/platform/Timer.h b/Source/WebCore/platform/Timer.h >index e7081567f43db94798efa94ec5f3a462c252bd08..b059362994adea5002bcb962e76dec7089164469 100644 >--- a/Source/WebCore/platform/Timer.h >+++ b/Source/WebCore/platform/Timer.h >@@ -49,7 +49,7 @@ public: > WEBCORE_EXPORT TimerBase(); > WEBCORE_EXPORT virtual ~TimerBase(); > >- WEBCORE_EXPORT void start(Seconds nextFireInterval, Seconds repeatInterval); >+ void start(Seconds nextFireInterval, Seconds repeatInterval); > > void startRepeating(Seconds repeatInterval) { start(repeatInterval, repeatInterval); } > void startOneShot(Seconds interval) { start(interval, 0_s); } >@@ -58,8 +58,9 @@ public: > bool isActive() const; > > WEBCORE_EXPORT Seconds nextFireInterval() const; >- Seconds nextUnalignedFireInterval() const; >+ Seconds nextUnalignedFireInterval(MonotonicTime beginTime) const; > Seconds repeatInterval() const { return m_repeatInterval; } >+ unsigned insertionOrder() const { return m_heapItem->insertionOrder; } > > void augmentFireInterval(Seconds delta) { setNextFireTime(m_heapItem->time + delta); } > void augmentRepeatInterval(Seconds delta) { augmentFireInterval(delta); m_repeatInterval += delta; } >@@ -68,6 +69,9 @@ public: > > static void fireTimersInNestedEventLoop(); > >+protected: >+ WEBCORE_EXPORT void start(Seconds nextFireInterval, Seconds repeatInterval, Optional<unsigned> insertionOrder); >+ > private: > virtual void fired() = 0; > >@@ -76,7 +80,7 @@ private: > void checkConsistency() const; > void checkHeapIndex() const; > >- void setNextFireTime(MonotonicTime); >+ void setNextFireTime(MonotonicTime, Optional<unsigned> insertionOrder = WTF::nullopt); > > bool inHeap() const { return m_heapItem && m_heapItem->isInHeap(); } > >@@ -140,6 +144,11 @@ inline bool TimerBase::isActive() const > return static_cast<bool>(nextFireTime()); > } > >+inline void TimerBase::start(Seconds nextFireInterval, Seconds repeatInterval) >+{ >+ start(nextFireInterval, repeatInterval, WTF::nullopt); >+} >+ > class DeferrableOneShotTimer : protected TimerBase { > WTF_MAKE_FAST_ALLOCATED; > public: >diff --git a/Source/WebCore/workers/Worker.cpp b/Source/WebCore/workers/Worker.cpp >index a762913e7d56b5f6b687f26789bfa341d0c78d92..7c644ee7932d7e9ba4572842bbf644b9936d342a 100644 >--- a/Source/WebCore/workers/Worker.cpp >+++ b/Source/WebCore/workers/Worker.cpp >@@ -160,7 +160,7 @@ void Worker::stop() > terminate(); > } > >-void Worker::suspend(ReasonForSuspension reason) >+void Worker::suspend(ReasonForSuspension reason, MonotonicTime) > { > if (reason == ReasonForSuspension::BackForwardCache) { > m_contextProxy.suspendForBackForwardCache(); >diff --git a/Source/WebCore/workers/Worker.h b/Source/WebCore/workers/Worker.h >index 1bb5955dfefaf42afdabcd9040a7945d52f899ff..05132b8fc395ac7b75a13d223d9966d32b7225c9 100644 >--- a/Source/WebCore/workers/Worker.h >+++ b/Source/WebCore/workers/Worker.h >@@ -83,7 +83,7 @@ private: > void notifyFinished() final; > > void stop() final; >- void suspend(ReasonForSuspension) final; >+ void suspend(ReasonForSuspension, MonotonicTime) final; > void resume() final; > const char* activeDOMObjectName() const final; > >diff --git a/Source/WebCore/workers/service/ServiceWorker.cpp b/Source/WebCore/workers/service/ServiceWorker.cpp >index 42a5ab038c5ed9e77d42920600b883328920b192..6cc59b8d5eda59f2532af297b272cd2c47ee48ba 100644 >--- a/Source/WebCore/workers/service/ServiceWorker.cpp >+++ b/Source/WebCore/workers/service/ServiceWorker.cpp >@@ -144,7 +144,7 @@ const char* ServiceWorker::activeDOMObjectName() const > return "ServiceWorker"; > } > >-void ServiceWorker::suspend(ReasonForSuspension) >+void ServiceWorker::suspend(ReasonForSuspension, MonotonicTime) > { > m_isSuspended = true; > } >diff --git a/Source/WebCore/workers/service/ServiceWorker.h b/Source/WebCore/workers/service/ServiceWorker.h >index 67567e5408c3614fecc94ba2fad0d5789204216f..0935c6d0025d9e655a31125de50c568c1f64d045 100644 >--- a/Source/WebCore/workers/service/ServiceWorker.h >+++ b/Source/WebCore/workers/service/ServiceWorker.h >@@ -76,7 +76,7 @@ private: > > // ActiveDOMObject. > const char* activeDOMObjectName() const final; >- void suspend(ReasonForSuspension) final; >+ void suspend(ReasonForSuspension, MonotonicTime) final; > void resume() final; > void stop() final; > >diff --git a/Source/WebCore/workers/service/ServiceWorkerRegistration.cpp b/Source/WebCore/workers/service/ServiceWorkerRegistration.cpp >index f2189070eab78d8c52791b3b5de304c0f53d23c7..96182a29a5a6fb74a814c22e84372bf0080344ad 100644 >--- a/Source/WebCore/workers/service/ServiceWorkerRegistration.cpp >+++ b/Source/WebCore/workers/service/ServiceWorkerRegistration.cpp >@@ -238,7 +238,7 @@ const char* ServiceWorkerRegistration::activeDOMObjectName() const > return "ServiceWorkerRegistration"; > } > >-void ServiceWorkerRegistration::suspend(ReasonForSuspension) >+void ServiceWorkerRegistration::suspend(ReasonForSuspension, MonotonicTime) > { > m_isSuspended = true; > } >diff --git a/Source/WebCore/workers/service/ServiceWorkerRegistration.h b/Source/WebCore/workers/service/ServiceWorkerRegistration.h >index 191fa40b8fa77d292b13dcd1f2ad5df71ac4f70e..ab3e243c2d0672965967caed355f56cf00c0a36e 100644 >--- a/Source/WebCore/workers/service/ServiceWorkerRegistration.h >+++ b/Source/WebCore/workers/service/ServiceWorkerRegistration.h >@@ -91,7 +91,7 @@ private: > > // ActiveDOMObject. > const char* activeDOMObjectName() const final; >- void suspend(ReasonForSuspension) final; >+ void suspend(ReasonForSuspension, MonotonicTime) final; > void resume() final; > void stop() final; > >diff --git a/Source/WebCore/xml/XMLHttpRequest.cpp b/Source/WebCore/xml/XMLHttpRequest.cpp >index 696fd3878a990bb1a13466b6cdb37865216cc26d..f0ad15f8808529c854f811b930ba4f571d024c69 100644 >--- a/Source/WebCore/xml/XMLHttpRequest.cpp >+++ b/Source/WebCore/xml/XMLHttpRequest.cpp >@@ -1151,7 +1151,7 @@ const char* XMLHttpRequest::activeDOMObjectName() const > return "XMLHttpRequest"; > } > >-void XMLHttpRequest::suspend(ReasonForSuspension) >+void XMLHttpRequest::suspend(ReasonForSuspension, MonotonicTime) > { > m_progressEventThrottle.suspend(); > } >diff --git a/Source/WebCore/xml/XMLHttpRequest.h b/Source/WebCore/xml/XMLHttpRequest.h >index df32ab0de9ea6353c3cad55c5cf192f8b26cc740..c67e292cf068973d319d3c2ae38d95eca2b8ff87 100644 >--- a/Source/WebCore/xml/XMLHttpRequest.h >+++ b/Source/WebCore/xml/XMLHttpRequest.h >@@ -137,7 +137,7 @@ private: > > // ActiveDOMObject > void contextDestroyed() override; >- void suspend(ReasonForSuspension) override; >+ void suspend(ReasonForSuspension, MonotonicTime) override; > void resume() override; > void stop() override; > const char* activeDOMObjectName() const override; >diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog >index 9016a7ab30eb7eca7fe0ce1547d02e9e1d839e84..5ef72b54dfda5512874fabb8b87a1115c281784c 100644 >--- a/LayoutTests/ChangeLog >+++ b/LayoutTests/ChangeLog >@@ -1,3 +1,17 @@ >+2019-10-29 Daniel Bates <dabates@apple.com> >+ >+ Events with the same firing time can fire in the wrong order after page suspend and resume >+ https://bugs.webkit.org/show_bug.cgi?id=203586 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Add a test to ensure that the order of timers is preserved when navigating forward and back. >+ Navigating forward and back when the page cache is enabled suspends and resumes the page's >+ timers, respectively. >+ >+ * fast/dom/simultaneously-registered-timer-fire-order-after-navigation-back-expected.txt: Added. >+ * fast/dom/simultaneously-registered-timer-fire-order-after-navigation-back.html: Added. >+ > 2019-07-26 Daniel Bates <dabates@apple.com> > > [iOS] ASSERTION FAILURE: !isMissingPostLayoutData in WebKit::EditorState::postLayoutData() >diff --git a/LayoutTests/fast/dom/simultaneously-registered-timer-fire-order-after-navigation-back-expected.txt b/LayoutTests/fast/dom/simultaneously-registered-timer-fire-order-after-navigation-back-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..6f0b0cf6a7253b94abb297d144403c097d423c81 >--- /dev/null >+++ b/LayoutTests/fast/dom/simultaneously-registered-timer-fire-order-after-navigation-back-expected.txt >@@ -0,0 +1,103 @@ >+This page verifies that timers with equivalent fire times fire in the order they were registered even after navigating forward and then back, which suspends and resumes them. If the test passes, you'll see a series of PASS messages below. >+ >+PASS: firedTimers[0] should be 0 and is. >+PASS: firedTimers[1] should be 1 and is. >+PASS: firedTimers[2] should be 2 and is. >+PASS: firedTimers[3] should be 3 and is. >+PASS: firedTimers[4] should be 4 and is. >+PASS: firedTimers[5] should be 5 and is. >+PASS: firedTimers[6] should be 6 and is. >+PASS: firedTimers[7] should be 7 and is. >+PASS: firedTimers[8] should be 8 and is. >+PASS: firedTimers[9] should be 9 and is. >+PASS: firedTimers[10] should be 10 and is. >+PASS: firedTimers[11] should be 11 and is. >+PASS: firedTimers[12] should be 12 and is. >+PASS: firedTimers[13] should be 13 and is. >+PASS: firedTimers[14] should be 14 and is. >+PASS: firedTimers[15] should be 15 and is. >+PASS: firedTimers[16] should be 16 and is. >+PASS: firedTimers[17] should be 17 and is. >+PASS: firedTimers[18] should be 18 and is. >+PASS: firedTimers[19] should be 19 and is. >+PASS: firedTimers[20] should be 20 and is. >+PASS: firedTimers[21] should be 21 and is. >+PASS: firedTimers[22] should be 22 and is. >+PASS: firedTimers[23] should be 23 and is. >+PASS: firedTimers[24] should be 24 and is. >+PASS: firedTimers[25] should be 25 and is. >+PASS: firedTimers[26] should be 26 and is. >+PASS: firedTimers[27] should be 27 and is. >+PASS: firedTimers[28] should be 28 and is. >+PASS: firedTimers[29] should be 29 and is. >+PASS: firedTimers[30] should be 30 and is. >+PASS: firedTimers[31] should be 31 and is. >+PASS: firedTimers[32] should be 32 and is. >+PASS: firedTimers[33] should be 33 and is. >+PASS: firedTimers[34] should be 34 and is. >+PASS: firedTimers[35] should be 35 and is. >+PASS: firedTimers[36] should be 36 and is. >+PASS: firedTimers[37] should be 37 and is. >+PASS: firedTimers[38] should be 38 and is. >+PASS: firedTimers[39] should be 39 and is. >+PASS: firedTimers[40] should be 40 and is. >+PASS: firedTimers[41] should be 41 and is. >+PASS: firedTimers[42] should be 42 and is. >+PASS: firedTimers[43] should be 43 and is. >+PASS: firedTimers[44] should be 44 and is. >+PASS: firedTimers[45] should be 45 and is. >+PASS: firedTimers[46] should be 46 and is. >+PASS: firedTimers[47] should be 47 and is. >+PASS: firedTimers[48] should be 48 and is. >+PASS: firedTimers[49] should be 49 and is. >+PASS: firedTimers[50] should be 50 and is. >+PASS: firedTimers[51] should be 51 and is. >+PASS: firedTimers[52] should be 52 and is. >+PASS: firedTimers[53] should be 53 and is. >+PASS: firedTimers[54] should be 54 and is. >+PASS: firedTimers[55] should be 55 and is. >+PASS: firedTimers[56] should be 56 and is. >+PASS: firedTimers[57] should be 57 and is. >+PASS: firedTimers[58] should be 58 and is. >+PASS: firedTimers[59] should be 59 and is. >+PASS: firedTimers[60] should be 60 and is. >+PASS: firedTimers[61] should be 61 and is. >+PASS: firedTimers[62] should be 62 and is. >+PASS: firedTimers[63] should be 63 and is. >+PASS: firedTimers[64] should be 64 and is. >+PASS: firedTimers[65] should be 65 and is. >+PASS: firedTimers[66] should be 66 and is. >+PASS: firedTimers[67] should be 67 and is. >+PASS: firedTimers[68] should be 68 and is. >+PASS: firedTimers[69] should be 69 and is. >+PASS: firedTimers[70] should be 70 and is. >+PASS: firedTimers[71] should be 71 and is. >+PASS: firedTimers[72] should be 72 and is. >+PASS: firedTimers[73] should be 73 and is. >+PASS: firedTimers[74] should be 74 and is. >+PASS: firedTimers[75] should be 75 and is. >+PASS: firedTimers[76] should be 76 and is. >+PASS: firedTimers[77] should be 77 and is. >+PASS: firedTimers[78] should be 78 and is. >+PASS: firedTimers[79] should be 79 and is. >+PASS: firedTimers[80] should be 80 and is. >+PASS: firedTimers[81] should be 81 and is. >+PASS: firedTimers[82] should be 82 and is. >+PASS: firedTimers[83] should be 83 and is. >+PASS: firedTimers[84] should be 84 and is. >+PASS: firedTimers[85] should be 85 and is. >+PASS: firedTimers[86] should be 86 and is. >+PASS: firedTimers[87] should be 87 and is. >+PASS: firedTimers[88] should be 88 and is. >+PASS: firedTimers[89] should be 89 and is. >+PASS: firedTimers[90] should be 90 and is. >+PASS: firedTimers[91] should be 91 and is. >+PASS: firedTimers[92] should be 92 and is. >+PASS: firedTimers[93] should be 93 and is. >+PASS: firedTimers[94] should be 94 and is. >+PASS: firedTimers[95] should be 95 and is. >+PASS: firedTimers[96] should be 96 and is. >+PASS: firedTimers[97] should be 97 and is. >+PASS: firedTimers[98] should be 98 and is. >+PASS: firedTimers[99] should be 99 and is. >+ >diff --git a/LayoutTests/fast/dom/simultaneously-registered-timer-fire-order-after-navigation-back.html b/LayoutTests/fast/dom/simultaneously-registered-timer-fire-order-after-navigation-back.html >new file mode 100644 >index 0000000000000000000000000000000000000000..1ff074c5654fd33e4cbde315672506487a910032 >--- /dev/null >+++ b/LayoutTests/fast/dom/simultaneously-registered-timer-fire-order-after-navigation-back.html >@@ -0,0 +1,74 @@ >+<!DOCTYPE html><!-- webkit-test-runner [ enableBackForwardCache=true ] --> >+<html> >+<body> >+<p> >+This page verifies that timers with equivalent fire times fire in the order they were registered even after navigating forward and then back, which suspends and resumes them. If the test passes, you'll see a series of PASS messages below. >+</p> >+ >+<pre id="pre"></pre> >+ >+<script> >+if (window.testRunner) { >+ testRunner.dumpAsText(); >+ testRunner.waitUntilDone(); >+} >+ >+function log(s) >+{ >+ document.getElementById("pre").appendChild(document.createTextNode(s + "\n")); >+} >+ >+function shouldBe(a, aDescription, b) >+{ >+ if (a === b) { >+ log("PASS: " + aDescription + " should be " + b + " and is."); >+ } else { >+ log("FAIL: " + aDescription + " should be " + b + " but instead is " + a + "."); >+ } >+} >+ >+var count = 100; >+var firedTimers = []; >+ >+function fired(id) >+{ >+ firedTimers.push(id); >+ if (id == count - 1) >+ done(); >+} >+ >+function done() >+{ >+ for (var i = 0; i < count; ++i) >+ shouldBe(firedTimers[i], "firedTimers[" + i + "]", i); >+ >+ if (window.testRunner) >+ testRunner.notifyDone(); >+} >+ >+function registerTimers() >+{ >+ function createCallback(i) { >+ return () => fired(i); >+ } >+ // Use a timeout of at least 10ms to make it less likely that this test passes just by luck. >+ for (var i = 0; i < count; ++i) >+ window.setTimeout(createCallback(i), 10); >+} >+ >+function handlePageShow(event) >+{ >+ if (event.persisted) { >+ // Returned from the cache. Let the timers run... >+ return; >+ } >+ window.setTimeout(() => { >+ window.location.href = "data:text/html,<p>Another page</p><script>window.setTimeout('history.back()', 0)</" + "script>"; >+ }, 0); >+ registerTimers(); >+} >+ >+window.onpageshow = handlePageShow; >+</script> >+</body> >+</html>
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 203586
:
382223
|
382239
|
382374
|
382379
|
382387
|
382460