citadel-realms/js/model/ObjectManager.js
2024-11-12 17:26:26 -05:00

184 lines
7.0 KiB
JavaScript

import Gio from 'gi://Gio';
import GLib from 'gi://GLib';
const Signals = imports.signals;
Gio._promisify(Gio.DBusProxy.prototype, 'init_async', 'init_finish');
// Specified in the D-Bus specification here:
// http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager
const ObjectManagerIface = `
<node>
<interface name="org.freedesktop.DBus.ObjectManager">
<method name="GetManagedObjects">
<arg name="objects" type="a{oa{sa{sv}}}" direction="out"/>
</method>
<signal name="InterfacesAdded">
<arg name="objectPath" type="o"/>
<arg name="interfaces" type="a{sa{sv}}" />
</signal>
<signal name="InterfacesRemoved">
<arg name="objectPath" type="o"/>
<arg name="interfaces" type="as" />
</signal>
</interface>
</node>`;
const ObjectManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(ObjectManagerIface);
;
export class ObjectManager {
constructor(params) {
var _a;
this._connection = params.connection;
this._serviceName = params.name;
this._managerPath = params.objectPath;
this._cancellable = (_a = params.cancellable) !== null && _a !== void 0 ? _a : null;
this._managerProxy = new Gio.DBusProxy({
g_connection: this._connection,
g_interface_name: ObjectManagerInfo.name,
g_interface_info: ObjectManagerInfo,
g_name: this._serviceName,
g_object_path: this._managerPath,
g_flags: Gio.DBusProxyFlags.DO_NOT_AUTO_START,
});
this._interfaceInfos = {};
this._objects = {};
this._interfaces = {};
this._onLoaded = params.onLoaded;
if (params.knownInterfaces)
this._registerInterfaces(params.knownInterfaces);
this._initManagerProxy();
}
_completeLoad() {
if (this._onLoaded)
this._onLoaded();
}
async _addInterface(objectPath, interfaceName) {
let info = this._interfaceInfos[interfaceName];
if (!info)
return;
const proxy = new Gio.DBusProxy({
g_connection: this._connection,
g_name: this._serviceName,
g_object_path: objectPath,
g_interface_name: interfaceName,
g_interface_info: info,
g_flags: Gio.DBusProxyFlags.DO_NOT_AUTO_START,
});
try {
await proxy.init_async(GLib.PRIORITY_DEFAULT, this._cancellable);
}
catch (e) {
logError(e, `could not initialize proxy for interface ${interfaceName}`);
return;
}
let isNewObject;
if (!this._objects[objectPath]) {
this._objects[objectPath] = {};
isNewObject = true;
}
else {
isNewObject = false;
}
this._objects[objectPath][interfaceName] = proxy;
if (!this._interfaces[interfaceName])
this._interfaces[interfaceName] = [];
this._interfaces[interfaceName].push(proxy);
if (isNewObject)
this.emit('object-added', objectPath);
this.emit('interface-added', interfaceName, proxy);
}
_removeInterface(objectPath, interfaceName) {
if (!this._objects[objectPath])
return;
let proxy = this._objects[objectPath][interfaceName];
if (this._interfaces[interfaceName]) {
let index = this._interfaces[interfaceName].indexOf(proxy);
if (index >= 0)
this._interfaces[interfaceName].splice(index, 1);
if (this._interfaces[interfaceName].length === 0)
delete this._interfaces[interfaceName];
}
this.emit('interface-removed', interfaceName, proxy);
delete this._objects[objectPath][interfaceName];
if (Object.keys(this._objects[objectPath]).length === 0) {
delete this._objects[objectPath];
this.emit('object-removed', objectPath);
}
}
async _initManagerProxy() {
try {
await this._managerProxy.init_async(GLib.PRIORITY_DEFAULT, this._cancellable);
}
catch (e) {
logError(e, `could not initialize object manager for object ${this._serviceName}`);
this._completeLoad();
return;
}
this._managerProxy.connectSignal('InterfacesAdded', (_objectManager, _sender, [objectPath, interfaces]) => {
let interfaceNames = Object.keys(interfaces);
for (let i = 0; i < interfaceNames.length; i++)
this._addInterface(objectPath, interfaceNames[i]);
});
this._managerProxy.connectSignal('InterfacesRemoved', (_objectManager, _sender, [objectPath, interfaceNames]) => {
for (let i = 0; i < interfaceNames.length; i++)
this._removeInterface(objectPath, interfaceNames[i]);
});
if (Object.keys(this._interfaceInfos).length === 0) {
this._completeLoad();
return;
}
this._managerProxy.connect('notify::g-name-owner', () => {
if (this._managerProxy.g_name_owner)
this._onNameAppeared();
else
this._onNameVanished();
});
if (this._managerProxy.g_name_owner)
this._onNameAppeared().catch(logError);
}
async _onNameAppeared() {
try {
const [objects] = await this._managerProxy.GetManagedObjectsAsync();
if (!objects) {
this._completeLoad();
return;
}
const objectPaths = Object.keys(objects);
await Promise.allSettled(objectPaths.flatMap(objectPath => {
const object = objects[objectPath];
const interfaceNames = Object.getOwnPropertyNames(object);
return interfaceNames.map(ifaceName => this._addInterface(objectPath, ifaceName));
}));
}
catch (error) {
logError(error, `could not get remote objects for service ${this._serviceName} path ${this._managerPath}`);
}
finally {
this._completeLoad();
}
}
_onNameVanished() {
let objectPaths = Object.keys(this._objects);
for (let i = 0; i < objectPaths.length; i++) {
let objectPath = objectPaths[i];
let object = this._objects[objectPath];
let interfaceNames = Object.keys(object);
for (let j = 0; j < interfaceNames.length; j++) {
let interfaceName = interfaceNames[j];
if (object[interfaceName])
this._removeInterface(objectPath, interfaceName);
}
}
}
_registerInterfaces(interfaces) {
for (let i = 0; i < interfaces.length; i++) {
let info = Gio.DBusInterfaceInfo.new_for_xml(interfaces[i]);
this._interfaceInfos[info.name] = info;
}
}
getProxiesForInterface(interfaceName) {
let proxyList = this._interfaces[interfaceName];
if (!proxyList)
return [];
return proxyList;
}
}
Signals.addSignalMethods(ObjectManager.prototype);