extensionDownloader: Add update/blacklist support for extensions

This is a bare-bones copy/replace. It does not implement ChangeLog
support. If we cannot get System Updates integration, I will implement
notification support.

https://bugzilla.gnome.org/show_bug.cgi?id=679099
This commit is contained in:
Jasper St. Pierre 2012-06-26 20:47:44 -04:00
parent 539993b4f4
commit 1e286e43ad
3 changed files with 112 additions and 8 deletions

View File

@ -28,7 +28,7 @@ function deleteGFile(file) {
return file['delete'](null); return file['delete'](null);
} }
function recursivelyDeleteDir(dir) { function recursivelyDeleteDir(dir, deleteParent) {
let children = dir.enumerate_children('standard::name,standard::type', let children = dir.enumerate_children('standard::name,standard::type',
Gio.FileQueryInfoFlags.NONE, null); Gio.FileQueryInfoFlags.NONE, null);
@ -39,8 +39,29 @@ function recursivelyDeleteDir(dir) {
if (type == Gio.FileType.REGULAR) if (type == Gio.FileType.REGULAR)
deleteGFile(child); deleteGFile(child);
else if (type == Gio.FileType.DIRECTORY) else if (type == Gio.FileType.DIRECTORY)
recursivelyDeleteDir(child); recursivelyDeleteDir(child, true);
} }
deleteGFile(dir); if (deleteParent)
deleteGFile(dir);
}
function recursivelyMoveDir(srcDir, destDir) {
let children = srcDir.enumerate_children('standard::name,standard::type',
Gio.FileQueryInfoFlags.NONE, null);
if (!destDir.query_exists(null))
destDir.make_directory_with_parents(null);
let info, child;
while ((info = children.next_file(null)) != null) {
let type = info.get_file_type();
let srcChild = srcDir.get_child(info.get_name());
let destChild = destDir.get_child(info.get_name());
log([srcChild.get_path(), destChild.get_path()]);
if (type == Gio.FileType.REGULAR)
srcChild.move(destChild, Gio.FileCopyFlags.NONE, null, null);
else if (type == Gio.FileType.DIRECTORY)
recursivelyMoveDir(srcChild, destChild);
}
} }

View File

