environment: Port environment.js to be an ES module

Removes the init() function in favor of executing all environment
changes when the file is imported.

Additionally ports all unit tests using imports.gi.environment.init() to
use the updated module.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2822>
This commit is contained in:
Evan Welsh 2023-07-09 23:05:14 -07:00
parent 26fa1046a3
commit 9a2c3da868
10 changed files with 125 additions and 134 deletions

View File

@ -4,6 +4,7 @@ extends:
overrides:
- files:
- js/ui/init.js
- js/ui/environment.js
- js/dbusServices/**
- js/portalHelper/**
- subprojects/extensions-app/**

View File

@ -1,22 +1,24 @@
/* exported init */
// Load all required dependencies with the correct versions
imports.misc.dependencies; // eslint-disable-line no-unused-expressions
import '../misc/dependencies.js';
import {setConsoleLogDomain} from 'console';
import * as Gettext from 'gettext';
import Clutter from 'gi://Clutter';
import Gdk from 'gi://Gdk';
import Gio from 'gi://Gio';
import GLib from 'gi://GLib';
import GObject from 'gi://GObject';
import Meta from 'gi://Meta';
import Polkit from 'gi://Polkit';
import Shell from 'gi://Shell';
import St from 'gi://St';
const Clutter = imports.gi.Clutter;
const Gdk = imports.gi.Gdk;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const GObject = imports.gi.GObject;
const Meta = imports.gi.Meta;
const Polkit = imports.gi.Polkit;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Gettext = imports.gettext;
const SignalTracker = imports.misc.signalTracker;
const {adjustAnimationTime} = imports.misc.animationUtils;
setConsoleLogDomain('GNOME Shell');
Gio._promisify(Gio.DataInputStream.prototype, 'fill_async');
Gio._promisify(Gio.DataInputStream.prototype, 'read_line_async');
Gio._promisify(Gio.DBus, 'get');
@ -28,9 +30,8 @@ Gio._promisify(Gio.File.prototype, 'query_info_async');
Gio._promisify(Polkit.Permission, 'new');
// We can't import shell JS modules yet, because they may have
// variable initializations, etc, that depend on init() already having
// been run.
// variable initializations, etc, that depend on this file's
// changes
// "monkey patch" in some varargs ClutterContainer methods; we need
// to do this per-container class since there is no representation
@ -248,116 +249,114 @@ function _easeActorProperty(actor, propName, target, params) {
transition.connect('stopped', (t, finished) => callback(finished));
}
function init() {
// Add some bindings to the global JS namespace
globalThis.global = Shell.Global.get();
// Add some bindings to the global JS namespace
globalThis.global = Shell.Global.get();
globalThis._ = Gettext.gettext;
globalThis.C_ = Gettext.pgettext;
globalThis.ngettext = Gettext.ngettext;
globalThis.N_ = s => s;
globalThis._ = Gettext.gettext;
globalThis.C_ = Gettext.pgettext;
globalThis.ngettext = Gettext.ngettext;
globalThis.N_ = s => s;
GObject.gtypeNameBasedOnJSPath = true;
GObject.gtypeNameBasedOnJSPath = true;
GObject.Object.prototype.connectObject = function (...args) {
SignalTracker.connectObject(this, ...args);
};
GObject.Object.prototype.connect_object = function (...args) {
SignalTracker.connectObject(this, ...args);
};
GObject.Object.prototype.disconnectObject = function (...args) {
SignalTracker.disconnectObject(this, ...args);
};
GObject.Object.prototype.disconnect_object = function (...args) {
SignalTracker.disconnectObject(this, ...args);
};
GObject.Object.prototype.connectObject = function (...args) {
SignalTracker.connectObject(this, ...args);
};
GObject.Object.prototype.connect_object = function (...args) {
SignalTracker.connectObject(this, ...args);
};
GObject.Object.prototype.disconnectObject = function (...args) {
SignalTracker.disconnectObject(this, ...args);
};
GObject.Object.prototype.disconnect_object = function (...args) {
SignalTracker.disconnectObject(this, ...args);
};
SignalTracker.registerDestroyableType(Clutter.Actor);
SignalTracker.registerDestroyableType(Clutter.Actor);
// Miscellaneous monkeypatching
_patchContainerClass(St.BoxLayout);
// Miscellaneous monkeypatching
_patchContainerClass(St.BoxLayout);
_patchLayoutClass(Clutter.GridLayout, {
row_spacing: 'spacing-rows',
column_spacing: 'spacing-columns',
});
_patchLayoutClass(Clutter.BoxLayout, { spacing: 'spacing' });
_patchLayoutClass(Clutter.GridLayout, {
row_spacing: 'spacing-rows',
column_spacing: 'spacing-columns',
});
_patchLayoutClass(Clutter.BoxLayout, {spacing: 'spacing'});
let origSetEasingDuration = Clutter.Actor.prototype.set_easing_duration;
Clutter.Actor.prototype.set_easing_duration = function (msecs) {
origSetEasingDuration.call(this, adjustAnimationTime(msecs));
};
let origSetEasingDelay = Clutter.Actor.prototype.set_easing_delay;
Clutter.Actor.prototype.set_easing_delay = function (msecs) {
origSetEasingDelay.call(this, adjustAnimationTime(msecs));
};
const origSetEasingDuration = Clutter.Actor.prototype.set_easing_duration;
Clutter.Actor.prototype.set_easing_duration = function (msecs) {
origSetEasingDuration.call(this, adjustAnimationTime(msecs));
};
const origSetEasingDelay = Clutter.Actor.prototype.set_easing_delay;
Clutter.Actor.prototype.set_easing_delay = function (msecs) {
origSetEasingDelay.call(this, adjustAnimationTime(msecs));
};
Clutter.Actor.prototype.ease = function (props) {
_easeActor(this, props);
};
Clutter.Actor.prototype.ease_property = function (propName, target, params) {
_easeActorProperty(this, propName, target, params);
};
St.Adjustment.prototype.ease = function (target, params) {
// we're not an actor of course, but we implement the same
// transition API as Clutter.Actor, so this works anyway
_easeActorProperty(this, 'value', target, params);
};
Clutter.Actor.prototype.ease = function (props) {
_easeActor(this, props);
};
Clutter.Actor.prototype.ease_property = function (propName, target, params) {
_easeActorProperty(this, propName, target, params);
};
St.Adjustment.prototype.ease = function (target, params) {
// we're not an actor of course, but we implement the same
// transition API as Clutter.Actor, so this works anyway
_easeActorProperty(this, 'value', target, params);
};
Clutter.Actor.prototype[Symbol.iterator] = function* () {
for (let c = this.get_first_child(); c; c = c.get_next_sibling())
yield c;
};
Clutter.Actor.prototype[Symbol.iterator] = function* () {
for (let c = this.get_first_child(); c; c = c.get_next_sibling())
yield c;
};
Clutter.Actor.prototype.toString = function () {
return St.describe_actor(this);
};
// Deprecation warning for former JS classes turned into an actor subclass
Object.defineProperty(Clutter.Actor.prototype, 'actor', {
get() {
let klass = this.constructor.name;
let { stack } = new Error();
log(`Usage of object.actor is deprecated for ${klass}\n${stack}`);
return this;
},
});
Clutter.Actor.prototype.toString = function () {
return St.describe_actor(this);
};
// Deprecation warning for former JS classes turned into an actor subclass
Object.defineProperty(Clutter.Actor.prototype, 'actor', {
get() {
let klass = this.constructor.name;
let {stack} = new Error();
log(`Usage of object.actor is deprecated for ${klass}\n${stack}`);
return this;
},
});
Gio.File.prototype.touch_async = function (callback) {
Shell.util_touch_file_async(this, callback);
};
Gio.File.prototype.touch_finish = function (result) {
return Shell.util_touch_file_finish(this, result);
};
Gio.File.prototype.touch_async = function (callback) {
Shell.util_touch_file_async(this, callback);
};
Gio.File.prototype.touch_finish = function (result) {
return Shell.util_touch_file_finish(this, result);
};
let origToString = Object.prototype.toString;
Object.prototype.toString = function () {
let base = origToString.call(this);
try {
if ('actor' in this && this.actor instanceof Clutter.Actor)
return base.replace(/\]$/, ` delegate for ${this.actor.toString().substring(1)}`);
else
return base;
} catch (e) {
const origToString = Object.prototype.toString;
Object.prototype.toString = function () {
let base = origToString.call(this);
try {
if ('actor' in this && this.actor instanceof Clutter.Actor)
return base.replace(/\]$/, ` delegate for ${this.actor.toString().substring(1)}`);
else
return base;
}
};
let slowdownEnv = GLib.getenv('GNOME_SHELL_SLOWDOWN_FACTOR');
if (slowdownEnv) {
let factor = parseFloat(slowdownEnv);
if (!isNaN(factor) && factor > 0.0)
St.Settings.get().slow_down_factor = factor;
} catch (e) {
return base;
}
};
// OK, now things are initialized enough that we can import shell JS
const Format = imports.format;
String.prototype.format = Format.format;
Math.clamp = function (x, lower, upper) {
return Math.min(Math.max(x, lower), upper);
};
// Prevent extensions from opening a display connection to ourselves
Gdk.set_allowed_backends('');
const slowdownEnv = GLib.getenv('GNOME_SHELL_SLOWDOWN_FACTOR');
if (slowdownEnv) {
let factor = parseFloat(slowdownEnv);
if (!isNaN(factor) && factor > 0.0)
St.Settings.get().slow_down_factor = factor;
}
// OK, now things are initialized enough that we can import shell JS
const Format = imports.format;
String.prototype.format = Format.format;
Math.clamp = function (x, lower, upper) {
return Math.min(Math.max(x, lower), upper);
};
// Prevent extensions from opening a display connection to ourselves
Gdk.set_allowed_backends('');

