[gnome-shell] Move a lot of miscellaneous code related to extensions into a new module
- From: Jasper St. Pierre <jstpierre src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-shell] Move a lot of miscellaneous code related to extensions into a new module
- Date: Tue, 7 Feb 2012 21:18:47 +0000 (UTC)
commit 80ff6ff797dfc5b0e94893d42c5639463bb5ec17
Author: Jasper St. Pierre <jstpierre mecheye net>
Date: Wed Jan 18 19:55:20 2012 -0500
Move a lot of miscellaneous code related to extensions into a new module
ExtensionUtils is a new module that has a lot of miscellaneous things related
to loading extensions and the extension system put into a place that does not
depend on Shell or St.
Note that this will break extensions that have with multiple files by replacing
the old uuid-based importer with an object directly on the meta object.
https://bugzilla.gnome.org/show_bug.cgi?id=668429
js/Makefile.am | 1 +
js/misc/extensionUtils.js | 155 +++++++++++++++++++++++++++++++++++++++++++++
js/ui/extensionSystem.js | 152 +++++---------------------------------------
3 files changed, 174 insertions(+), 134 deletions(-)
---
diff --git a/js/Makefile.am b/js/Makefile.am
index 28d9352..f7ce7cd 100644
--- a/js/Makefile.am
+++ b/js/Makefile.am
@@ -9,6 +9,7 @@ nobase_dist_js_DATA = \
gdm/powerMenu.js \
misc/config.js \
misc/docInfo.js \
+ misc/extensionUtils.js \
misc/fileUtils.js \
misc/format.js \
misc/gnomeSession.js \
diff --git a/js/misc/extensionUtils.js b/js/misc/extensionUtils.js
new file mode 100644
index 0000000..3ff0b22
--- /dev/null
+++ b/js/misc/extensionUtils.js
@@ -0,0 +1,155 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+// Common utils for the extension system and the extension
+// preferences tool
+
+const GLib = imports.gi.GLib;
+const Gio = imports.gi.Gio;
+const ShellJS = imports.gi.ShellJS;
+
+const Config = imports.misc.config;
+
+const ExtensionType = {
+ SYSTEM: 1,
+ PER_USER: 2
+};
+
+// GFile for user extensions
+var userExtensionsDir = null;
+
+/**
+ * versionCheck:
+ * @required: an array of versions we're compatible with
+ * @current: the version we have
+ *
+ * Check if a component is compatible for an extension.
+ * @required is an array, and at least one version must match.
+ * @current must be in the format <major>.<minor>.<point>.<micro>
+ * <micro> is always ignored
+ * <point> is ignored if <minor> is even (so you can target the
+ * whole stable release)
+ * <minor> and <major> must match
+ * Each target version must be at least <major> and <minor>
+ */
+function versionCheck(required, current) {
+ let currentArray = current.split('.');
+ let major = currentArray[0];
+ let minor = currentArray[1];
+ let point = currentArray[2];
+ for (let i = 0; i < required.length; i++) {
+ let requiredArray = required[i].split('.');
+ if (requiredArray[0] == major &&
+ requiredArray[1] == minor &&
+ (requiredArray[2] == point ||
+ (requiredArray[2] == undefined && parseInt(minor) % 2 == 0)))
+ return true;
+ }
+ return false;
+}
+
+function isOutOfDate(meta) {
+ if (!versionCheck(meta['shell-version'], Config.PACKAGE_VERSION))
+ return true;
+
+ if (meta['js-version'] && !versionCheck(meta['js-version'], Config.GJS_VERSION))
+ return true;
+
+ return false;
+}
+
+function loadMetadata(uuid, dir, type) {
+ let info;
+
+ let metadataFile = dir.get_child('metadata.json');
+ if (!metadataFile.query_exists(null)) {
+ throw new Error('Missing metadata.json');
+ }
+
+ let metadataContents, success, tag;
+ try {
+ [success, metadataContents, tag] = metadataFile.load_contents(null);
+ } catch (e) {
+ throw new Error('Failed to load metadata.json: ' + e);
+ }
+ let meta;
+ try {
+ meta = JSON.parse(metadataContents);
+ } catch (e) {
+ throw new Error('Failed to parse metadata.json: ' + e);
+ }
+
+ let requiredProperties = ['uuid', 'name', 'description', 'shell-version'];
+ for (let i = 0; i < requiredProperties.length; i++) {
+ let prop = requiredProperties[i];
+ if (!meta[prop]) {
+ throw new Error('missing "' + prop + '" property in metadata.json');
+ }
+ }
+
+ // Encourage people to add this
+ if (!meta.url) {
+ global.log('Warning: Missing "url" property in metadata.json');
+ }
+
+ if (uuid != meta.uuid) {
+ throw new Error('uuid "' + meta.uuid + '" from metadata.json does not match directory name "' + uuid + '"');
+ }
+
+ meta.type = type;
+ meta.dir = dir;
+ meta.path = dir.get_path();
+ meta.error = '';
+
+ return meta;
+}
+
+var _meta = null;
+
+function installImporter(meta) {
+ _meta = meta;
+ ShellJS.add_extension_importer('imports.misc.extensionUtils._meta', 'importer', meta.path);
+ _meta = null;
+}
+
+function init() {
+ let userExtensionsPath = GLib.build_filenamev([global.userdatadir, 'extensions']);
+ userExtensionsDir = Gio.file_new_for_path(userExtensionsPath);
+ try {
+ if (!userExtensionsDir.query_exists(null))
+ userExtensionsDir.make_directory_with_parents(null);
+ } catch (e) {
+ global.logError('' + e);
+ }
+}
+
+function scanExtensionsInDirectory(callback, dir, type) {
+ let fileEnum;
+ let file, info;
+ try {
+ fileEnum = dir.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null);
+ } catch(e) {
+ global.logError('' + e);
+ return;
+ }
+
+ while ((info = fileEnum.next_file(null)) != null) {
+ let fileType = info.get_file_type();
+ if (fileType != Gio.FileType.DIRECTORY)
+ continue;
+ let uuid = info.get_name();
+ let extensionDir = dir.get_child(uuid);
+ callback(uuid, extensionDir, type);
+ }
+ fileEnum.close(null);
+}
+
+function scanExtensions(callback) {
+ let systemDataDirs = GLib.get_system_data_dirs();
+ for (let i = 0; i < systemDataDirs.length; i++) {
+ let dirPath = GLib.build_filenamev([systemDataDirs[i], 'gnome-shell', 'extensions']);
+ let dir = Gio.file_new_for_path(dirPath);
+ if (dir.query_exists(null))
+ scanExtensionsInDirectory(callback, dir, ExtensionType.SYSTEM);
+ }
+ scanExtensionsInDirectory(callback, userExtensionsDir, ExtensionType.PER_USER);
+}
diff --git a/js/ui/extensionSystem.js b/js/ui/extensionSystem.js
index e7eea66..d2b25a0 100644
--- a/js/ui/extensionSystem.js
+++ b/js/ui/extensionSystem.js
@@ -8,10 +8,10 @@ const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
-const ShellJS = imports.gi.ShellJS;
const Soup = imports.gi.Soup;
const Config = imports.misc.config;
+const ExtensionUtils = imports.misc.extensionUtils;
const FileUtils = imports.misc.fileUtils;
const ModalDialog = imports.ui.modalDialog;
@@ -30,11 +30,6 @@ const ExtensionState = {
UNINSTALLED: 99
};
-const ExtensionType = {
- SYSTEM: 1,
- PER_USER: 2
-};
-
const REPOSITORY_URL_BASE = 'https://extensions.gnome.org';
const REPOSITORY_URL_DOWNLOAD = REPOSITORY_URL_BASE + '/download-extension/%s.shell-extension.zip';
const REPOSITORY_URL_INFO = REPOSITORY_URL_BASE + '/extension-info/';
@@ -60,8 +55,6 @@ _httpSession.ssl_ca_file = _getCertFile();
// Maps uuid -> metadata object
const extensionMeta = {};
-// Maps uuid -> importer object (extension directory tree)
-const extensions = {};
// Maps uuid -> extension state object (returned from init())
const extensionStateObjs = {};
// Contains the order that extensions were enabled in.
@@ -69,8 +62,6 @@ const extensionOrder = [];
// Arrays of uuids
var enabledExtensions;
-// GFile for user extensions
-var userExtensionsDir = null;
// We don't really have a class to add signals on. So, create
// a simple dummy object, add the signal methods, and export those
@@ -86,36 +77,6 @@ var errors = {};
const ENABLED_EXTENSIONS_KEY = 'enabled-extensions';
-/**
- * versionCheck:
- * @required: an array of versions we're compatible with
- * @current: the version we have
- *
- * Check if a component is compatible for an extension.
- * @required is an array, and at least one version must match.
- * @current must be in the format <major>.<minor>.<point>.<micro>
- * <micro> is always ignored
- * <point> is ignored if <minor> is even (so you can target the
- * whole stable release)
- * <minor> and <major> must match
- * Each target version must be at least <major> and <minor>
- */
-function versionCheck(required, current) {
- let currentArray = current.split('.');
- let major = currentArray[0];
- let minor = currentArray[1];
- let point = currentArray[2];
- for (let i = 0; i < required.length; i++) {
- let requiredArray = required[i].split('.');
- if (requiredArray[0] == major &&
- requiredArray[1] == minor &&
- (requiredArray[2] == point ||
- (requiredArray[2] == undefined && parseInt(minor) % 2 == 0)))
- return true;
- }
- return false;
-}
-
function installExtensionFromUUID(uuid, version_tag) {
let params = { uuid: uuid,
version_tag: version_tag,
@@ -143,18 +104,13 @@ function uninstallExtensionFromUUID(uuid) {
disableExtension(uuid);
// Don't try to uninstall system extensions
- if (meta.type != ExtensionType.PER_USER)
+ if (meta.type != ExtensionUtils.ExtensionType.PER_USER)
return false;
meta.state = ExtensionState.UNINSTALLED;
_signals.emit('extension-state-changed', meta);
delete extensionMeta[uuid];
-
- // Importers are marked as PERMANENT, so we can't do this.
- // delete extensions[uuid];
- extensions[uuid] = undefined;
-
delete extensionStateObjs[uuid];
delete errors[uuid];
@@ -179,7 +135,7 @@ function gotExtensionZipFile(session, message, uuid) {
}
let stream = new Gio.UnixOutputStream({ fd: fd });
- let dir = userExtensionsDir.get_child(uuid);
+ let dir = ExtensionUtils.userExtensionsDir.get_child(uuid);
Shell.write_soup_message_to_stream(stream, message);
stream.close(null);
let [success, pid] = GLib.spawn_async(null,
@@ -203,7 +159,7 @@ function gotExtensionZipFile(session, message, uuid) {
global.settings.set_strv(ENABLED_EXTENSIONS_KEY, enabledExtensions);
}
- loadExtension(dir, ExtensionType.PER_USER, true);
+ loadExtension(dir, ExtensionUtils.ExtensionType.PER_USER, true);
});
}
@@ -299,65 +255,26 @@ function logExtensionError(uuid, message, state) {
}
function loadExtension(dir, type, enabled) {
- let info;
let uuid = dir.get_basename();
-
- let metadataFile = dir.get_child('metadata.json');
- if (!metadataFile.query_exists(null)) {
- logExtensionError(uuid, 'Missing metadata.json');
- return;
- }
-
- let metadataContents;
- try {
- metadataContents = Shell.get_file_contents_utf8_sync(metadataFile.get_path());
- } catch (e) {
- logExtensionError(uuid, 'Failed to load metadata.json: ' + e);
- return;
- }
let meta;
- try {
- meta = JSON.parse(metadataContents);
- } catch (e) {
- logExtensionError(uuid, 'Failed to parse metadata.json: ' + e);
- return;
- }
-
- let requiredProperties = ['uuid', 'name', 'description', 'shell-version'];
- for (let i = 0; i < requiredProperties.length; i++) {
- let prop = requiredProperties[i];
- if (!meta[prop]) {
- logExtensionError(uuid, 'missing "' + prop + '" property in metadata.json');
- return;
- }
- }
-
- if (extensions[uuid] != undefined) {
- logExtensionError(uuid, 'extension already loaded');
- return;
- }
- // Encourage people to add this
- if (!meta['url']) {
- global.log('Warning: Missing "url" property in metadata.json');
+ if (extensionMeta[uuid] != undefined) {
+ throw new Error('extension already loaded');
}
- if (uuid != meta.uuid) {
- logExtensionError(uuid, 'uuid "' + meta.uuid + '" from metadata.json does not match directory name "' + uuid + '"');
+ try {
+ meta = ExtensionUtils.loadMetadata(uuid, dir, type);
+ } catch(e) {
+ logExtensionError(uuid, e.message);
return;
}
extensionMeta[uuid] = meta;
- meta.type = type;
- meta.dir = dir;
- meta.path = dir.get_path();
- meta.error = '';
// Default to error, we set success as the last step
meta.state = ExtensionState.ERROR;
- if (!versionCheck(meta['shell-version'], Config.PACKAGE_VERSION) ||
- (meta['js-version'] && !versionCheck(meta['js-version'], Config.GJS_VERSION))) {
+ if (ExtensionUtils.isOutOfDate(meta)) {
logExtensionError(uuid, 'extension is not compatible with current GNOME Shell and/or GJS version', ExtensionState.OUT_OF_DATE);
meta.state = ExtensionState.OUT_OF_DATE;
return;
@@ -389,8 +306,8 @@ function loadExtension(dir, type, enabled) {
let extensionModule;
let extensionState = null;
try {
- ShellJS.add_extension_importer('imports.ui.extensionSystem.extensions', meta.uuid, dir.get_path());
- extensionModule = extensions[meta.uuid].extension;
+ ExtensionUtils.installImporter(meta);
+ extensionModule = meta.importer.extension;
} catch (e) {
if (stylesheetPath != null)
theme.unload_stylesheet(stylesheetPath);
@@ -457,50 +374,17 @@ function onEnabledExtensionsChanged() {
}
function init() {
- let userExtensionsPath = GLib.build_filenamev([global.userdatadir, 'extensions']);
- userExtensionsDir = Gio.file_new_for_path(userExtensionsPath);
- try {
- if (!userExtensionsDir.query_exists(null))
- userExtensionsDir.make_directory_with_parents(null);
- } catch (e) {
- global.logError('' + e);
- }
+ ExtensionUtils.init();
global.settings.connect('changed::' + ENABLED_EXTENSIONS_KEY, onEnabledExtensionsChanged);
enabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
}
-function _loadExtensionsIn(dir, type) {
- let fileEnum;
- let file, info;
- try {
- fileEnum = dir.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null);
- } catch (e) {
- global.logError('' + e);
- return;
- }
-
- while ((info = fileEnum.next_file(null)) != null) {
- let fileType = info.get_file_type();
- if (fileType != Gio.FileType.DIRECTORY)
- continue;
- let name = info.get_name();
- let child = dir.get_child(name);
- let enabled = enabledExtensions.indexOf(name) != -1;
- loadExtension(child, type, enabled);
- }
- fileEnum.close(null);
-}
-
function loadExtensions() {
- let systemDataDirs = GLib.get_system_data_dirs();
- for (let i = 0; i < systemDataDirs.length; i++) {
- let dirPath = systemDataDirs[i] + '/gnome-shell/extensions';
- let dir = Gio.file_new_for_path(dirPath);
- if (dir.query_exists(null))
- _loadExtensionsIn(dir, ExtensionType.SYSTEM);
- }
- _loadExtensionsIn(userExtensionsDir, ExtensionType.PER_USER);
+ ExtensionUtils.scanExtensions(function(uuid, dir, type) {
+ let enabled = enabledExtensions.indexOf(uuid) != -1;
+ loadExtension(dir, type, enabled);
+ });
}
const InstallExtensionDialog = new Lang.Class({
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]