@ -7,6 +7,7 @@ const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const Soup = imports.gi.Soup; const Soup = imports.gi.Soup;
const St = imports.gi.St; const St = imports.gi.St;
const Shell = imports.gi.Shell;
const Config = imports.misc.config; const Config = imports.misc.config;
const ExtensionUtils = imports.misc.extensionUtils; const ExtensionUtils = imports.misc.extensionUtils;
@ -18,7 +19,8 @@ const _signals = ExtensionSystem._signals;
const REPOSITORY_URL_BASE = 'https://extensions.gnome.org'; const REPOSITORY_URL_BASE = 'https://extensions.gnome.org';
const REPOSITORY_URL_DOWNLOAD = REPOSITORY_URL_BASE + '/download-extension/%s.shell-extension.zip'; const REPOSITORY_URL_DOWNLOAD = REPOSITORY_URL_BASE + '/download-extension/%s.shell-extension.zip';
const REPOSITORY_URL_INFO = REPOSITORY_URL_BASE + '/extension-info/'; const REPOSITORY_URL_INFO = REPOSITORY_URL_BASE + '/extension-info/';
const REPOSITORY_URL_UPDATE = REPOSITORY_URL_BASE + '/update-info/';
let _httpSession; let _httpSession;
@ -61,17 +63,16 @@ function uninstallExtension(uuid) {
if (!ExtensionSystem.unloadExtension(uuid)) if (!ExtensionSystem.unloadExtension(uuid))
return false; return false;
FileUtils.recursivelyDeleteDir(extension.dir); FileUtils.recursivelyDeleteDir(extension.dir, true);
return true; return true;
} }
function gotExtensionZipFile(session, message, uuid, callback, errback) { function gotExtensionZipFile(session, message, uuid, dir, callback, errback) {
if (message.status_code != Soup.KnownStatusCode.OK) { if (message.status_code != Soup.KnownStatusCode.OK) {
errback('DownloadExtensionError', message.status_code); errback('DownloadExtensionError', message.status_code);
return; return;
} }
let dir = Gio.File.new_for_path(GLib.build_filenamev([global.userdatadir, 'extensions', uuid]));
try { try {
if (!dir.query_exists(null)) if (!dir.query_exists(null))
dir.make_directory_with_parents(null); dir.make_directory_with_parents(null);
@ -105,6 +106,81 @@ function gotExtensionZipFile(session, message, uuid, callback, errback) {
}); });
} }
function updateExtension(uuid) {
// This gets a bit tricky. We want the update to be seamless -
// if we have any error during downloading or extracting, we
// want to not unload the current version.
let oldExtensionTmpDir = GLib.Dir.make_tmp('XXXXXX-shell-extension');
let newExtensionTmpDir = GLib.Dir.make_tmp('XXXXXX-shell-extension');
let params = { shell_version: Config.PACKAGE_VERSION };
let url = REPOSITORY_URL_DOWNLOAD.format(uuid);
let message = Soup.form_request_new_from_hash('GET', url, params);
_httpSession.queue_message(message, Lang.bind(this, function(session, message) {
gotExtensionZipFile(session, message, uuid, newExtensionTmpDir, function() {
let oldExtension = ExtensionUtils.extensions[uuid];
let extensionDir = oldExtension.dir;
if (!ExtensionSystem.unloadExtension(uuid))
return;
FileUtils.recursivelyMoveDir(extensionDir, oldExtensionTmpDir);
FileUtils.recursivelyMoveDir(newExtensionTmpDir, extensionDir);
let extension = ExtensionUtils.createExtensionObject(uuid, extensionDir, ExtensionUtils.ExtensionType.PER_USER);
try {
ExtensionSystem.loadExtension(extension);
} catch(e) {
ExtensionSystem.unloadExtension(uuid);
logError(e, 'Error loading extension %s'.format(uuid));
FileUtils.recursivelyDeleteDir(extensionDir, false);
FileUtils.recursivelyMoveDir(oldExtensionTmpDir, extensionDir);
// Restore what was there before. We can't do much if we
// fail here.
ExtensionSystem.loadExtension(oldExtension);
return;
}
FileUtils.recursivelyDeleteDir(oldExtensionTmpDir, true);
}, function(code, message) {
log('Error while updating extension %s: %s (%s)'.format(uuid, code, message ? message : ''));
});
}));
}
function checkForUpdates() {
let metadatas = {};
for (let uuid in ExtensionUtils.extensions) {
metadatas[uuid] = ExtensionUtils.extensions[uuid].metadata;
}
let params = { shell_version: Config.PACKAGE_VERSION,
installed: JSON.stringify(metadatas) };
let url = REPOSITORY_URL_UPDATE;
let message = Soup.form_request_new_from_hash('GET', url, params);
_httpSession.queue_message(message, function(session, message) {
if (message.status_code != Soup.KnownStatusCode.OK)
return;
let operations = JSON.parse(message.response_body.data);
for (let uuid in operations) {
let operation = operations[uuid];
if (operation == 'blacklist')
uninstallExtension(uuid);
else if (operation == 'upgrade' || operation == 'downgrade')
updateExtension(uuid);
}
});
}
const InstallExtensionDialog = new Lang.Class({ const InstallExtensionDialog = new Lang.Class({
Name: 'InstallExtensionDialog', Name: 'InstallExtensionDialog',
Extends: ModalDialog.ModalDialog, Extends: ModalDialog.ModalDialog,
@ -149,6 +225,7 @@ const InstallExtensionDialog = new Lang.Class({
let message = Soup.form_request_new_from_hash('GET', url, params); let message = Soup.form_request_new_from_hash('GET', url, params);
let uuid = this._uuid; let uuid = this._uuid;
let dir = Gio.File.new_for_path(GLib.build_filenamev([global.userdatadir, 'extensions', uuid]));
let invocation = this._invocation; let invocation = this._invocation;
function errback(code, message) { function errback(code, message) {
invocation.return_dbus_error('org.gnome.Shell.' + code, message ? message.toString() : ''); invocation.return_dbus_error('org.gnome.Shell.' + code, message ? message.toString() : '');
@ -176,7 +253,7 @@ const InstallExtensionDialog = new Lang.Class({
} }
_httpSession.queue_message(message, Lang.bind(this, function(session, message) { _httpSession.queue_message(message, Lang.bind(this, function(session, message) {
gotExtensionZipFile(session, message, uuid, callback, errback); gotExtensionZipFile(session, message, uuid, dir, callback, errback);
})); }));
this.close(global.get_current_time()); this.close(global.get_current_time());

View File

@ -214,6 +214,8 @@ const GnomeShellExtensionsIface = <interface name="org.gnome.Shell.Extensions">
<method name="ReloadExtension"> <method name="ReloadExtension">
<arg type="s" direction="in" name="uuid"/> <arg type="s" direction="in" name="uuid"/>
</method> </method>
<method name="CheckForUpdates">
</method>
<property name="ShellVersion" type="s" access="read" /> <property name="ShellVersion" type="s" access="read" />
</interface>; </interface>;
@ -306,6 +308,10 @@ const GnomeShellExtensions = new Lang.Class({
ExtensionSystem.loadExtension(uuid); ExtensionSystem.loadExtension(uuid);
}, },
CheckForUpdates: function() {
ExtensionDownloader.checkForUpdates();
},
ShellVersion: Config.PACKAGE_VERSION, ShellVersion: Config.PACKAGE_VERSION,
_extensionStateChanged: function(_, newState) { _extensionStateChanged: function(_, newState) {