diff --git a/js/extensions/extension.js b/js/extensions/extension.js index a0d3f049d..c6a05f08b 100644 --- a/js/extensions/extension.js +++ b/js/extensions/extension.js @@ -1,3 +1,5 @@ +const Gi = imports._gi; + import {ExtensionBase, GettextWrapper} from './sharedInternals.js'; const {extensionManager} = imports.ui.main; @@ -48,7 +50,7 @@ export class InjectionManager { */ overrideMethod(prototype, methodName, createOverrideFunc) { const originalMethod = this._saveMethod(prototype, methodName); - prototype[methodName] = createOverrideFunc(originalMethod); + this._installMethod(prototype, methodName, createOverrideFunc(originalMethod)); } /** @@ -66,7 +68,7 @@ export class InjectionManager { if (originalMethod === undefined) delete prototype[methodName]; else - prototype[methodName] = originalMethod; + this._installMethod(prototype, methodName, originalMethod); savedProtoMethods.delete(methodName); if (savedProtoMethods.size === 0) @@ -96,4 +98,13 @@ export class InjectionManager { 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; + } + } } diff --git a/tests/unit/injectionManager.js b/tests/unit/injectionManager.js index 6de509441..e727f35e9 100644 --- a/tests/unit/injectionManager.js +++ b/tests/unit/injectionManager.js @@ -1,5 +1,7 @@ const JsUnit = imports.jsUnit; +import GObject from 'gi://GObject'; + import '../../js/ui/environment.js'; import {InjectionManager} from '../../js/extensions/extension.js'; @@ -16,6 +18,23 @@ class Object1 { } } +class GObject1 extends GObject.Object { + static [GObject.properties] = { + 'plonked': GObject.ParamSpec.boolean( + 'plonked', '', '', + GObject.ParamFlags.READWRITE, + false), + }; + + static { + GObject.registerClass(this); + } + + plonk() { + this.set_property('plonked', true); + } +} + /** * @param {object} object to modify */ @@ -91,3 +110,28 @@ JsUnit.assertEquals(obj.getNumber(), 42); JsUnit.assertEquals(obj.getCount(), obj.count); JsUnit.assert(obj.count > 0); JsUnit.assertRaises(() => obj.getOtherNumber()); + +// GObject injections +const gobj = new GObject1(); +let vfuncCalled; + +injectionManager.overrideMethod( + GObject1.prototype, 'vfunc_set_property', originalMethod => { + return function (...args) { + // eslint-disable-next-line no-invalid-this + originalMethod.apply(this, args); + vfuncCalled = true; + }; + }); + +// gobj is now modified +vfuncCalled = false; +gobj.plonk(); +JsUnit.assertTrue(vfuncCalled); + +injectionManager.clear(); + +// gobj is unmodified again +vfuncCalled = false; +gobj.plonk(); +JsUnit.assertFalse(vfuncCalled);