diff --git a/js/ui/environment.js b/js/ui/environment.js index 209cdc822..67682d8fe 100644 --- a/js/ui/environment.js +++ b/js/ui/environment.js @@ -1,5 +1,6 @@ /* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ +const Shell = imports.gi.Shell; const St = imports.gi.St; const Gettext_gtk20 = imports.gettext.domain('gtk20'); @@ -28,8 +29,34 @@ function _patchContainerClass(containerClass) { }; } -_patchContainerClass(St.BoxLayout); -_patchContainerClass(St.Table); +// Replace @method with something that throws an error instead +function _blockMethod(method, replacement, reason) { + let match = method.match(/^(.+)\.([^.]+)$/); + if (!match) + throw new Error('Bad method name "' + method + '"'); + let proto = 'imports.gi.' + match[1] + '.prototype'; + let property = match[2]; + + if (!global.set_property_mutable(proto, property, true)) + throw new Error('Bad method name "' + method + '"'); + + // eval() is evil in general, but we know it's safe here since + // set_property_mutable() would have failed if proto was + // malformed. + let node = eval(proto); + + let msg = 'Do not use "' + method + '".'; + if (replacement) + msg += ' Use "' + replacement + '" instead.'; + if (reason) + msg += ' (' + reason + ')'; + + node[property] = function() { + throw new Error(msg); + }; + + global.set_property_mutable(proto, property, false); +} function init() { Tweener.init(); @@ -39,4 +66,20 @@ function init() { if (Gettext_gtk20.gettext('default:LTR') == 'default:RTL') { St.Widget.set_default_direction(St.TextDirection.RTL); } + + _patchContainerClass(St.BoxLayout); + _patchContainerClass(St.Table); + + _blockMethod('Clutter.Event.get_state', 'Shell.get_event_state', + 'gjs\'s handling of Clutter.ModifierType is broken. See bug 597292.'); + _blockMethod('Gdk.Display.get_pointer', 'global.get_pointer', + 'gjs\'s handling of Gdk.ModifierType is broken. See bug 597292.'); + _blockMethod('Gdk.Window.get_pointer', 'global.get_pointer', + 'gjs\'s handling of Gdk.ModifierType is broken. See bug 597292.'); + + // Now close the back door to prevent extensions from trying to + // abuse it. We can't actually delete it since + // Shell.Global.prototype itself is read-only. + global.set_property_mutable('imports.gi.Shell.Global.prototype', 'set_property_mutable', true); + Shell.Global.prototype.set_property_mutable = undefined; } diff --git a/src/shell-global.c b/src/shell-global.c index 758af542e..5d61e41e2 100644 --- a/src/shell-global.c +++ b/src/shell-global.c @@ -1327,3 +1327,68 @@ shell_global_create_app_launch_context (ShellGlobal *global) return (GAppLaunchContext *)context; } + +/** + * shell_global_set_property_mutable: + * @global: the #ShellGlobal + * @object: the "path" to a JS object, starting from the root object. + * (Eg, "global.stage" or "imports.gi.Gtk.Window.prototype") + * @property: a property on @object + * @mutable: %TRUE or %FALSE + * + * If @mutable is %TRUE, this clears the "permanent" and "readonly" flags + * on @property of @object. If @mutable is %FALSE, it sets them. + * + * You can use this to make it possible to modify properties that + * would otherwise be read-only from JavaScript. + * + * Return value: success or failure. + */ +gboolean +shell_global_set_property_mutable (ShellGlobal *global, + const char *object, + const char *property, + gboolean mutable) +{ + JSContext *context = gjs_context_get_native_context (global->js_context); + char **parts; + JSObject *obj; + jsval val = JSVAL_VOID; + int i; + jsuint attrs; + JSBool found; + + JS_AddRoot (context, &val); + + parts = g_strsplit (object, ".", -1); + obj = JS_GetGlobalObject (context); + for (i = 0; parts[i]; i++) + { + if (!JS_GetProperty (context, obj, parts[i], &val)) + { + g_strfreev (parts); + JS_RemoveRoot (context, &val); + gjs_log_exception (context, NULL); + return FALSE; + } + obj = JSVAL_TO_OBJECT (val); + } + g_strfreev (parts); + + if (!JS_GetPropertyAttributes (context, obj, property, &attrs, &found) || !found) + { + JS_RemoveRoot (context, &val); + gjs_log_exception (context, NULL); + return FALSE; + } + + if (mutable) + attrs &= ~(JSPROP_PERMANENT | JSPROP_READONLY); + else + attrs |= (JSPROP_PERMANENT | JSPROP_READONLY); + + JS_SetPropertyAttributes (context, obj, property, attrs, &found); + + JS_RemoveRoot (context, &val); + return !gjs_log_exception (context, NULL); +} diff --git a/src/shell-global.h b/src/shell-global.h index 5ac31d820..27c43be97 100644 --- a/src/shell-global.h +++ b/src/shell-global.h @@ -96,6 +96,11 @@ guint32 shell_global_get_current_time (ShellGlobal *global); GAppLaunchContext *shell_global_create_app_launch_context (ShellGlobal *global); +gboolean shell_global_set_property_mutable (ShellGlobal *global, + const char *object, + const char *property, + gboolean mutable); + G_END_DECLS #endif /* __SHELL_GLOBAL_H__ */