If you’ve written and submitted a GNOME Shell Extension, you’ve probably run across a reviewer saying that you shouldn’t do something, not even knowing what you shouldn’t have done beforehand. I’m sorry, the Shell team (including myself) hasn’t really done a good job documenting what a good GNOME Shell Extension should do, and we reviewers constantly run across the same mistakes in review.
I’m going to hopefully change this for the better by documenting most of what us reviewers look for when reviewing code. This isn’t meant to be an exhaustive list – even if you do everything in here, we still may reject your extension for other reasons. If it’s a code reason and it’s a common one, I’ll update the list.
This is mostly about GNOME-specific gotchas. Please attempt to write straightforward and clean code. Unravelling spaghetti is not fun for any of the extension reviewer team.
Don’t do anything major in init()
init()
is there to have anything that needs to happen at first-run, like binding text domains. Do not make any UI modifications or setup any callbacks or anything in init()
. Your extension will break, and I will reject it. Do any and all modifications in enable()
.
Undo all UI modifications in disable()
If you make any UI modifications in enable()
, you need to undo them in disable()
. So, if you add an icon to the top bar when your extension is enabled, you need to remove it. Simple. Failure to do so will be an immediate rejection.
Remove all possible callback sources
If you’re writing an extension, you must disconnect all signals and remove all Mainloop sources. Failure to do so will be an immediate rejection. So, if you connect to a signal using global.display.connect('window-created', onWindowCreated);
, you need to disconnect it:
let _windowCreatedId; function enable() { _windowCreatedId = global.display.connect('window-created', onWindowCreated); } function disable() { global.display.disconnect(_windowCreatedId); }
The same goes for any timeouts or intervals added with the Mainloop module. Use Mainloop.source_remove
to make sure your source is removed.
Extensions targeted for GNOME 3.2 cannot use GSettings
It’s unfortuate, but true. Extensions that target GNOME 3.2 cannot use GSettings when uploaded to the repository. It will crash the Shell. There are new APIs in GNOME 3.4 that allow us to work around this. The technique used most often is to have a bunch of const
statements at the top of the shell that the user can modify.
A quick aside, a libsoup gotcha
function buttonPressed() { // This code will crash the Shell, do not use it! let session = new Soup.SessionAsync(); let message = Soup.form_request_new_for_hash('GET', 'http://example.com', {'beans': 'sauce'}); session.queue_message(message, function() { log('soup is so easy!'); }); }
While we really shouldn’t be crashing here, it’s a hard issue to fix. The technical reason is that when the session gets garbage collected, it calls back into all callbacks to let them know that the request is cancelled. Because the session is in destruction, this will crash gjs when it tries to find the object to pass to the callback.
A quick fix is to prevent the session from being garbage collected. Move it outside the function to get the correct behaviour.
const session = new Soup.SessionAsync(); // This makes the session work under a proxy. The funky syntax here // is required because of another libsoup quirk, where there's a gobject // property called 'add-feature', designed as a construct property for // C convenience. Soup.Session.prototype.add_feature.call(session, new Soup.ProxyResolverDefault()); function buttonPressed() { let message = Soup.form_request_new_for_hash('GET', 'http://example.com', {'beans': 'sauce'}); session.queue_message(message, function() { log('soup is so easy!'); }); }
And now, these aren’t required, but it helps me tremendously when reading extensions. Following these tips will likely get your extension approved faster.
Consistent and appropriate indentation
If you indent your code properly and consistently, I don’t have to figure out which brace corresponds to what. Makes it easier for me to read code. It also helps if you follow the GNOME Shell’s indentation and brace style (4-space indents, braces on the same line), but I don’t want to start a flamewar in the comments section. If you like another indentation and brace style, use it. The important thing is that you are consistent.
Removing dead and commented out code
If you know that some code you have is dead, remove it. If you’re using a modern VCS like git, you should be able to get it back at any point. Don’t leave a bunch of commented code in there for me to digest through.