From 91b7474d5a7bc711efdbb68c29ad2ae7ef3a8d68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Wed, 4 Mar 2020 04:02:28 +0100 Subject: [PATCH] dbusServices/extensions: Proxy Extensions API Similar to the previously added org.freedesktop.Notifications proxy, this exposes the org.gnome.Shell.Extensions API and forwards any request to the real implementation in gnome-shell. The motivation differs though: We want to be able to package the extension app as flatpak and distribute it separately, but the extension prefs dialog is hard to impossible to sandbox: - filenames need translating between host and sandbox, and we can only do that in some cases (serializing/deserializing extensions), but not others (extension settings that refer to files) - system extensions install their GSettings schemas in the system path; the best we can do there is assume a host prefix of /usr and set GSETTINGS_SCHEMA_DIR in the flatpak (eeks) - extensions may rely on additional typelibs that are present on the host (for example because gnome-shell itself depends on them), but not inside the sandbox - unless we bundle all of gnome-shell's dependencies - if gjs/mozjs differ between host and sandbox, extensions must handle different runtimes for the extension and its prefs And all those issues occur despite a very permissive sandbox (full host filesystem access, full dconf access, full org.gnome.Shell access (including Eval()!)). This new service will give us an alternative place for handling the preference dialog: - it runs outside of gnome-shell process, so can open windows - it runs on the host, so the extension's prefs get to run in the same namespace as the extension itself That is, the service will provide portal-like functionality (albeit not using the org.freedesktop.portal.* namespace, as extension management is an inherently privileged operation). https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1106 --- .../extensions/extensionsService.js | 127 ++++++++++++++++++ js/dbusServices/extensions/main.js | 11 ++ js/dbusServices/meson.build | 1 + ...g.gnome.Shell.Extensions.src.gresource.xml | 11 ++ 4 files changed, 150 insertions(+) create mode 100644 js/dbusServices/extensions/extensionsService.js create mode 100644 js/dbusServices/extensions/main.js create mode 100644 js/dbusServices/org.gnome.Shell.Extensions.src.gresource.xml diff --git a/js/dbusServices/extensions/extensionsService.js b/js/dbusServices/extensions/extensionsService.js new file mode 100644 index 000000000..9ea07df45 --- /dev/null +++ b/js/dbusServices/extensions/extensionsService.js @@ -0,0 +1,127 @@ +// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- +/* exported ExtensionsService */ + +const { Gio, GLib } = imports.gi; + +const { loadInterfaceXML } = imports.misc.fileUtils; +const { ServiceImplementation } = imports.dbusService; + +const ExtensionsIface = loadInterfaceXML('org.gnome.Shell.Extensions'); +const ExtensionsProxy = Gio.DBusProxy.makeProxyWrapper(ExtensionsIface); + +var ExtensionsService = class extends ServiceImplementation { + constructor() { + super(ExtensionsIface, '/org/gnome/Shell/Extensions'); + + this._proxy = new ExtensionsProxy(Gio.DBus.session, + 'org.gnome.Shell', '/org/gnome/Shell'); + + this._proxy.connectSignal('ExtensionStateChanged', + (proxy, sender, params) => { + this._dbusImpl.emit_signal('ExtensionStateChanged', + new GLib.Variant('(sa{sv})', params)); + }); + + this._proxy.connect('g-properties-changed', () => { + this._dbusImpl.emit_property_changed('UserExtensionsEnabled', + new GLib.Variant('b', this._proxy.UserExtensionsEnabled)); + }); + } + + get ShellVersion() { + return this._proxy.ShellVersion; + } + + get UserExtensionsEnabled() { + return this._proxy.UserExtensionsEnabled; + } + + set UserExtensionsEnabled(enable) { + this._proxy.UserExtensionsEnabled = enable; + } + + ListExtensionsAsync(params, invocation) { + this._proxy.ListExtensionsRemote(...params, (res, error) => { + if (this._handleError(invocation, error)) + return; + + invocation.return_value(new GLib.Variant('(a{sa{sv}})', res)); + }); + } + + GetExtensionInfoAsync(params, invocation) { + this._proxy.GetExtensionInfoRemote(...params, (res, error) => { + if (this._handleError(invocation, error)) + return; + + invocation.return_value(new GLib.Variant('(a{sv})', res)); + }); + } + + GetExtensionErrorsAsync(params, invocation) { + this._proxy.GetExtensionErrorsRemote(...params, (res, error) => { + if (this._handleError(invocation, error)) + return; + + invocation.return_value(new GLib.Variant('(as)', res)); + }); + } + + InstallRemoteExtensionAsync(params, invocation) { + this._proxy.InstallRemoteExtensionRemote(...params, (res, error) => { + if (this._handleError(invocation, error)) + return; + + invocation.return_value(new GLib.Variant('(s)', res)); + }); + } + + UninstallExtensionAsync(params, invocation) { + this._proxy.UninstallExtensionRemote(...params, (res, error) => { + if (this._handleError(invocation, error)) + return; + + invocation.return_value(new GLib.Variant('(b)', res)); + }); + } + + EnableExtensionAsync(params, invocation) { + this._proxy.EnableExtensionRemote(...params, (res, error) => { + if (this._handleError(invocation, error)) + return; + + invocation.return_value(new GLib.Variant('(b)', res)); + }); + } + + DisableExtensionAsync(params, invocation) { + this._proxy.DisableExtensionRemote(...params, (res, error) => { + if (this._handleError(invocation, error)) + return; + + invocation.return_value(new GLib.Variant('(b)', res)); + }); + } + + LaunchExtensionPrefsAsync([uuid], invocation) { + this.OpenExtensionPrefsAsync([uuid, '', {}], invocation); + } + + OpenExtensionPrefsAsync(params, invocation) { + this._proxy.OpenExtensionPrefsRemote(...params, (res, error) => { + if (this._handleError(invocation, error)) + return; + + invocation.return_value(null); + }); + } + + CheckForUpdatesAsync(params, invocation) { + this._proxy.CheckForUpdatesRemote(...params, (res, error) => { + if (this._handleError(invocation, error)) + return; + + invocation.return_value(null); + }); + } +}; diff --git a/js/dbusServices/extensions/main.js b/js/dbusServices/extensions/main.js new file mode 100644 index 000000000..8f139a64f --- /dev/null +++ b/js/dbusServices/extensions/main.js @@ -0,0 +1,11 @@ +/* exported main */ + +const { DBusService } = imports.dbusService; +const { ExtensionsService } = imports.extensionsService; + +function main() { + const service = new DBusService( + 'org.gnome.Shell.Extensions', + new ExtensionsService()); + service.run(); +} diff --git a/js/dbusServices/meson.build b/js/dbusServices/meson.build index 2cca6544f..c749f45dc 100644 --- a/js/dbusServices/meson.build +++ b/js/dbusServices/meson.build @@ -4,6 +4,7 @@ launcherconf.set('prefix', prefix) launcherconf.set('libdir', libdir) dbus_services = { + 'org.gnome.Shell.Extensions': 'extensions', 'org.gnome.Shell.Notifications': 'notifications', } diff --git a/js/dbusServices/org.gnome.Shell.Extensions.src.gresource.xml b/js/dbusServices/org.gnome.Shell.Extensions.src.gresource.xml new file mode 100644 index 000000000..7bbcdab11 --- /dev/null +++ b/js/dbusServices/org.gnome.Shell.Extensions.src.gresource.xml @@ -0,0 +1,11 @@ + + + + main.js + extensionsService.js + dbusService.js + + misc/config.js + misc/fileUtils.js + +