diff options
Diffstat (limited to 'dom')
-rw-r--r-- | dom/base/nsGlobalWindow.cpp | 27 | ||||
-rw-r--r-- | dom/base/nsGlobalWindow.h | 4 | ||||
-rw-r--r-- | dom/webidl/WindowOrWorkerGlobalScope.webidl | 4 | ||||
-rw-r--r-- | dom/workers/WorkerPrivate.cpp | 46 | ||||
-rw-r--r-- | dom/workers/WorkerPrivate.h | 19 | ||||
-rw-r--r-- | dom/workers/WorkerScope.cpp | 14 | ||||
-rw-r--r-- | dom/workers/WorkerScope.h | 4 |
7 files changed, 75 insertions, 43 deletions
diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index dd6d190591..ae84de3a19 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -12685,41 +12685,22 @@ nsGlobalWindow::SetTimeout(JSContext* aCx, const nsAString& aHandler, return SetTimeoutOrInterval(aCx, aHandler, aTimeout, false, aError); } -static bool -IsInterval(const Optional<int32_t>& aTimeout, int32_t& aResultTimeout) -{ - if (aTimeout.WasPassed()) { - aResultTimeout = aTimeout.Value(); - return true; - } - - // If no interval was specified, treat this like a timeout, to avoid setting - // an interval of 0 milliseconds. - aResultTimeout = 0; - return false; -} - int32_t nsGlobalWindow::SetInterval(JSContext* aCx, Function& aFunction, - const Optional<int32_t>& aTimeout, + const int32_t aTimeout, const Sequence<JS::Value>& aArguments, ErrorResult& aError) { - int32_t timeout; - bool isInterval = IsInterval(aTimeout, timeout); - return SetTimeoutOrInterval(aCx, aFunction, timeout, aArguments, isInterval, - aError); + return SetTimeoutOrInterval(aCx, aFunction, aTimeout, aArguments, true, aError); } int32_t nsGlobalWindow::SetInterval(JSContext* aCx, const nsAString& aHandler, - const Optional<int32_t>& aTimeout, + const int32_t aTimeout, const Sequence<JS::Value>& /* unused */, ErrorResult& aError) { - int32_t timeout; - bool isInterval = IsInterval(aTimeout, timeout); - return SetTimeoutOrInterval(aCx, aHandler, timeout, isInterval, aError); + return SetTimeoutOrInterval(aCx, aHandler, aTimeout, true, aError); } nsresult diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h index 63bb574dd4..c59baee648 100644 --- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -953,11 +953,11 @@ public: mozilla::ErrorResult& aError); void ClearTimeout(int32_t aHandle); int32_t SetInterval(JSContext* aCx, mozilla::dom::Function& aFunction, - const mozilla::dom::Optional<int32_t>& aTimeout, + const int32_t aTimeout, const mozilla::dom::Sequence<JS::Value>& aArguments, mozilla::ErrorResult& aError); int32_t SetInterval(JSContext* aCx, const nsAString& aHandler, - const mozilla::dom::Optional<int32_t>& aTimeout, + const int32_t aTimeout, const mozilla::dom::Sequence<JS::Value>& /* unused */, mozilla::ErrorResult& aError); void ClearInterval(int32_t aHandle); diff --git a/dom/webidl/WindowOrWorkerGlobalScope.webidl b/dom/webidl/WindowOrWorkerGlobalScope.webidl index 9e639db5f4..d378ba49ec 100644 --- a/dom/webidl/WindowOrWorkerGlobalScope.webidl +++ b/dom/webidl/WindowOrWorkerGlobalScope.webidl @@ -31,9 +31,9 @@ interface WindowOrWorkerGlobalScope { long setTimeout(DOMString handler, optional long timeout = 0, any... unused); void clearTimeout(optional long handle = 0); [Throws] - long setInterval(Function handler, optional long timeout, any... arguments); + long setInterval(Function handler, optional long timeout = 0, any... arguments); [Throws] - long setInterval(DOMString handler, optional long timeout, any... unused); + long setInterval(DOMString handler, optional long timeout = 0, any... unused); void clearInterval(optional long handle = 0); // microtask queuing diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index 4a3cf7eaea..3b3de7e3b5 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -167,6 +167,9 @@ const nsIID kDEBUGWorkerEventTargetIID = { #endif +// The number of nested timeouts before we start clamping. HTML says 5. +const uint32_t kClampTimeoutNestingLevel = 5u; + template <class T> class AutoPtrComparator { @@ -1956,7 +1959,11 @@ NS_IMPL_QUERY_INTERFACE(WorkerLoadInfo::InterfaceRequestor, nsIInterfaceRequesto struct WorkerPrivate::TimeoutInfo { TimeoutInfo() - : mId(0), mIsInterval(false), mCanceled(false) + : mId(0) + , mNestingLevel(0) + , mIsInterval(false) + , mCanceled(false) + , mOnChromeWorker(false) { MOZ_COUNT_CTOR(mozilla::dom::workers::WorkerPrivate::TimeoutInfo); } @@ -1976,12 +1983,33 @@ struct WorkerPrivate::TimeoutInfo return mTargetTime < aOther.mTargetTime; } + void AccumulateNestingLevel(const uint32_t& aBaseLevel) { + if (aBaseLevel < kClampTimeoutNestingLevel) { + mNestingLevel = aBaseLevel + 1; + return; + } + mNestingLevel = kClampTimeoutNestingLevel; + } + + void CalculateTargetTime() { + auto target = mInterval; + // Clamp timeout for workers, except chrome workers + if (mNestingLevel >= kClampTimeoutNestingLevel && !mOnChromeWorker) { + target = TimeDuration::Max( + mInterval, + TimeDuration::FromMilliseconds(Preferences::GetInt("dom.min_timeout_value"))); + } + mTargetTime = TimeStamp::Now() + target; + } + nsCOMPtr<nsIScriptTimeoutHandler> mHandler; mozilla::TimeStamp mTargetTime; mozilla::TimeDuration mInterval; int32_t mId; + uint32_t mNestingLevel; bool mIsInterval; bool mCanceled; + bool mOnChromeWorker; }; class WorkerJSContextStats final : public JS::RuntimeStats @@ -4131,6 +4159,7 @@ WorkerPrivate::WorkerPrivate(WorkerPrivate* aParent, , mMainThreadEventTarget(do_GetMainThread()) , mErrorHandlerRecursionCount(0) , mNextTimeoutId(1) + , mCurrentTimerNestingLevel(0) , mStatus(Pending) , mFrozen(false) , mTimerRunning(false) @@ -6081,8 +6110,10 @@ WorkerPrivate::SetTimeout(JSContext* aCx, } nsAutoPtr<TimeoutInfo> newInfo(new TimeoutInfo()); + newInfo->mOnChromeWorker = mIsChromeWorker; newInfo->mIsInterval = aIsInterval; newInfo->mId = timerId; + newInfo->AccumulateNestingLevel(this->mCurrentTimerNestingLevel); if (MOZ_UNLIKELY(timerId == INT32_MAX)) { NS_WARNING("Timeout ids overflowed!"); @@ -6195,14 +6226,21 @@ WorkerPrivate::RunExpiredTimeouts(JSContext* aCx) // Guard against recursion. mRunningExpiredTimeouts = true; + MOZ_DIAGNOSTIC_ASSERT(data->mCurrentTimerNestingLevel == 0); + // Run expired timeouts. for (uint32_t index = 0; index < expiredTimeouts.Length(); index++) { TimeoutInfo*& info = expiredTimeouts[index]; + AutoRestore<uint32_t> nestingLevel(this->mCurrentTimerNestingLevel); if (info->mCanceled) { continue; } + // Set current timer nesting level to current running timer handler's + // nesting level + this->mCurrentTimerNestingLevel = info->mNestingLevel; + LOG(TimeoutsLog(), ("Worker %p executing timeout with original delay %f ms.\n", this, info->mInterval.ToMilliseconds())); @@ -6280,8 +6318,10 @@ WorkerPrivate::RunExpiredTimeouts(JSContext* aCx) info->mCanceled) { if (info->mIsInterval && !info->mCanceled) { // Reschedule intervals. - info->mTargetTime = info->mTargetTime + info->mInterval; - // Don't resort the list here, we'll do that at the end. + // Reschedule a timeout and, if needed, increase the nesting level. + info->AccumulateNestingLevel(info->mNestingLevel); + info->CalculateTargetTime(); + // Don't re-sort the list here, we'll do that at the end. ++index; } else { diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h index 6ca6a6fee3..26afecb69b 100644 --- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h @@ -17,6 +17,7 @@ #include "mozilla/Assertions.h" #include "mozilla/Attributes.h" +#include "mozilla/AutoRestore.h" #include "mozilla/CondVar.h" #include "mozilla/ConsoleReportCollector.h" #include "mozilla/DOMEventTargetHelper.h" @@ -212,6 +213,9 @@ protected: RefPtr<EventTarget> mEventTarget; nsTArray<RefPtr<WorkerRunnable>> mPreStartRunnables; + // True if the worker is used in the UI + bool mIsChromeWorker; + private: WorkerPrivate* mParent; nsString mScriptURL; @@ -241,7 +245,6 @@ private: uint32_t mParentWindowPausedDepth; Status mParentStatus; bool mParentFrozen; - bool mIsChromeWorker; bool mMainThreadObjectsForgotten; // mIsSecureContext is set once in our constructor; after that it can be read // from various threads. We could make this const if we were OK with setting @@ -1017,6 +1020,20 @@ class WorkerPrivate : public WorkerPrivateParent<WorkerPrivate> uint32_t mErrorHandlerRecursionCount; uint32_t mNextTimeoutId; Status mStatus; + + // Tracks the current setTimeout/setInterval nesting level. + // When there isn't a TimeoutHandler on the stack, this will be 0. + // Whenever setTimeout/setInterval are called, a new TimeoutInfo will be + // created with a nesting level one more than the current nesting level, + // saturating at the kClampTimeoutNestingLevel. + // + // When RunExpiredTimeouts is run, it sets this value to the + // TimeoutInfo::mNestingLevel for the duration of + // the WorkerScriptTimeoutHandler::Call which will explicitly trigger a + // microtask checkpoint so that any immediately-resolved promises will + // still see the nesting level. + uint32_t mCurrentTimerNestingLevel; + bool mFrozen; bool mTimerRunning; bool mRunningExpiredTimeouts; diff --git a/dom/workers/WorkerScope.cpp b/dom/workers/WorkerScope.cpp index 637e441732..df75ff887a 100644 --- a/dom/workers/WorkerScope.cpp +++ b/dom/workers/WorkerScope.cpp @@ -299,28 +299,25 @@ WorkerGlobalScope::ClearTimeout(int32_t aHandle) int32_t WorkerGlobalScope::SetInterval(JSContext* aCx, Function& aHandler, - const Optional<int32_t>& aTimeout, + const int32_t aTimeout, const Sequence<JS::Value>& aArguments, ErrorResult& aRv) { mWorkerPrivate->AssertIsOnWorkerThread(); - bool isInterval = aTimeout.WasPassed(); - int32_t timeout = aTimeout.WasPassed() ? aTimeout.Value() : 0; - nsCOMPtr<nsIScriptTimeoutHandler> handler = NS_CreateJSTimeoutHandler(aCx, mWorkerPrivate, aHandler, aArguments, aRv); if (NS_WARN_IF(aRv.Failed())) { return 0; } - return mWorkerPrivate->SetTimeout(aCx, handler, timeout, isInterval, aRv); + return mWorkerPrivate->SetTimeout(aCx, handler, aTimeout, true, aRv); } int32_t WorkerGlobalScope::SetInterval(JSContext* aCx, const nsAString& aHandler, - const Optional<int32_t>& aTimeout, + const int32_t aTimeout, const Sequence<JS::Value>& /* unused */, ErrorResult& aRv) { @@ -328,12 +325,9 @@ WorkerGlobalScope::SetInterval(JSContext* aCx, Sequence<JS::Value> dummy; - bool isInterval = aTimeout.WasPassed(); - int32_t timeout = aTimeout.WasPassed() ? aTimeout.Value() : 0; - nsCOMPtr<nsIScriptTimeoutHandler> handler = NS_CreateJSTimeoutHandler(aCx, mWorkerPrivate, aHandler); - return mWorkerPrivate->SetTimeout(aCx, handler, timeout, isInterval, aRv); + return mWorkerPrivate->SetTimeout(aCx, handler, aTimeout, true, aRv); } void diff --git a/dom/workers/WorkerScope.h b/dom/workers/WorkerScope.h index 01bae5cc11..81d2733efe 100644 --- a/dom/workers/WorkerScope.h +++ b/dom/workers/WorkerScope.h @@ -132,11 +132,11 @@ public: ClearTimeout(int32_t aHandle); int32_t SetInterval(JSContext* aCx, Function& aHandler, - const Optional<int32_t>& aTimeout, + const int32_t aTimeout, const Sequence<JS::Value>& aArguments, ErrorResult& aRv); int32_t SetInterval(JSContext* aCx, const nsAString& aHandler, - const Optional<int32_t>& aTimeout, + const int32_t aTimeout, const Sequence<JS::Value>& /* unused */, ErrorResult& aRv); void ClearInterval(int32_t aHandle); |