The GNOME and JavaScript Relationship: Exploration
For a long time I knew there was JavaScript used in the GNOME project, but only recently I asked myself: How much of it?
Ah GNOME: Some love it, some hate it. After messing around in some custom shells, such as Ubuntu’s and Zorin’s, I was curious about the project, more particularly: JavaScript usage.
Indeed, I was curious about the JavaScript usage in GNOME but luckily my long running question was answered in vanvugt’s post (https://discourse.ubuntu.com/t/boosting-the-real-time-performance-of-gnome-shell-3-34-in-ubuntu-19-10/13095) — about 10% JS and 90% C (GNOME Shell and Mutter).
After some searching I landed on GJS (https://gitlab.gnome.org/GNOME/gjs). GJS corresponds to the GNOME JavaScript bindings. This is a library useful for JavaScript applications to use the GNOME API functions written in C, and it’s using Mozilla’s SpiderMonkey engine (https://gitlab.gnome.org/GNOME/gjs/blob/master/doc/Home.md). So far, a few GNOME apps like Characters, Documents, Maps, Sound Recorder, and Weather use GJS.
For fun, I went around GNOME’s GitLab instance (22 pages!) and decided to create this table with the most used language per project in alphabetical order. Metrics by the GitLab instance.
Note: Vala is derived from C, uses the GObject model, and generates C code when compiled.
Project | Description |
Language usage |
Clocks | International clocks, timer, alarms | Vala (86.5%), C (4.6%), Meson (4.5%), CSS (4.1%), Python (0.4%) |
clutter | Software library for creating fast, compelling, portable, and dynamic graphical user interfaces |
C (96.8%), Makefile (0.8%), M4 (0.8%), Shell (0.5%), Meson (0.4%) |
dconf-editor | Vala (94.5%), CSS (4%), Meson (1.1%), Roff (0.2%), Python (0.1%) | |
Documents | Document manager | JavaScript (68.6%), C (28.7%), Meson (2.1%), CSS (0.4%), Python (0.2%) |
Document Scanner | Vala (97.8%), Meson (1.6%), Roff (0.6%), Python (0.1%) | |
Epiphany | Web browser | C (92.7%), JavaScript (3.2%), CSS (2.9%), Meson (0.8%), Python (0.3%) |
evince | Document viewer | C (89.8%), C++ (5.1%), Makefile (1.8%), Meson (1.4%), M4 (1.2%) |
Eye of GNOME | Image viewer | C (79.2%), Roff (16.1%), C++ (1.9%), Meson (1.3%), Python (1.3%) |
gedit | Text editor | C (77.2%), Python (17.8%), Objective-C (1.9%), Meson (1.5%), Shell (1.1%) |
gjs | GNOME JavaScript bindings | C++ (60.6%), JavaScript (32%), Shell (2.2%), C (2.1%), Meson (1.7%) |
gnome-calculator | Vala (94.6%), Meson (2.6%), Python (1.2%), M4 (0.7%), Gherkin (0.6%) | |
gnome-calendar | C (98%), Meson (1.2%), CSS (0.7%), | |
gnome-characters | Show a list of characters | C (72.9%), JavaScript (20.8%), Python (3.2%), Meson (2.2%), Ruby (0.6%) |
GNOME Contacts | Vala (80.7%), C (13.8%), Meson (3.5%), Python (1.4%), CSS (0.5%) | |
gnome-desktop | Unstable library intended for sharing code between GNOME desktop components | C (96.8%), Meson (2.1%), Python (0.6%), Objective-C (0.5%) |
gnome-initial-setup | C (97.2%), Meson (2.3%), JavaScript (0.2%), Shell (0.2%), CSS (0.2%) | |
gnome-maps | JavaScript (88.6%), C (10.5%), Meson (0.6%), CSS (0.2%), Python (0.1%) | |
gnome-shell | Core user interface functions for the GNOME desktop | C (50.3%), JavaScript (46.3%), CSS (1.5%), Meson (0.7%), Python (0.6%) |
gnome-terminal | C (92.4%), M4 (4.9%), Makefile (2.6%), CSS (0.1%) | |
gnome-todo | C (96.4%), Meson (2.4%), Python (0.9%), CSS (0.3%) | |
GNOME Tour | GNOME Tour & Greeter | Rust (70%), Meson (22.1%), Shell (5.2%), Python (1.6%), CSS (1.2%) |
Mutter | Wayland display server and X11 window manager and compositor library | C (98.6%), Meson (0.7%), C++ (0.4%), Python (0.2%), Shell (0.1%) |
nautilus | File manager | C (98.9%), Meson (0.6%), Python (0.2%), CSS (0.1%), Shell (0.1%) |
Settings | Main interface to configure various aspects of the desktop | C (96.5%), Meson (1.8%), Python (1.6%), CSS (0.1%), Shell (0.1%) |
vala | Vala programming language | Vala (71.8%), GAP (25.2%), C (1.7%), Makefile (0.6%), CSS (0.2%) |
Weather | JavaScript (84.6%), Meson (7.6%), Python (4.9%), CSS (2.9%) |
What a list! Here we can see a few applications that use the GJS bindings. To look at the JavaScript interactions, let’s take a look at a few examples: Maps, gnome-shell, and GJS.
Maps
First to check out: Maps. Maps is pretty straight-forward: It’s a map utility that uses the Mapbox service (using OpenStreetMap) and GJS. Interestingly, to launch it in GDB, the command-line is: libtool --mode=execute gdb --args gjs /usr/bin/gnome-maps
(https://wiki.gnome.org/Apps/Maps/Resources). Seeing src/mainWindow.js
, I found that GJS acts more like an engine, like Electron does.
GNOME Shell
Next: gnome-shell. The description for the shell is “GNOME Shell provides core user interface functions for the GNOME 3 desktop, like switching to windows and launching applications.”. On their FAQ page (https://wiki.gnome.org/Projects/GnomeShell/FAQ), it does say “The Shell is Javascript and CSS – yes, that Javascript and CSS. The same ones that a gazillion teenagers are playing with right now.” with the following figure.
The page also states “We also have a custom Javascript console called Looking Glass that allows evaluating arbitrary JavaScript statements, as well as accessing and manipulating objects in the Shell from within the Shell. So it is similar to the Firebug in Firefox or to the interactive lisp facilities in Emacs in its nature.”
What purpose does JavaScript have here? On their Technology page (https://wiki.gnome.org/Projects/GnomeShell/Technology), it is written “Much of the code of the shell is written in Javascript but uses Clutter and GNOME platform libraries via GObject Introspection and JavaScript bindings for GNOME.” The page also link’s Owen Taylor’s October 22, 2008 post (https://blog.fishsoup.net/2008/10/22/implementing-the-next-gnome-shell/) about the implementation of the GNOME Shell and reads “There’s going to be lots of code that needs experimentation to get the right user behavior, but not a lot of code that is implementing some complicated algorithm over lots of data.”, which I assume while a lot of the shell code is JavaScript, not much of it will handles more complex tasks.
To verify that, I decided to look in the gnome-shell repository. In the js
folder, there is gdm
(GNOME Desktop Manager) and ui
. So yes, gdm is written in JavaScript. As for the rest of the code in src, it is in C, which handles window management (src/shell-wm.c
), tray (src/shell-tray.c
), blurring (src/shell-blur.c
), app system (src/shell-app.c
, src/shell-app-system.c
), and so on.
The js/gdm
folder contains the following files.
File | Description | Notes |
authPrompt.js |
Seems to handle the authentication prompt or unlocking and/or logging in (i.e. root access for package management) | Contains a hack for PAM authentication. Can do smart-cards. |
batch.js |
Seems to handle batches of tasks, like signals. | In code: “These APIs may ultimately get dropped entirely and replaced by something else.” |
fingerprint.js |
One function to handle a D-Bus function to use FIFO. | |
loginDialog.js |
Seems to be the initial login screen. | 1287 lines |
oVirt.js |
oVirt credential manager. | |
realmd.js |
Realm manager. (Think of Active Directory) | Connected to org.freedesktop.realmd, /org/freedesktop/realmd, and D-Bus. |
util.js |
Seems to aid the other scripts found in this folder. | Service names, user shell verifier, signal connections, etc. |
As the blog post says, it’s mostly common tasks and behaviors that are written on top of the JavaScript Engine (GJS). The rest, such as session management (js/misc/gnomeSession.js
), Weather API (js/misc/weather.js
), and the keyboard manager (js/misc/keyboardManager.js
) are all in js/misc
(more files than js/gdm
!).
Basically, the JavaScript here handles some basic tasks, such as managing a session, popping an user unlock dialog (placing buttons around and passing the credentials to the manager), etc., but it does not handle window management, so only high-level behavior, as it seems.
GJS
Lastly, the core JavaScript piece: GNOME JavaScript Engine. The JavaScript engine provides a proxy to D-Bus, GNOME APIs, and the sort. It embeds Mozilla’s SpiderMonkey JavaScript engine, the same version used as the Extended Support Release (ESR) (https://gitlab.gnome.org/GNOME/gjs/-/blob/master/doc/Understanding-SpiderMonkey-code.md). Notably, likely a version still before ESR 52 as the team was hoping to get ESR 60 as a stand-alone version. There is no HTML renderer, assuming WebKit can be used in those cases.
The examples
folder contains a calculator (gjs/examples/calc.js
), WebKit (gjs/examples/wekbit.js
), GTK (gjs/examples/gtk.js
), Clutter (gjs/examples/clutter.js
), and other examples of the sort. Among these examples, there is a module that serves as the glue point: gi
. The gi
folder contains C++ code that provides the GObject JS bindings. Cairo, as I assume is a module used to render controls (see gjs/modules/cairo.cpp
, class XLibConstructor
), is within the gjs/modules
folder, providing such facility via the gi
module.
For example, the calc.js example has a createButton function:
function createButton(str, func) { var btn = new Gtk.Button({label: str}); btn.connect('clicked', func); return btn; }
Upon window creation, there is a function, createButtons, that serves the purpose of creating buttons:
function createButtons() { var vbox = new Gtk.VBox({homogeneous: true}); packButtons([ createButton('(', pressedNumber), createButton('←', backspace), createButton('↻', randomNum), createButton('Clr', clear), createButton('±', swapSign), ], vbox); // ...
And I guess that’s pretty much it!
Conclusion
Do I worry about JavaScript usage? Eh, not really. JavaScript is used like Python in the sense that Python has bindings to a native module via an intermediate module. Instead of CPython, there’s GJS (using SpiderMonkey), and in the case of GDM — It is used lightly (basic behavior, UI definitions, no complex/heavy handling) compared to their under-the-hood C parts (and C++ bindings). For example, Ubuntu’s shell is a GJS application, as the buttons on the shell have CSS rules and JS functions attached to them (e.g. seeing [0x563d43efc880 StButton.app-well-app:last-child first-child ("")]
). However, I do hope the GNOME team keeps up with the SpiderMonkey version used whenever possible. (I might want to send an email or comment somewhere)
After some searching, I found (https://wiki.gnome.org/Gnome3CheatSheet#Developer_tools) that you can pop the Looking Glass console by pressing ALT+F2, typing lg
, and pressing Return or Enter (also here https://wiki.gnome.org/Projects/GnomeShell/LookingGlass). Although this console operates on Python, it is a GJS console. Though, I could not obtain the SpiderMonkey version through lg (even looking at inspect(global)
). Oh well. What is neat though is that shell extensions can be found at /usr/share/gnome-shell/extensions
(including the Ubuntu dock).
This article does not attempt to defend GNOME, people can bash it as much as they want, I was simply a little curious (and lazy to go further). Current grudges: Too little titlebar space in some applications (well, in a VM, I use Xfce so it picks my Alt key before Virtualbox does) and the ToDo application bugs out when I enter in and out of a list of task repetitively.
This little adventure helped me defined where JS is defined (onto of GJS), what it is used for (basic UI stuff and basic apps), and how it is used.