Branch data Line data Source code
1 : : // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
2 : : // SPDX-FileCopyrightText: 2021 Evan Welsh <contact@evanwelsh.com>
3 : :
4 : : /* exported setTimeout, setInterval, clearTimeout, clearInterval */
5 : : /* eslint no-implicit-coercion: ["error", {"allow": ["+"]}] */
6 : : // Note: implicit coercion with + is used to perform the ToNumber algorithm from
7 : : // the timers specification
8 : :
9 : : /**
10 : : * @param {number} delay a number value (in milliseconds)
11 : : */
12 : : function validateDelay(delay) {
13 : : // |0 always returns a signed 32-bit integer.
14 : 9776 : return Math.max(0, +delay | 0);
15 : : }
16 : :
17 : : /** @type {Map<GLib.Source, number>} */
18 : 47 : const timeouts = new Map();
19 : :
20 : : /**
21 : : * @param {GLib.Source} source the source to add to our map
22 : : */
23 : 9806 : function addSource(source) {
24 : 9806 : const id = source.attach(null);
25 : 9806 : timeouts.set(source, id);
26 : : }
27 : :
28 : : /**
29 : : * @param {GLib.Source} source the source object to remove from our map
30 : : */
31 : : function releaseSource(source) {
32 : 9892 : timeouts.delete(source);
33 : : }
34 : :
35 : : /**
36 : : * @param {unknown} thisArg 'this' argument
37 : : * @returns {asserts thisArg is (null | undefined | typeof globalThis)}
38 : : */
39 : : function checkThis(thisArg) {
40 [ + + ][ + + ]: 9775 : if (thisArg !== null && thisArg !== undefined && thisArg !== globalThis)
[ + + ]
41 : 9 : throw new TypeError('Illegal invocation');
42 : : }
43 : :
44 : : /**
45 : : * @param {number} timeout a timeout in milliseconds
46 : : * @param {(...args) => any} handler a callback
47 : : * @returns {GLib.Source}
48 : : */
49 : 9786 : function createTimeoutSource(timeout, handler) {
50 : 9786 : const source = imports.gi.GLib.timeout_source_new(timeout);
51 : 9786 : source.set_priority(imports.gi.GLib.PRIORITY_DEFAULT);
52 : 9786 : imports.gi.GObject.source_set_closure(source, handler);
53 : :
54 : 9786 : return source;
55 : 9786 : }
56 : :
57 : : /**
58 : : * @this {typeof globalThis}
59 : : * @param {(...args) => any} callback a callback function
60 : : * @param {number} delay the duration in milliseconds to wait before running callback
61 : : * @param {...any} args arguments to pass to callback
62 : : */
63 [ + + ]: 9773 : function setTimeout(callback, delay = 0, ...args) {
64 : 9773 : checkThis(this);
65 : :
66 : 9764 : delay = validateDelay(delay);
67 [ + + ]: 9764 : const boundCallback = callback.bind(globalThis, ...args);
68 : 9774 : const source = createTimeoutSource(delay, () => {
69 [ + - ]: 7456 : if (!timeouts.has(source))
70 : 0 : return imports.gi.GLib.SOURCE_REMOVE;
71 : :
72 : 7456 : boundCallback();
73 : 7456 : releaseSource(source);
74 : 7456 : import.meta.importSync('_promiseNative').drainMicrotaskQueue();
75 : :
76 : 7456 : return imports.gi.GLib.SOURCE_REMOVE;
77 : : });
78 : :
79 : 9774 : addSource(source);
80 : 9774 : return source;
81 : 9774 : }
82 : :
83 : : /**
84 : : * @this {typeof globalThis}
85 : : * @param {(...args) => any} callback a callback function
86 : : * @param {number} delay the duration in milliseconds to wait between calling callback
87 : : * @param {...any} args arguments to pass to callback
88 : : */
89 [ + - ]: 2 : function setInterval(callback, delay = 0, ...args) {
90 : 2 : checkThis(this);
91 : :
92 : 2 : delay = validateDelay(delay);
93 [ + - ]: 2 : const boundCallback = callback.bind(globalThis, ...args);
94 : 2 : const source = createTimeoutSource(delay, () => {
95 [ + - ]: 1 : if (!timeouts.has(source))
96 : 0 : return imports.gi.GLib.SOURCE_REMOVE;
97 : :
98 : 1 : boundCallback();
99 : 1 : import.meta.importSync('_promiseNative').drainMicrotaskQueue();
100 : :
101 : 1 : return imports.gi.GLib.SOURCE_CONTINUE;
102 : : });
103 : :
104 : 2 : addSource(source);
105 : 2 : return source;
106 : 2 : }
107 : :
108 : : /**
109 : : * @param {GLib.Source} source the timeout to clear
110 : : */
111 : : function _clearTimer(source) {
112 [ + + ]: 3393 : if (!timeouts.has(source))
113 : 3 : return;
114 : :
115 [ - + ]: 3390 : if (source) {
116 : 3390 : source.destroy();
117 : 3390 : releaseSource(source);
118 : : }
119 : : }
120 : :
121 : : /**
122 : : * @param {GLib.Source} timeout the timeout to clear
123 : : */
124 [ + - ]: 3389 : function clearTimeout(timeout = null) {
125 : 3389 : _clearTimer(timeout);
126 : : }
127 : :
128 : : /**
129 : : * @param {Glib.Source} timeout the timeout to clear
130 : : */
131 [ + - ]: 4 : function clearInterval(timeout = null) {
132 : 4 : _clearTimer(timeout);
133 : : }
134 : :
135 : 94 : Object.defineProperty(globalThis, 'setTimeout', {
136 : 47 : configurable: false,
137 : 47 : enumerable: true,
138 : 47 : writable: true,
139 : 47 : value: setTimeout,
140 : : });
141 : :
142 : 94 : Object.defineProperty(globalThis, 'setInterval', {
143 : 47 : configurable: false,
144 : 47 : enumerable: true,
145 : 47 : writable: true,
146 : 47 : value: setInterval,
147 : : });
148 : :
149 : 94 : Object.defineProperty(globalThis, 'clearTimeout', {
150 : 47 : configurable: false,
151 : 47 : enumerable: true,
152 : 47 : writable: true,
153 : 47 : value: clearTimeout,
154 : : });
155 : :
156 : 94 : Object.defineProperty(globalThis, 'clearInterval', {
157 : 47 : configurable: false,
158 : 47 : enumerable: true,
159 : 47 : writable: true,
160 : 47 : value: clearInterval,
161 : : });
|