View File

@ -1,10 +1,7 @@
import {setConsoleLogDomain} from 'console';
import GLib from 'gi://GLib';
import Gio from 'gi://Gio';
setConsoleLogDomain('GNOME Shell');
imports.ui.environment.init();
import './environment.js';
// Run the Mutter main loop after
// GJS finishes resolving this module.

View File

@ -5,9 +5,7 @@
const JsUnit = imports.jsUnit;
import Pango from 'gi://Pango';
const Environment = imports.ui.environment;
Environment.init();
import '../../js/ui/environment.js';
const Util = imports.misc.util;
const tests = [

View File

@ -1,12 +1,13 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// Test cases for Util.insertSorted
import * as Assertions from '../common/assertions.js';
// Needed so that Util can bring some UI stuff
// we don't actually use
const Environment = imports.ui.environment;
Environment.init();
import '../../js/ui/environment.js';
import * as Assertions from '../common/assertions.js';
const Util = imports.misc.util;
let arrayInt = [1, 2, 3, 5, 6];

View File

@ -4,9 +4,9 @@
const JsUnit = imports.jsUnit;
import '../../js/ui/environment.js';
import * as Assertions from '../common/assertions.js';
const Environment = imports.ui.environment;
Environment.init();
const JsParse = imports.misc.jsParse;

View File

@ -5,8 +5,7 @@
const JsUnit = imports.jsUnit;
import Pango from 'gi://Pango';
const Environment = imports.ui.environment;
Environment.init();
import '../../js/ui/environment.js';
imports.ui.main; // eslint-disable-line no-unused-expressions
const MessageList = imports.ui.messageList;

View File

@ -2,16 +2,14 @@
// Test cases for version comparison
import '../../js/ui/environment.js';
import GObject from 'gi://GObject';
const JsUnit = imports.jsUnit;
const Signals = imports.misc.signals;
const Environment = imports.ui.environment;
const {TransientSignalHolder, registerDestroyableType} = imports.misc.signalTracker;
Environment.init();
const Destroyable = GObject.registerClass({
Signals: {'destroy': {}},
}, class Destroyable extends GObject.Object {});

View File

@ -4,8 +4,7 @@
const JsUnit = imports.jsUnit;
const Environment = imports.ui.environment;
Environment.init();
import '../../js/ui/environment.js';
const Util = imports.misc.util;

View File

@ -4,8 +4,7 @@
const JsUnit = imports.jsUnit;
const Environment = imports.ui.environment;
Environment.init();
import '../../js/ui/environment.js';
const Util = imports.misc.util;