summaryrefslogtreecommitdiff
path: root/dom
diff options
context:
space:
mode:
Diffstat (limited to 'dom')
-rw-r--r--dom/base/nsGlobalWindow.cpp27
-rw-r--r--dom/base/nsGlobalWindow.h4
-rw-r--r--dom/webidl/WindowOrWorkerGlobalScope.webidl4
-rw-r--r--dom/workers/WorkerPrivate.cpp46
-rw-r--r--dom/workers/WorkerPrivate.h19
-rw-r--r--dom/workers/WorkerScope.cpp14
-rw-r--r--dom/workers/WorkerScope.h4
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);