summaryrefslogtreecommitdiffstats
path: root/toolkit/jetpack/sdk/timers.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/jetpack/sdk/timers.js')
-rw-r--r--toolkit/jetpack/sdk/timers.js105
1 files changed, 105 insertions, 0 deletions
diff --git a/toolkit/jetpack/sdk/timers.js b/toolkit/jetpack/sdk/timers.js
new file mode 100644
index 000000000..e97db01f2
--- /dev/null
+++ b/toolkit/jetpack/sdk/timers.js
@@ -0,0 +1,105 @@
+/* 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/. */
+'use strict';
+
+module.metadata = {
+ "stability": "stable"
+};
+
+const { CC, Cc, Ci } = require("chrome");
+const { when: unload } = require("./system/unload");
+
+const { TYPE_ONE_SHOT, TYPE_REPEATING_SLACK } = Ci.nsITimer;
+const Timer = CC("@mozilla.org/timer;1", "nsITimer");
+const timers = Object.create(null);
+const threadManager = Cc["@mozilla.org/thread-manager;1"].
+ getService(Ci.nsIThreadManager);
+const prefBranch = Cc["@mozilla.org/preferences-service;1"].
+ getService(Ci.nsIPrefService).
+ QueryInterface(Ci.nsIPrefBranch);
+
+var MIN_DELAY = 4;
+// Try to get min timeout delay used by browser.
+try { MIN_DELAY = prefBranch.getIntPref("dom.min_timeout_value"); } finally {}
+
+
+// Last timer id.
+var lastID = 0;
+
+// Sets typer either by timeout or by interval
+// depending on a given type.
+function setTimer(type, callback, delay, ...args) {
+ let id = ++ lastID;
+ let timer = timers[id] = Timer();
+ timer.initWithCallback({
+ notify: function notify() {
+ try {
+ if (type === TYPE_ONE_SHOT)
+ delete timers[id];
+ callback.apply(null, args);
+ }
+ catch(error) {
+ console.exception(error);
+ }
+ }
+ }, Math.max(delay || MIN_DELAY), type);
+ return id;
+}
+
+function unsetTimer(id) {
+ let timer = timers[id];
+ delete timers[id];
+ if (timer) timer.cancel();
+}
+
+var immediates = new Map();
+
+var dispatcher = _ => {
+ // Allow scheduling of a new dispatch loop.
+ dispatcher.scheduled = false;
+ // Take a snapshot of timer `id`'s that have being present before
+ // starting a dispatch loop, in order to ignore timers registered
+ // in side effect to dispatch while also skipping immediates that
+ // were removed in side effect.
+ let ids = [...immediates.keys()];
+ for (let id of ids) {
+ let immediate = immediates.get(id);
+ if (immediate) {
+ immediates.delete(id);
+ try { immediate(); }
+ catch (error) { console.exception(error); }
+ }
+ }
+}
+
+function setImmediate(callback, ...params) {
+ let id = ++ lastID;
+ // register new immediate timer with curried params.
+ immediates.set(id, _ => callback.apply(callback, params));
+ // if dispatch loop is not scheduled schedule one. Own scheduler
+ if (!dispatcher.scheduled) {
+ dispatcher.scheduled = true;
+ threadManager.currentThread.dispatch(dispatcher,
+ Ci.nsIThread.DISPATCH_NORMAL);
+ }
+ return id;
+}
+
+function clearImmediate(id) {
+ immediates.delete(id);
+}
+
+// Bind timers so that toString-ing them looks same as on native timers.
+exports.setImmediate = setImmediate.bind(null);
+exports.clearImmediate = clearImmediate.bind(null);
+exports.setTimeout = setTimer.bind(null, TYPE_ONE_SHOT);
+exports.setInterval = setTimer.bind(null, TYPE_REPEATING_SLACK);
+exports.clearTimeout = unsetTimer.bind(null);
+exports.clearInterval = unsetTimer.bind(null);
+
+// all timers are cleared out on unload.
+unload(function() {
+ immediates.clear();
+ Object.keys(timers).forEach(unsetTimer)
+});