diff options
Diffstat (limited to 'toolkit/modules/Timer.sys.mjs')
-rw-r--r-- | toolkit/modules/Timer.sys.mjs | 137 |
1 files changed, 137 insertions, 0 deletions
diff --git a/toolkit/modules/Timer.sys.mjs b/toolkit/modules/Timer.sys.mjs new file mode 100644 index 0000000000..1d76079196 --- /dev/null +++ b/toolkit/modules/Timer.sys.mjs @@ -0,0 +1,137 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * JS module implementation of setTimeout and clearTimeout. + */ + +// This gives us >=2^30 unique timer IDs, enough for 1 per ms for 12.4 days. +var gNextId = 1; // setTimeout and setInterval must return a positive integer + +var gTimerTable = new Map(); // int -> nsITimer or idleCallback + +// Don't generate this for every timer. +var setTimeout_timerCallbackQI = ChromeUtils.generateQI([ + "nsITimerCallback", + "nsINamed", +]); + +function _setTimeoutOrIsInterval( + aCallback, + aMilliseconds, + aIsInterval, + aTarget, + aArgs +) { + if (typeof aCallback !== "function") { + throw new Error( + `callback is not a function in ${ + aIsInterval ? "setInterval" : "setTimeout" + }` + ); + } + let id = gNextId++; + let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + + if (aTarget) { + timer.target = aTarget; + } + + let callback = { + QueryInterface: setTimeout_timerCallbackQI, + + // nsITimerCallback + notify() { + if (!aIsInterval) { + gTimerTable.delete(id); + } + aCallback.apply(null, aArgs); + }, + + // nsINamed + name: `${ + aIsInterval ? "setInterval" : "setTimeout" + }() for ${Cu.getDebugName(aCallback)}`, + }; + + timer.initWithCallback( + callback, + aMilliseconds, + aIsInterval ? timer.TYPE_REPEATING_SLACK : timer.TYPE_ONE_SHOT + ); + + gTimerTable.set(id, timer); + return id; +} + +export function setTimeout(aCallback, aMilliseconds, ...aArgs) { + return _setTimeoutOrIsInterval(aCallback, aMilliseconds, false, null, aArgs); +} + +export function setTimeoutWithTarget( + aCallback, + aMilliseconds, + aTarget, + ...aArgs +) { + return _setTimeoutOrIsInterval( + aCallback, + aMilliseconds, + false, + aTarget, + aArgs + ); +} + +export function setInterval(aCallback, aMilliseconds, ...aArgs) { + return _setTimeoutOrIsInterval(aCallback, aMilliseconds, true, null, aArgs); +} + +export function setIntervalWithTarget( + aCallback, + aMilliseconds, + aTarget, + ...aArgs +) { + return _setTimeoutOrIsInterval( + aCallback, + aMilliseconds, + true, + aTarget, + aArgs + ); +} + +function clear(aId) { + if (gTimerTable.has(aId)) { + gTimerTable.get(aId).cancel(); + gTimerTable.delete(aId); + } +} +export var clearInterval = clear; +export var clearTimeout = clear; + +export function requestIdleCallback(aCallback, aOptions) { + if (typeof aCallback !== "function") { + throw new Error("callback is not a function in requestIdleCallback"); + } + let id = gNextId++; + + let callback = (...aArgs) => { + if (gTimerTable.has(id)) { + gTimerTable.delete(id); + aCallback(...aArgs); + } + }; + + ChromeUtils.idleDispatch(callback, aOptions); + gTimerTable.set(id, callback); + return id; +} + +export function cancelIdleCallback(aId) { + if (gTimerTable.has(aId)) { + gTimerTable.delete(aId); + } +} |