extensionSystem: Add install-from-HTTP capability
This adds a new DBus method: InstallExtensionRemote(uuid : s, url : s) Pass it the UUID of an extension and the URL of a manifest file: the same as a metadata.json, but with a special key, '__installer', which is an HTTP location that points to an zip file containing the extension. The Shell will download and use it to install the extension. In the future, the manifest file may be used to automatically detect and install updates. https://bugzilla.gnome.org/show_bug.cgi?id=654770
This commit is contained in:
parent
2d813cbdd8
commit
d8a98e5467
@ -7,6 +7,7 @@ const GLib = imports.gi.GLib;
|
|||||||
const Gio = imports.gi.Gio;
|
const Gio = imports.gi.Gio;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
|
const Soup = imports.gi.Soup;
|
||||||
|
|
||||||
const Config = imports.misc.config;
|
const Config = imports.misc.config;
|
||||||
|
|
||||||
@ -26,6 +27,15 @@ const ExtensionType = {
|
|||||||
PER_USER: 2
|
PER_USER: 2
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const _httpSession = new Soup.SessionAsync();
|
||||||
|
|
||||||
|
// The unfortunate state of gjs, gobject-introspection and libsoup
|
||||||
|
// means that I have to do a hack to add a feature.
|
||||||
|
// See: https://bugzilla.gnome.org/show_bug.cgi?id=655189 for context.
|
||||||
|
|
||||||
|
if (Soup.Session.prototype.add_feature != null)
|
||||||
|
Soup.Session.prototype.add_feature.call(_httpSession, new Soup.ProxyResolverDefault());
|
||||||
|
|
||||||
// Maps uuid -> metadata object
|
// Maps uuid -> metadata object
|
||||||
const extensionMeta = {};
|
const extensionMeta = {};
|
||||||
// Maps uuid -> importer object (extension directory tree)
|
// Maps uuid -> importer object (extension directory tree)
|
||||||
@ -81,6 +91,79 @@ function versionCheck(required, current) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function installExtensionFromManifestURL(uuid, url) {
|
||||||
|
_httpSession.queue_message(
|
||||||
|
Soup.Message.new('GET', url),
|
||||||
|
function(session, message) {
|
||||||
|
if (message.status_code != Soup.KnownStatusCode.OK) {
|
||||||
|
logExtensionError(uuid, 'downloading manifest: ' + message.status_code.toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let manifest;
|
||||||
|
try {
|
||||||
|
manifest = JSON.parse(message.response_body.data);
|
||||||
|
} catch (e) {
|
||||||
|
logExtensionError(uuid, 'parsing: ' + e.toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uuid != manifest['uuid']) {
|
||||||
|
logExtensionError(uuid, 'manifest: manifest uuids do not match');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
installExtensionFromManifest(manifest, meta);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function installExtensionFromManifest(manifest, meta) {
|
||||||
|
let uuid = manifest['uuid'];
|
||||||
|
let name = manifest['name'];
|
||||||
|
|
||||||
|
let url = manifest['__installer'];
|
||||||
|
_httpSession.queue_message(Soup.Message.new('GET', url),
|
||||||
|
function(session, message) {
|
||||||
|
gotExtensionZipFile(session, message, uuid);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function gotExtensionZipFile(session, message, uuid) {
|
||||||
|
if (message.status_code != Soup.KnownStatusCode.OK) {
|
||||||
|
logExtensionError(uuid, 'downloading extension: ' + message.status_code);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: use a GFile mkstemp-type method once one exists
|
||||||
|
let fd, tmpzip;
|
||||||
|
try {
|
||||||
|
[fd, tmpzip] = GLib.file_open_tmp('XXXXXX.shell-extension.zip');
|
||||||
|
} catch (e) {
|
||||||
|
logExtensionError(uuid, 'tempfile: ' + e.toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let stream = new Gio.UnixOutputStream({ fd: fd });
|
||||||
|
let dir = userExtensionsDir.get_child(uuid);
|
||||||
|
Shell.write_soup_message_to_stream(stream, message);
|
||||||
|
stream.close(null);
|
||||||
|
let [success, pid] = GLib.spawn_async(null,
|
||||||
|
['unzip', '-uod', dir.get_path(), '--', tmpzip],
|
||||||
|
null,
|
||||||
|
GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD,
|
||||||
|
null);
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
logExtensionError(uuid, 'extract: could not extract');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function(pid, status) {
|
||||||
|
GLib.spawn_close_pid(pid);
|
||||||
|
loadExtension(dir, true, ExtensionType.PER_USER);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function disableExtension(uuid) {
|
function disableExtension(uuid) {
|
||||||
let meta = extensionMeta[uuid];
|
let meta = extensionMeta[uuid];
|
||||||
if (!meta)
|
if (!meta)
|
||||||
|
@ -43,6 +43,10 @@ const GnomeShellIface = {
|
|||||||
{ name: 'DisableExtension',
|
{ name: 'DisableExtension',
|
||||||
inSignature: 's',
|
inSignature: 's',
|
||||||
outSignature: ''
|
outSignature: ''
|
||||||
|
},
|
||||||
|
{ name: 'InstallRemoteExtension',
|
||||||
|
inSignature: 's',
|
||||||
|
outSignature: ''
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
signals: [],
|
signals: [],
|
||||||
@ -166,6 +170,10 @@ GnomeShell.prototype = {
|
|||||||
global.settings.set_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY, enabledExtensions);
|
global.settings.set_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY, enabledExtensions);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
InstallRemoteExtension: function(uuid, url) {
|
||||||
|
ExtensionSystem.installExtensionFromManifestURL(uuid, url);
|
||||||
|
},
|
||||||
|
|
||||||
get OverviewActive() {
|
get OverviewActive() {
|
||||||
return Main.overview.visible;
|
return Main.overview.visible;
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user