111 lines
3.3 KiB
JavaScript
111 lines
3.3 KiB
JavaScript
const Gi = imports._gi;
|
|
|
|
import {ExtensionBase, GettextWrapper} from './sharedInternals.js';
|
|
|
|
import {extensionManager} from '../ui/main.js';
|
|
|
|
export class Extension extends ExtensionBase {
|
|
static lookupByUUID(uuid) {
|
|
return extensionManager.lookup(uuid)?.stateObj ?? null;
|
|
}
|
|
|
|
static defineTranslationFunctions(url) {
|
|
const wrapper = new GettextWrapper(this, url);
|
|
return wrapper.defineTranslationFunctions();
|
|
}
|
|
|
|
enable() {
|
|
}
|
|
|
|
disable() {
|
|
}
|
|
|
|
/**
|
|
* Open the extension's preferences window
|
|
*/
|
|
openPreferences() {
|
|
extensionManager.openExtensionPrefs(this.uuid, '', {});
|
|
}
|
|
}
|
|
|
|
export const {
|
|
gettext, ngettext, pgettext,
|
|
} = Extension.defineTranslationFunctions();
|
|
|
|
export class InjectionManager {
|
|
#savedMethods = new Map();
|
|
|
|
/**
|
|
* @callback CreateOverrideFunc
|
|
* @param {Function?} originalMethod - the original method if it exists
|
|
* @returns {Function} - a function to be used as override
|
|
*/
|
|
|
|
/**
|
|
* Modify, replace or inject a method
|
|
*
|
|
* @param {object} prototype - the object (or prototype) that is modified
|
|
* @param {string} methodName - the name of the overwritten method
|
|
* @param {CreateOverrideFunc} createOverrideFunc - function to call to create the override
|
|
*/
|
|
overrideMethod(prototype, methodName, createOverrideFunc) {
|
|
const originalMethod = this._saveMethod(prototype, methodName);
|
|
this._installMethod(prototype, methodName, createOverrideFunc(originalMethod));
|
|
}
|
|
|
|
/**
|
|
* Restore the original method
|
|
*
|
|
* @param {object} prototype - the object (or prototype) that is modified
|
|
* @param {string} methodName - the name of the method to restore
|
|
*/
|
|
restoreMethod(prototype, methodName) {
|
|
const savedProtoMethods = this.#savedMethods.get(prototype);
|
|
if (!savedProtoMethods)
|
|
return;
|
|
|
|
const originalMethod = savedProtoMethods.get(methodName);
|
|
if (originalMethod === undefined)
|
|
delete prototype[methodName];
|
|
else
|
|
this._installMethod(prototype, methodName, originalMethod);
|
|
|
|
savedProtoMethods.delete(methodName);
|
|
if (savedProtoMethods.size === 0)
|
|
this.#savedMethods.delete(prototype);
|
|
}
|
|
|
|
/**
|
|
* Restore all original methods and clear overrides
|
|
*/
|
|
clear() {
|
|
for (const [proto, map] of this.#savedMethods) {
|
|
map.forEach(
|
|
(_, methodName) => this.restoreMethod(proto, methodName));
|
|
}
|
|
console.assert(this.#savedMethods.size === 0,
|
|
`${this.#savedMethods.size} overrides left after clear()`);
|
|
}
|
|
|
|
_saveMethod(prototype, methodName) {
|
|
let savedProtoMethods = this.#savedMethods.get(prototype);
|
|
if (!savedProtoMethods) {
|
|
savedProtoMethods = new Map();
|
|
this.#savedMethods.set(prototype, savedProtoMethods);
|
|
}
|
|
|
|
const originalMethod = prototype[methodName];
|
|
savedProtoMethods.set(methodName, originalMethod);
|
|
return originalMethod;
|
|
}
|
|
|
|
_installMethod(prototype, methodName, method) {
|
|
if (methodName.startsWith('vfunc_')) {
|
|
const giPrototype = prototype[Gi.gobject_prototype_symbol];
|
|
giPrototype[Gi.hook_up_vfunc_symbol](methodName.slice(6), method);
|
|
} else {
|
|
prototype[methodName] = method;
|
|
}
|
|
}
|
|
}
|