Branch data Line data Source code
1 : 95 : // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
2 : : // SPDX-FileCopyrightText: 2011 Giovanni Campagna
3 : :
4 : 19 : var GLib = imports.gi.GLib;
5 : 19 : var GjsPrivate = imports.gi.GjsPrivate;
6 : 19 : var Signals = imports.signals;
7 : : var Gio;
8 : :
9 : : // Ensures that a Gio.UnixFDList being passed into or out of a DBus method with
10 : : // a parameter type that includes 'h' somewhere, actually has entries in it for
11 : : // each of the indices being passed as an 'h' parameter.
12 : 12 : function _validateFDVariant(variant, fdList) {
13 [ - + ][ - + ]: 12 : switch (String.fromCharCode(variant.classify())) {
[ - + ][ - + ]
[ - + ][ - + ]
[ - + ][ - + ]
[ - + ][ - + ]
[ - + ][ - + ]
[ + + ][ - + ]
[ - + ][ - + ]
[ + - ][ # # ]
14 : : case 'b':
15 : : case 'y':
16 : : case 'n':
17 : : case 'q':
18 : : case 'i':
19 : : case 'u':
20 : : case 'x':
21 : : case 't':
22 : : case 'd':
23 : : case 'o':
24 : : case 'g':
25 : : case 's':
26 : 0 : return;
27 : 6 : case 'h': {
28 : 6 : const val = variant.get_handle();
29 : 6 : const numFds = fdList.get_length();
30 [ + + ]: 6 : if (val >= numFds) {
31 : 4 : throw new Error(`handle ${val} is out of range of Gio.UnixFDList ` +
32 : 2 : `containing ${numFds} FDs`);
33 : : }
34 : 4 : return;
35 : : }
36 : : case 'v':
37 : 0 : _validateFDVariant(variant.get_variant(), fdList);
38 : 0 : return;
39 : 0 : case 'm': {
40 : 0 : let val = variant.get_maybe();
41 [ # # ]: 0 : if (val)
42 : 0 : _validateFDVariant(val, fdList);
43 : 0 : return;
44 : : }
45 : : case 'a':
46 : : case '(':
47 : 6 : case '{': {
48 : 6 : let nElements = variant.n_children();
49 [ + + ]: 10 : for (let ix = 0; ix < nElements; ix++)
50 : 6 : _validateFDVariant(variant.get_child_value(ix), fdList);
51 : 4 : return;
52 : : }
53 : : }
54 : :
55 : 0 : throw new Error('Assertion failure: this code should not be reached');
56 : 8 : }
57 : :
58 : 53 : function _proxyInvoker(methodName, sync, inSignature, argArray) {
59 : : var replyFunc;
60 : 53 : var flags = 0;
61 : 53 : var cancellable = null;
62 : 53 : let fdList = null;
63 : :
64 : : /* Convert argArray to a *real* array */
65 : 53 : argArray = Array.prototype.slice.call(argArray);
66 : :
67 : : /* The default replyFunc only logs the responses */
68 : 53 : replyFunc = _logReply;
69 : :
70 : 53 : var signatureLength = inSignature.length;
71 : 53 : var minNumberArgs = signatureLength;
72 : 53 : var maxNumberArgs = signatureLength + 4;
73 : :
74 [ + - ]: 53 : if (argArray.length < minNumberArgs) {
75 : 0 : throw new Error(`Not enough arguments passed for method: ${
76 : 0 : methodName}. Expected ${minNumberArgs}, got ${argArray.length}`);
77 [ + - ]: 53 : } else if (argArray.length > maxNumberArgs) {
78 : 0 : throw new Error(`Too many arguments passed for method ${methodName}. ` +
79 : 0 : `Maximum is ${maxNumberArgs} including one callback, ` +
80 : 0 : 'Gio.Cancellable, Gio.UnixFDList, and/or flags');
81 : : }
82 : :
83 [ + + ]: 113 : while (argArray.length > signatureLength) {
84 : 59 : var argNum = argArray.length - 1;
85 : 59 : var arg = argArray.pop();
86 [ + + ][ + + ]: 59 : if (typeof arg === 'function' && !sync) {
87 : 53 : replyFunc = arg;
88 [ + - ]: 6 : } else if (typeof arg === 'number') {
89 : 0 : flags = arg;
90 [ + - ]: 6 : } else if (arg instanceof Gio.Cancellable) {
91 : 0 : cancellable = arg;
92 [ - + ]: 6 : } else if (arg instanceof Gio.UnixFDList) {
93 : 6 : fdList = arg;
94 : : } else {
95 : 0 : throw new Error(`Argument ${argNum} of method ${methodName} is ` +
96 : 0 : `${typeof arg}. It should be a callback, flags, ` +
97 : 0 : 'Gio.UnixFDList, or a Gio.Cancellable');
98 : : }
99 : : }
100 : :
101 : 53 : const inTypeString = `(${inSignature.join('')})`;
102 : 53 : const inVariant = new GLib.Variant(inTypeString, argArray);
103 [ + + ]: 53 : if (inTypeString.includes('h')) {
104 [ + + ]: 8 : if (!fdList) {
105 : 4 : throw new Error(`Method ${methodName} with input type containing ` +
106 : 2 : '\'h\' must have a Gio.UnixFDList as an argument');
107 : : }
108 : 6 : _validateFDVariant(inVariant, fdList);
109 : : }
110 : :
111 : 49 : var asyncCallback = (proxy, result) => {
112 : 49 : try {
113 [ # # ][ # # ]: 0 : const [outVariant, outFdList] =
[ # # ][ # # ]
114 [ - + ][ - + ]: 49 : proxy.call_with_unix_fd_list_finish(result);
[ - + ]
115 : 42 : replyFunc(outVariant.deepUnpack(), null, outFdList);
116 : 7 : } catch (e) {
117 : 7 : replyFunc([], e, null);
118 : : }
119 : : };
120 : :
121 [ + - ]: 49 : if (sync) {
122 [ # # ][ # # ]: 0 : const [outVariant, outFdList] = this.call_with_unix_fd_list_sync(
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
123 : 0 : methodName, inVariant, flags, -1, fdList, cancellable);
124 [ # # ]: 0 : if (fdList)
125 : 0 : return [outVariant.deepUnpack(), outFdList];
126 : 0 : return outVariant.deepUnpack();
127 : : }
128 : :
129 : 98 : return this.call_with_unix_fd_list(methodName, inVariant, flags, -1, fdList,
130 : 49 : cancellable, asyncCallback);
131 : 49 : }
132 : :
133 : 0 : function _logReply(result, exc) {
134 [ # # ]: 0 : if (exc !== null)
135 : 0 : log(`Ignored exception from dbus method: ${exc}`);
136 : : }
137 : :
138 : 352 : function _makeProxyMethod(method, sync) {
139 : : var i;
140 : 352 : var name = method.name;
141 : 352 : var inArgs = method.in_args;
142 : 352 : var inSignature = [];
143 [ + + ]: 624 : for (i = 0; i < inArgs.length; i++)
144 : 272 : inSignature.push(inArgs[i].signature);
145 : :
146 : 405 : return function (...args) {
147 : 53 : return _proxyInvoker.call(this, name, sync, inSignature, args);
148 : : };
149 : : }
150 : :
151 : 6 : function _convertToNativeSignal(proxy, senderName, signalName, parameters) {
152 : 6 : Signals._emit.call(proxy, signalName, senderName, parameters.deepUnpack());
153 : : }
154 : :
155 : 6 : function _propertyGetter(name) {
156 : 6 : let value = this.get_cached_property(name);
157 [ - + ]: 6 : return value ? value.deepUnpack() : null;
158 : 6 : }
159 : :
160 : 2 : function _propertySetter(name, signature, value) {
161 : 2 : let variant = new GLib.Variant(signature, value);
162 : 2 : this.set_cached_property(name, variant);
163 : :
164 : 4 : this.call('org.freedesktop.DBus.Properties.Set',
165 : 2 : new GLib.Variant('(ssv)', [this.g_interface_name, name, variant]),
166 : 2 : Gio.DBusCallFlags.NONE, -1, null,
167 : 2 : (proxy, result) => {
168 : 2 : try {
169 : 2 : this.call_finish(result);
170 : 0 : } catch (e) {
171 : 0 : log(`Could not set property ${name} on remote object ${
172 : 0 : this.g_object_path}: ${e.message}`);
173 : : }
174 : : });
175 : : }
176 : :
177 : 8 : function _addDBusConvenience() {
178 : 8 : let info = this.g_interface_info;
179 [ + - ]: 8 : if (!info)
180 : 0 : return;
181 : :
182 [ - + ]: 8 : if (info.signals.length > 0)
183 : 8 : this.connect('g-signal', _convertToNativeSignal);
184 : :
185 : 8 : let i, methods = info.methods;
186 [ + + ]: 184 : for (i = 0; i < methods.length; i++) {
187 : 176 : var method = methods[i];
188 : 176 : let remoteMethod = _makeProxyMethod(methods[i], false);
189 : 176 : this[`${method.name}Remote`] = remoteMethod;
190 : 176 : this[`${method.name}Sync`] = _makeProxyMethod(methods[i], true);
191 : 202 : this[`${method.name}Async`] = function (...args) {
192 : 26 : return new Promise((resolve, reject) => {
193 : 26 : args.push((result, error, fdList) => {
194 [ + + ]: 24 : if (error)
195 : 3 : reject(error);
196 [ + + ]: 21 : else if (fdList)
197 : 2 : resolve([result, fdList]);
198 : : else
199 : 19 : resolve(result);
200 : : });
201 [ + + ]: 26 : remoteMethod.call(this, ...args);
202 : : });
203 : : };
204 : : }
205 : :
206 : 8 : let properties = info.properties;
207 [ + + ]: 40 : for (i = 0; i < properties.length; i++) {
208 : 32 : let name = properties[i].name;
209 : 32 : let signature = properties[i].signature;
210 : 32 : let flags = properties[i].flags;
211 : 32 : let getter = () => {
212 : 2 : throw new Error(`Property ${name} is not readable`);
213 : : };
214 : 32 : let setter = () => {
215 : 1 : throw new Error(`Property ${name} is not writable`);
216 : : };
217 : :
218 [ + + ]: 32 : if (flags & Gio.DBusPropertyInfoFlags.READABLE)
219 : 24 : getter = _propertyGetter.bind(this, name);
220 : :
221 [ + + ]: 32 : if (flags & Gio.DBusPropertyInfoFlags.WRITABLE)
222 : 16 : setter = _propertySetter.bind(this, name, signature);
223 : :
224 : 64 : Object.defineProperty(this, name, {
225 : 32 : get: getter,
226 : 32 : set: setter,
227 : 32 : configurable: false,
228 : 32 : enumerable: true,
229 : : });
230 : : }
231 : 0 : }
232 : :
233 : 1 : function _makeProxyWrapper(interfaceXml) {
234 : 1 : var info = _newInterfaceInfo(interfaceXml);
235 : 1 : var iname = info.name;
236 [ + + ]: 4 : function wrapper(bus, name, object, asyncCallback, cancellable,
237 : 3 : flags = Gio.DBusProxyFlags.NONE) {
238 : 8 : var obj = new Gio.DBusProxy({
239 : 4 : g_connection: bus,
240 : 4 : g_interface_name: iname,
241 : 4 : g_interface_info: info,
242 : 4 : g_name: name,
243 : 4 : g_flags: flags,
244 : 4 : g_object_path: object,
245 : : });
246 : :
247 [ + + ]: 4 : if (!cancellable)
248 : 2 : cancellable = null;
249 [ + + ]: 4 : if (asyncCallback) {
250 : 6 : obj.init_async(GLib.PRIORITY_DEFAULT, cancellable).then(
251 : 9 : () => asyncCallback(obj, null)).catch(e => asyncCallback(null, e));
252 : : } else {
253 : 1 : obj.init(cancellable);
254 : : }
255 : 3 : return obj;
256 : : }
257 [ - + ]: 4 : wrapper.newAsync = function newAsync(bus, name, object, cancellable,
258 : 3 : flags = Gio.DBusProxyFlags.NONE) {
259 : 6 : const obj = new Gio.DBusProxy({
260 : 3 : g_connection: bus,
261 : 3 : g_interface_name: info.name,
262 : 3 : g_interface_info: info,
263 : 3 : g_name: name,
264 : 3 : g_flags: flags,
265 : 3 : g_object_path: object,
266 : : });
267 : :
268 : 3 : return new Promise((resolve, reject) =>
269 [ + + ]: 6 : obj.init_async(GLib.PRIORITY_DEFAULT, cancellable ?? null).then(
270 : 8 : () => resolve(obj)).catch(reject));
271 : 3 : };
272 : 1 : return wrapper;
273 : : }
274 : :
275 : :
276 : 3 : function _newNodeInfo(constructor, value) {
277 [ - + ]: 3 : if (typeof value === 'string')
278 : 3 : return constructor(value);
279 : 0 : throw TypeError(`Invalid type ${Object.prototype.toString.call(value)}`);
280 : : }
281 : :
282 : 2 : function _newInterfaceInfo(value) {
283 : 2 : var nodeInfo = Gio.DBusNodeInfo.new_for_xml(value);
284 : 2 : return nodeInfo.interfaces[0];
285 : : }
286 : :
287 : 38 : function _injectToMethod(klass, method, addition) {
288 : 38 : var previous = klass[method];
289 : :
290 : 45 : klass[method] = function (...args) {
291 : 7 : addition.apply(this, args);
292 : 7 : return previous.apply(this, args);
293 : : };
294 : : }
295 : :
296 : 76 : function _injectToStaticMethod(klass, method, addition) {
297 : 76 : var previous = klass[method];
298 : :
299 : 77 : klass[method] = function (...parameters) {
300 : 1 : let obj = previous.apply(this, parameters);
301 : 1 : addition.apply(obj, parameters);
302 : 1 : return obj;
303 : 1 : };
304 : : }
305 : :
306 : 19 : function _wrapFunction(klass, method, addition) {
307 : 19 : var previous = klass[method];
308 : :
309 : 22 : klass[method] = function (...args) {
310 : 3 : args.unshift(previous);
311 : 3 : return addition.apply(this, args);
312 : : };
313 : : }
314 : :
315 : 34 : function _makeOutSignature(args) {
316 : 34 : var ret = '(';
317 [ + + ]: 74 : for (var i = 0; i < args.length; i++)
318 : 40 : ret += args[i].signature;
319 : :
320 : 34 : return `${ret})`;
321 : : }
322 : :
323 : 49 : function _handleMethodCall(info, impl, methodName, parameters, invocation) {
324 : : // prefer a sync version if available
325 [ + + ]: 49 : if (this[methodName]) {
326 : 41 : let retval;
327 : 41 : try {
328 : 41 : const fdList = invocation.get_message().get_unix_fd_list();
329 [ + + ]: 41 : retval = this[methodName](...parameters.deepUnpack(), fdList);
330 : 3 : } catch (e) {
331 [ + - ]: 3 : if (e instanceof GLib.Error) {
332 : 0 : invocation.return_gerror(e);
333 : 3 : } else {
334 : 3 : let name = e.name;
335 [ - + ]: 3 : if (!name.includes('.')) {
336 : : // likely to be a normal JS error
337 : 3 : name = `org.gnome.gjs.JSError.${name}`;
338 : : }
339 : 3 : logError(e, `Exception in method call: ${methodName}`);
340 : 3 : invocation.return_dbus_error(name, e.message);
341 : : }
342 : 3 : return;
343 : : }
344 [ + + ]: 38 : if (retval === undefined) {
345 : : // undefined (no return value) is the empty tuple
346 : 4 : retval = new GLib.Variant('()', []);
347 : : }
348 : 38 : try {
349 : 38 : let outFdList = null;
350 [ + + ]: 38 : if (!(retval instanceof GLib.Variant)) {
351 : : // attempt packing according to out signature
352 : 34 : let methodInfo = info.lookup_method(methodName);
353 : 34 : let outArgs = methodInfo.out_args;
354 : 34 : let outSignature = _makeOutSignature(outArgs);
355 [ + + ]: 34 : if (outSignature.includes('h') &&
356 [ + + ]: 2 : retval[retval.length - 1] instanceof Gio.UnixFDList) {
357 : 2 : outFdList = retval.pop();
358 [ + + ]: 32 : } else if (outArgs.length === 1) {
359 : : // if one arg, we don't require the handler wrapping it
360 : : // into an Array
361 : 28 : retval = [retval];
362 : : }
363 : 34 : retval = new GLib.Variant(outSignature, retval);
364 : : }
365 : 36 : invocation.return_value_with_unix_fd_list(retval, outFdList);
366 : 2 : } catch (e) {
367 : : // if we don't do this, the other side will never see a reply
368 : 4 : invocation.return_dbus_error('org.gnome.gjs.JSError.ValueError',
369 : 2 : 'Service implementation returned an incorrect value type');
370 : : }
371 [ + + ]: 8 : } else if (this[`${methodName}Async`]) {
372 : 6 : const fdList = invocation.get_message().get_unix_fd_list();
373 : 6 : this[`${methodName}Async`](parameters.deepUnpack(), invocation, fdList);
374 : : } else {
375 : 2 : log(`Missing handler for DBus method ${methodName}`);
376 : 4 : invocation.return_gerror(new Gio.DBusError({
377 : 2 : code: Gio.DBusError.UNKNOWN_METHOD,
378 : 2 : message: `Method ${methodName} is not implemented`,
379 : : }));
380 : : }
381 : 3 : }
382 : :
383 : 9 : function _handlePropertyGet(info, impl, propertyName) {
384 : 9 : let propInfo = info.lookup_property(propertyName);
385 : 9 : let jsval = this[propertyName];
386 [ - + ][ + + ]: 9 : if (jsval?.get_type_string?.() === propInfo.signature)
[ + + ]
387 : 3 : return jsval;
388 [ - + ]: 6 : else if (jsval !== undefined)
389 : 6 : return new GLib.Variant(propInfo.signature, jsval);
390 : : else
391 : 0 : return null;
392 : 9 : }
393 : :
394 : 2 : function _handlePropertySet(info, impl, propertyName, newValue) {
395 : 2 : this[propertyName] = newValue.deepUnpack();
396 : : }
397 : :
398 : 1 : function _wrapJSObject(interfaceInfo, jsObj) {
399 : : var info;
400 [ + - ]: 1 : if (interfaceInfo instanceof Gio.DBusInterfaceInfo)
401 : 0 : info = interfaceInfo;
402 : : else
403 : 1 : info = Gio.DBusInterfaceInfo.new_for_xml(interfaceInfo);
404 : 1 : info.cache_build();
405 : :
406 : 1 : var impl = new GjsPrivate.DBusImplementation({g_interface_info: info});
407 : 1 : impl.connect('handle-method-call', function (self, methodName, parameters, invocation) {
408 : 49 : return _handleMethodCall.call(jsObj, info, self, methodName, parameters, invocation);
409 : : });
410 : 1 : impl.connect('handle-property-get', function (self, propertyName) {
411 : 9 : return _handlePropertyGet.call(jsObj, info, self, propertyName);
412 : : });
413 : 1 : impl.connect('handle-property-set', function (self, propertyName, value) {
414 : 2 : return _handlePropertySet.call(jsObj, info, self, propertyName, value);
415 : : });
416 : :
417 : 1 : return impl;
418 : : }
419 : :
420 : 3 : function* _listModelIterator() {
421 : 3 : let _index = 0;
422 : 3 : const _len = this.get_n_items();
423 [ + + ]: 124 : while (_index < _len)
424 : 120 : yield this.get_item(_index++);
425 : 3 : }
426 : :
427 [ + + ]: 26 : function _promisify(proto, asyncFunc, finishFunc = undefined) {
428 [ + + ]: 26 : if (proto[asyncFunc] === undefined)
429 : 1 : throw new Error(`${proto} has no method named ${asyncFunc}`);
430 : :
431 [ + + ]: 25 : if (finishFunc === undefined) {
432 [ - + ][ + + ]: 24 : if (asyncFunc.endsWith('_begin') || asyncFunc.endsWith('_async'))
433 : 23 : finishFunc = `${asyncFunc.slice(0, -5)}finish`;
434 : : else
435 : 1 : finishFunc = `${asyncFunc}_finish`;
436 : : }
437 : :
438 [ + + ]: 25 : if (proto[finishFunc] === undefined)
439 : 1 : throw new Error(`${proto} has no method named ${finishFunc}`);
440 : :
441 : 24 : const originalFuncName = `_original_${asyncFunc}`;
442 [ + + ]: 24 : if (proto[originalFuncName] !== undefined)
443 : 2 : return;
444 : 22 : proto[originalFuncName] = proto[asyncFunc];
445 : 30 : proto[asyncFunc] = function (...args) {
446 [ + + ]: 8 : if (args.length === this[originalFuncName].length)
447 [ + - ][ # # ]: 1 : return this[originalFuncName](...args);
448 : 14 : return new Promise((resolve, reject) => {
449 : 7 : let {stack: callStack} = new Error();
450 [ + + ]: 7 : this[originalFuncName](...args, function (source, res) {
451 : 7 : try {
452 [ - + ][ - + ]: 7 : const result = source !== null && source[finishFunc] !== undefined
453 : 7 : ? source[finishFunc](res)
454 : 0 : : proto[finishFunc](res);
455 [ + - ][ # # ]: 5 : if (Array.isArray(result) && result.length > 1 && result[0] === true)
[ + - ]
456 : 0 : result.shift();
457 : 5 : resolve(result);
458 : 2 : } catch (error) {
459 : 2 : callStack = callStack.split('\n').filter(line =>
460 : 31 : line.indexOf('_promisify/') === -1).join('\n');
461 [ - + ]: 2 : if (error.stack)
462 : 2 : error.stack += `### Promise created here: ###\n${callStack}`;
463 : : else
464 : 0 : error.stack = callStack;
465 : 2 : reject(error);
466 : : }
467 : : });
468 : : });
469 : : };
470 : 2 : }
471 : :
472 : 13 : function _notIntrospectableError(funcName, replacement) {
473 : 13 : return new Error(`${funcName} is not introspectable. Use ${replacement} instead.`);
474 : : }
475 : :
476 : 10 : function _warnNotIntrospectable(funcName, replacement) {
477 : 10 : logError(_notIntrospectableError(funcName, replacement));
478 : : }
479 : :
480 : 19 : function _init() {
481 : 19 : Gio = this;
482 : :
483 : 19 : Gio.Application.prototype.runAsync = GLib.MainLoop.prototype.runAsync;
484 : :
485 : 19 : Gio.DBus = {
486 : : // Namespace some functions
487 : 19 : get: Gio.bus_get,
488 : 19 : get_finish: Gio.bus_get_finish,
489 : 19 : get_sync: Gio.bus_get_sync,
490 : :
491 : 19 : own_name: Gio.bus_own_name,
492 : 19 : own_name_on_connection: Gio.bus_own_name_on_connection,
493 : 19 : unown_name: Gio.bus_unown_name,
494 : :
495 : 19 : watch_name: Gio.bus_watch_name,
496 : 19 : watch_name_on_connection: Gio.bus_watch_name_on_connection,
497 : 19 : unwatch_name: Gio.bus_unwatch_name,
498 : : };
499 : :
500 : 38 : Object.defineProperties(Gio.DBus, {
501 : 19 : 'session': {
502 : 19 : get() {
503 : 10 : return Gio.bus_get_sync(Gio.BusType.SESSION, null);
504 : : },
505 : 19 : enumerable: false,
506 : : },
507 : 19 : 'system': {
508 : 19 : get() {
509 : 0 : return Gio.bus_get_sync(Gio.BusType.SYSTEM, null);
510 : : },
511 : 19 : enumerable: false,
512 : : },
513 : : });
514 : :
515 : 19 : Gio.DBusConnection.prototype.watch_name = function (name, flags, appeared, vanished) {
516 : 0 : return Gio.bus_watch_name_on_connection(this, name, flags, appeared, vanished);
517 : : };
518 : 19 : Gio.DBusConnection.prototype.unwatch_name = function (id) {
519 : 0 : return Gio.bus_unwatch_name(id);
520 : : };
521 : 19 : Gio.DBusConnection.prototype.own_name = function (name, flags, acquired, lost) {
522 : 1 : return Gio.bus_own_name_on_connection(this, name, flags, acquired, lost);
523 : : };
524 : 19 : Gio.DBusConnection.prototype.unown_name = function (id) {
525 : 1 : return Gio.bus_unown_name(id);
526 : : };
527 : :
528 : 19 : _injectToMethod(Gio.DBusProxy.prototype, 'init', _addDBusConvenience);
529 : 19 : _promisify(Gio.DBusProxy.prototype, 'init_async');
530 : 19 : _injectToMethod(Gio.DBusProxy.prototype, 'init_async', _addDBusConvenience);
531 : 19 : _injectToStaticMethod(Gio.DBusProxy, 'new_sync', _addDBusConvenience);
532 : 19 : _injectToStaticMethod(Gio.DBusProxy, 'new_finish', _addDBusConvenience);
533 : 19 : _injectToStaticMethod(Gio.DBusProxy, 'new_for_bus_sync', _addDBusConvenience);
534 : 19 : _injectToStaticMethod(Gio.DBusProxy, 'new_for_bus_finish', _addDBusConvenience);
535 : 19 : Gio.DBusProxy.prototype.connectSignal = Signals._connect;
536 : 19 : Gio.DBusProxy.prototype.disconnectSignal = Signals._disconnect;
537 : :
538 : 19 : Gio.DBusProxy.makeProxyWrapper = _makeProxyWrapper;
539 : :
540 : : // Some helpers
541 : 19 : _wrapFunction(Gio.DBusNodeInfo, 'new_for_xml', _newNodeInfo);
542 : 19 : Gio.DBusInterfaceInfo.new_for_xml = _newInterfaceInfo;
543 : :
544 : 19 : Gio.DBusExportedObject = GjsPrivate.DBusImplementation;
545 : 19 : Gio.DBusExportedObject.wrapJSObject = _wrapJSObject;
546 : :
547 : : // ListStore
548 : 19 : Gio.ListStore.prototype[Symbol.iterator] = _listModelIterator;
549 : 19 : Gio.ListStore.prototype.insert_sorted = function (item, compareFunc) {
550 : 10 : return GjsPrivate.list_store_insert_sorted(this, item, compareFunc);
551 : : };
552 : 19 : Gio.ListStore.prototype.sort = function (compareFunc) {
553 : 1 : return GjsPrivate.list_store_sort(this, compareFunc);
554 : : };
555 : :
556 : : // Promisify
557 : 19 : Gio._promisify = _promisify;
558 : :
559 : : // Temporary Gio.File.prototype fix
560 : 19 : Gio._LocalFilePrototype = Gio.File.new_for_path('/').constructor.prototype;
561 : :
562 : 19 : Gio.File.prototype.replace_contents_async = function replace_contents_async(contents, etag, make_backup, flags, cancellable, callback) {
563 : 0 : return this.replace_contents_bytes_async(contents, etag, make_backup, flags, cancellable, callback);
564 : : };
565 : :
566 : : // Best-effort attempt to replace set_attribute(), which is not
567 : : // introspectable due to the pointer argument
568 : 19 : Gio.File.prototype.set_attribute = function set_attribute(attribute, type, value, flags, cancellable) {
569 : 5 : _warnNotIntrospectable('Gio.File.prototype.set_attribute', 'set_attribute_{type}');
570 : :
571 : 5 : switch (type) {
572 [ - + ]: 5 : case Gio.FileAttributeType.STRING:
573 : 0 : return this.set_attribute_string(attribute, value, flags, cancellable);
574 [ - + ]: 5 : case Gio.FileAttributeType.BYTE_STRING:
575 : 0 : return this.set_attribute_byte_string(attribute, value, flags, cancellable);
576 [ + + ]: 5 : case Gio.FileAttributeType.UINT32:
577 : 1 : return this.set_attribute_uint32(attribute, value, flags, cancellable);
578 [ - + ]: 4 : case Gio.FileAttributeType.INT32:
579 : 0 : return this.set_attribute_int32(attribute, value, flags, cancellable);
580 [ + + ]: 4 : case Gio.FileAttributeType.UINT64:
581 : 1 : return this.set_attribute_uint64(attribute, value, flags, cancellable);
582 [ - + ]: 3 : case Gio.FileAttributeType.INT64:
583 : 0 : return this.set_attribute_int64(attribute, value, flags, cancellable);
584 [ + + ]: 3 : case Gio.FileAttributeType.INVALID:
585 [ + + ]: 2 : case Gio.FileAttributeType.BOOLEAN:
586 [ + - ]: 1 : case Gio.FileAttributeType.OBJECT:
587 [ # # ]: 0 : case Gio.FileAttributeType.STRINGV:
588 : 3 : throw _notIntrospectableError('This attribute type', 'Gio.FileInfo');
589 : : }
590 : : };
591 : :
592 : 19 : Gio.FileInfo.prototype.set_attribute = function set_attribute(attribute, type, value) {
593 : 5 : _warnNotIntrospectable('Gio.FileInfo.prototype.set_attribute', 'set_attribute_{type}');
594 : :
595 : 5 : switch (type) {
596 [ + + ]: 5 : case Gio.FileAttributeType.INVALID:
597 : 1 : return this.remove_attribute(attribute);
598 [ - + ]: 4 : case Gio.FileAttributeType.STRING:
599 : 0 : return this.set_attribute_string(attribute, value);
600 [ - + ]: 4 : case Gio.FileAttributeType.BYTE_STRING:
601 : 0 : return this.set_attribute_byte_string(attribute, value);
602 [ + + ]: 4 : case Gio.FileAttributeType.BOOLEAN:
603 : 1 : return this.set_attribute_boolean(attribute, value);
604 [ + + ]: 3 : case Gio.FileAttributeType.UINT32:
605 : 1 : return this.set_attribute_uint32(attribute, value);
606 [ - + ]: 2 : case Gio.FileAttributeType.INT32:
607 : 0 : return this.set_attribute_int32(attribute, value);
608 [ + + ]: 2 : case Gio.FileAttributeType.UINT64:
609 : 1 : return this.set_attribute_uint64(attribute, value);
610 [ - + ]: 1 : case Gio.FileAttributeType.INT64:
611 : 0 : return this.set_attribute_int64(attribute, value);
612 [ + - ]: 1 : case Gio.FileAttributeType.OBJECT:
613 : 1 : return this.set_attribute_object(attribute, value);
614 [ # # ]: 0 : case Gio.FileAttributeType.STRINGV:
615 : 0 : return this.set_attribute_stringv(attribute, value);
616 : : }
617 : : };
618 : :
619 : 20 : Gio.InputStream.prototype.createSyncIterator = function* createSyncIterator(count) {
620 [ - + ]: 9 : while (true) {
621 : 9 : const bytes = this.read_bytes(count, null);
622 [ + + ]: 9 : if (bytes.get_size() === 0)
623 : 1 : return;
624 : 8 : yield bytes;
625 : : }
626 : 1 : };
627 : :
628 [ - + ]: 20 : Gio.InputStream.prototype.createAsyncIterator = async function* createAsyncIterator(
629 : 1 : count, ioPriority = GLib.PRIORITY_DEFAULT) {
630 : 1 : const self = this;
631 : :
632 : : function next() {
633 : 9 : return new Promise((resolve, reject) => {
634 : 9 : self.read_bytes_async(count, ioPriority, null, (_self, res) => {
635 : 9 : try {
636 : 9 : const bytes = self.read_bytes_finish(res);
637 : 9 : resolve(bytes);
638 : 0 : } catch (err) {
639 : 0 : reject(err);
640 : : }
641 : : });
642 : : });
643 : : }
644 : :
645 [ - + ]: 9 : while (true) {
646 : : // eslint-disable-next-line no-await-in-loop
647 [ - + ]: 9 : const bytes = await next(count);
648 [ + + ]: 9 : if (bytes.get_size() === 0)
649 : 1 : return;
650 [ - + ]: 8 : yield bytes;
651 : : }
652 : 1 : };
653 : :
654 : 20 : Gio.FileEnumerator.prototype[Symbol.iterator] = function* FileEnumeratorIterator() {
655 [ + + ]: 36 : while (true) {
656 : 35 : try {
657 : 35 : const info = this.next_file(null);
658 [ + + ]: 35 : if (info === null)
659 : 1 : break;
660 : 34 : yield info;
661 : 0 : } catch (err) {
662 : 0 : this.close(null);
663 : 0 : throw err;
664 : : }
665 : : }
666 : 1 : this.close(null);
667 : 1 : };
668 : :
669 : 20 : Gio.FileEnumerator.prototype[Symbol.asyncIterator] = async function* AsyncFileEnumeratorIterator() {
670 : 1 : const self = this;
671 : :
672 : 44 : function next() {
673 : 35 : return new Promise((resolve, reject) => {
674 : 35 : self.next_files_async(1, GLib.PRIORITY_DEFAULT, null, (_self, res) => {
675 : 35 : try {
676 : 35 : const files = self.next_files_finish(res);
677 [ + + ]: 35 : resolve(files.length === 0 ? null : files[0]);
678 : 0 : } catch (err) {
679 : 0 : reject(err);
680 : : }
681 : : });
682 : : });
683 : : }
684 : :
685 : 1 : function close() {
686 : 1 : return new Promise((resolve, reject) => {
687 : 1 : self.close_async(GLib.PRIORITY_DEFAULT, null, (_self, res) => {
688 : 1 : try {
689 : 1 : resolve(self.close_finish(res));
690 : 0 : } catch (err) {
691 : 0 : reject(err);
692 : : }
693 : : });
694 : : });
695 : : }
696 : :
697 [ + + ]: 36 : while (true) {
698 : 35 : try {
699 : : // eslint-disable-next-line no-await-in-loop
700 [ - + ]: 35 : const info = await next();
701 [ + + ]: 35 : if (info === null)
702 : 1 : break;
703 [ - + ]: 34 : yield info;
704 : 0 : } catch (err) {
705 : : // eslint-disable-next-line no-await-in-loop
706 [ # # ]: 0 : await close();
707 : 0 : throw err;
708 : : }
709 : : }
710 : :
711 [ - + ]: 1 : return close();
712 : 1 : };
713 : :
714 : : // Override Gio.Settings and Gio.SettingsSchema - the C API asserts if
715 : : // trying to access a nonexistent schema or key, which is not handy for
716 : : // shell-extension writers
717 : :
718 : 19 : Gio.SettingsSchema.prototype._realGetKey = Gio.SettingsSchema.prototype.get_key;
719 : 19 : Gio.SettingsSchema.prototype.get_key = function (key) {
720 [ + + ]: 2 : if (!this.has_key(key))
721 : 1 : throw new Error(`GSettings key ${key} not found in schema ${this.get_id()}`);
722 : 1 : return this._realGetKey(key);
723 : : };
724 : :
725 : 19 : Gio.Settings.prototype._realMethods = Object.assign({}, Gio.Settings.prototype);
726 : :
727 [ + + ]: 570 : function createCheckedMethod(method, checkMethod = '_checkKey') {
728 : 627 : return function (id, ...args) {
729 : 57 : this[checkMethod](id);
730 [ + + ]: 27 : return this._realMethods[method].call(this, id, ...args);
731 : : };
732 : : }
733 : :
734 : 38 : Object.assign(Gio.Settings.prototype, {
735 : 19 : _realInit: Gio.Settings.prototype._init, // add manually, not enumerable
736 [ + + ]: 36 : _init(props = {}) {
737 : : // 'schema' is a deprecated alias for schema_id
738 : 17 : const schemaIdProp = ['schema', 'schema-id', 'schema_id',
739 : 85 : 'schemaId'].find(prop => prop in props);
740 : 17 : const settingsSchemaProp = ['settings-schema', 'settings_schema',
741 : 68 : 'settingsSchema'].find(prop => prop in props);
742 [ + + ][ + + ]: 17 : if (!schemaIdProp && !settingsSchemaProp) {
743 : 1 : throw new Error('One of property \'schema-id\' or ' +
744 : : '\'settings-schema\' are required for Gio.Settings');
745 : : }
746 [ + + ][ + - ]: 16 : if (settingsSchemaProp && !(props[settingsSchemaProp] instanceof Gio.SettingsSchema))
747 : 0 : throw new Error(`Value of property '${settingsSchemaProp}' is not of type Gio.SettingsSchema`);
748 : :
749 : 16 : const source = Gio.SettingsSchemaSource.get_default();
750 [ + + ]: 16 : const settingsSchema = settingsSchemaProp
751 : 1 : ? props[settingsSchemaProp]
752 : 15 : : source.lookup(props[schemaIdProp], true);
753 : :
754 [ + + ]: 16 : if (!settingsSchema)
755 : 1 : throw new Error(`GSettings schema ${props[schemaIdProp]} not found`);
756 : :
757 : 15 : const settingsSchemaPath = settingsSchema.get_path();
758 [ + + ][ + + ]: 15 : if (props['path'] === undefined && !settingsSchemaPath) {
759 : 2 : throw new Error('Attempting to create schema ' +
760 : 1 : `'${settingsSchema.get_id()}' without a path`);
761 : : }
762 : :
763 [ + + ][ - + ]: 14 : if (props['path'] !== undefined && settingsSchemaPath &&
764 [ + + ]: 1 : props['path'] !== settingsSchemaPath) {
765 : 2 : throw new Error(`GSettings created for path '${props['path']}'` +
766 : 1 : `, but schema specifies '${settingsSchemaPath}'`);
767 : : }
768 : :
769 : 13 : return this._realInit(props);
770 : 13 : },
771 : :
772 : 19 : _checkKey(key) {
773 : : // Avoid using has_key(); checking a JS array is faster than calling
774 : : // through G-I.
775 [ + + ]: 55 : if (!this._keys)
776 : 11 : this._keys = this.settings_schema.list_keys();
777 : :
778 [ + + ]: 55 : if (!this._keys.includes(key))
779 : 29 : throw new Error(`GSettings key ${key} not found in schema ${this.schema_id}`);
780 : : },
781 : :
782 : 19 : _checkChild(name) {
783 [ - + ]: 2 : if (!this._children)
784 : 2 : this._children = this.list_children();
785 : :
786 [ + + ]: 2 : if (!this._children.includes(name))
787 : 1 : throw new Error(`Child ${name} not found in GSettings schema ${this.schema_id}`);
788 : : },
789 : :
790 : 19 : get_boolean: createCheckedMethod('get_boolean'),
791 : 19 : set_boolean: createCheckedMethod('set_boolean'),
792 : 19 : get_double: createCheckedMethod('get_double'),
793 : 19 : set_double: createCheckedMethod('set_double'),
794 : 19 : get_enum: createCheckedMethod('get_enum'),
795 : 19 : set_enum: createCheckedMethod('set_enum'),
796 : 19 : get_flags: createCheckedMethod('get_flags'),
797 : 19 : set_flags: createCheckedMethod('set_flags'),
798 : 19 : get_int: createCheckedMethod('get_int'),
799 : 19 : set_int: createCheckedMethod('set_int'),
800 : 19 : get_int64: createCheckedMethod('get_int64'),
801 : 19 : set_int64: createCheckedMethod('set_int64'),
802 : 19 : get_string: createCheckedMethod('get_string'),
803 : 19 : set_string: createCheckedMethod('set_string'),
804 : 19 : get_strv: createCheckedMethod('get_strv'),
805 : 19 : set_strv: createCheckedMethod('set_strv'),
806 : 19 : get_uint: createCheckedMethod('get_uint'),
807 : 19 : set_uint: createCheckedMethod('set_uint'),
808 : 19 : get_uint64: createCheckedMethod('get_uint64'),
809 : 19 : set_uint64: createCheckedMethod('set_uint64'),
810 : 19 : get_value: createCheckedMethod('get_value'),
811 : 19 : set_value: createCheckedMethod('set_value'),
812 : :
813 : 19 : bind: createCheckedMethod('bind'),
814 : 19 : bind_writable: createCheckedMethod('bind_writable'),
815 : 19 : create_action: createCheckedMethod('create_action'),
816 : 19 : get_default_value: createCheckedMethod('get_default_value'),
817 : 19 : get_user_value: createCheckedMethod('get_user_value'),
818 : 19 : is_writable: createCheckedMethod('is_writable'),
819 : 19 : reset: createCheckedMethod('reset'),
820 : :
821 : 19 : get_child: createCheckedMethod('get_child', '_checkChild'),
822 : : });
823 : :
824 : : // ActionMap
825 : : // add_action_entries is not introspectable
826 : : // https://gitlab.gnome.org/GNOME/gjs/-/issues/407
827 : 19 : Gio.ActionMap.prototype.add_action_entries = function add_action_entries(entries) {
828 [ + + ]: 9 : for (const {name, activate, parameter_type, state, change_state} of entries) {
829 [ + + ][ + + ]: 6 : if (typeof parameter_type === 'string' && !GLib.variant_type_string_is_valid(parameter_type))
830 : 1 : throw new Error(`parameter_type "${parameter_type}" is not a valid VariantType`);
831 : :
832 : 9 : const action = new Gio.SimpleAction({
833 : 5 : name,
834 [ + + ]: 5 : parameter_type: typeof parameter_type === 'string' ? new GLib.VariantType(parameter_type) : null,
835 [ + + ]: 5 : state: typeof state === 'string' ? GLib.Variant.parse(null, state, null, null) : null,
836 : : });
837 : :
838 [ + + ]: 4 : if (typeof activate === 'function')
839 : 1 : action.connect('activate', activate.bind(action));
840 : :
841 [ + + ]: 4 : if (typeof change_state === 'function')
842 : 1 : action.connect('change-state', change_state.bind(action));
843 : :
844 : 4 : this.add_action(action);
845 : : }
846 : : };
847 : : }
|