michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: 'use strict'; michael@0: michael@0: module.metadata = { michael@0: "stability": "stable" michael@0: }; michael@0: michael@0: const { CC, Cc, Ci } = require("chrome"); michael@0: const { when: unload } = require("./system/unload"); michael@0: michael@0: const { TYPE_ONE_SHOT, TYPE_REPEATING_SLACK } = Ci.nsITimer; michael@0: const Timer = CC("@mozilla.org/timer;1", "nsITimer"); michael@0: const timers = Object.create(null); michael@0: const threadManager = Cc["@mozilla.org/thread-manager;1"]. michael@0: getService(Ci.nsIThreadManager); michael@0: const prefBranch = Cc["@mozilla.org/preferences-service;1"]. michael@0: getService(Ci.nsIPrefService). michael@0: QueryInterface(Ci.nsIPrefBranch); michael@0: michael@0: let MIN_DELAY = 4; michael@0: // Try to get min timeout delay used by browser. michael@0: try { MIN_DELAY = prefBranch.getIntPref("dom.min_timeout_value"); } finally {} michael@0: michael@0: michael@0: // Last timer id. michael@0: let lastID = 0; michael@0: michael@0: // Sets typer either by timeout or by interval michael@0: // depending on a given type. michael@0: function setTimer(type, callback, delay, ...args) { michael@0: let id = ++ lastID; michael@0: let timer = timers[id] = Timer(); michael@0: timer.initWithCallback({ michael@0: notify: function notify() { michael@0: try { michael@0: if (type === TYPE_ONE_SHOT) michael@0: delete timers[id]; michael@0: callback.apply(null, args); michael@0: } michael@0: catch(error) { michael@0: console.exception(error); michael@0: } michael@0: } michael@0: }, Math.max(delay || MIN_DELAY), type); michael@0: return id; michael@0: } michael@0: michael@0: function unsetTimer(id) { michael@0: let timer = timers[id]; michael@0: delete timers[id]; michael@0: if (timer) timer.cancel(); michael@0: } michael@0: michael@0: let immediates = new Map(); michael@0: michael@0: let dispatcher = _ => { michael@0: // Allow scheduling of a new dispatch loop. michael@0: dispatcher.scheduled = false; michael@0: // Take a snapshot of timer `id`'s that have being present before michael@0: // starting a dispatch loop, in order to ignore timers registered michael@0: // in side effect to dispatch while also skipping immediates that michael@0: // were removed in side effect. michael@0: let ids = [id for ([id] of immediates)]; michael@0: for (let id of ids) { michael@0: let immediate = immediates.get(id); michael@0: if (immediate) { michael@0: immediates.delete(id); michael@0: try { immediate(); } michael@0: catch (error) { console.exception(error); } michael@0: } michael@0: } michael@0: } michael@0: michael@0: function setImmediate(callback, ...params) { michael@0: let id = ++ lastID; michael@0: // register new immediate timer with curried params. michael@0: immediates.set(id, _ => callback.apply(callback, params)); michael@0: // if dispatch loop is not scheduled schedule one. Own scheduler michael@0: if (!dispatcher.scheduled) { michael@0: dispatcher.scheduled = true; michael@0: threadManager.currentThread.dispatch(dispatcher, michael@0: Ci.nsIThread.DISPATCH_NORMAL); michael@0: } michael@0: return id; michael@0: } michael@0: michael@0: function clearImmediate(id) { michael@0: immediates.delete(id); michael@0: } michael@0: michael@0: // Bind timers so that toString-ing them looks same as on native timers. michael@0: exports.setImmediate = setImmediate.bind(null); michael@0: exports.clearImmediate = clearImmediate.bind(null); michael@0: exports.setTimeout = setTimer.bind(null, TYPE_ONE_SHOT); michael@0: exports.setInterval = setTimer.bind(null, TYPE_REPEATING_SLACK); michael@0: exports.clearTimeout = unsetTimer.bind(null); michael@0: exports.clearInterval = unsetTimer.bind(null); michael@0: michael@0: // all timers are cleared out on unload. michael@0: unload(function() { michael@0: immediates.clear(); michael@0: Object.keys(timers).forEach(unsetTimer) michael@0: });