As mentioned in the
main post, Greasemonkey 4 is changing to be compatible with the Browser Extension API. At its core, this set of APIs is completely asynchronous. Greasemonkey's old "
GM_" APIs are typically synchronous.
Why?
There exist other user script engines which have already done the work of bridging the gap. Greasemonkey has elected to move exclusively towards a more performant asynchronous model. Eventually in the future, such scripts will be faster.
What?
The
Greasespot Wiki has been updated to explain Greasemonkey 4 in detail. Here's a quick summary.
First, there is only an embedded editor. Browser Extensions have no access to the file system, so you can no longer author user scripts in your familiar text editor.
There is only one object provided to user scripts now, named
GM. It has several properties. One of them is
info – the equivalent of the old
GM_info. There are also several methods of this object:
getResourceUrl,
deleteValue/
getValue/
listValues/
setValue,
xmlHttpRequest.
To use these methods you still need
@grant, and use the new name, e.g.:
// @grant GM.setValue
The new form has a dot, where the old form has an underscore. You may specify both
@grants, if you'd like to be compatible with Greasemonkey 4 and other user script engines at the same time. As of today, there is
no support for:
GM_log (use console.log),
GM_addStyle,
GM_registerMenuCommand, nor
GM_getResourceText.
In general these methods work like their old counterparts, but their return values are
Promises. The async and await keywords make asynchronous promises easy to work with. For example:
// ==UserScript==
// @name GM set/get demo
// @grant GM.getValue
// @grant GM.setValue
// ==/UserScript==
(async function() {
console.log('Starting the get/set demo ...');
let i = await GM.getValue('i', 0);
console.log(`This time, i was ${i}.`);
GM.setValue('i', i+1);
})();
Here the
GM.getValue() method actually returns a promise, but the
await keyword transparently converts that to its resolved value, allowing us to write code just as if the value was directly returned – with neither callbacks nor promise resolution. See the documentation on
async and
await.
If you'd like your script to be compatible with Greasemonkey 4 and also Greasemonkey 3 (or other user script engines), we have provided a
polyfill, which makes new-style API calls work on top of older engines. To use it: 1) keep your old-API @grant line, 2) add a new-API @grant line, 4) require the polyfill script, 4) switch your code to use new-API style (and probably async/await). So the above example might look like:
// ==UserScript==
// @name GM set/get demo
// @require https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js// @grant GM.getValue
// @grant GM_getValue// @grant GM.setValue
// @grant GM_setValue
// ==/UserScript==
...
With the exact same new-API style code as above. Such a script can be installed in either Greasemonkey 4 or Greasemonkey 3 (or TamperMonkey, or ViolentMonkey, etc.) and work as intended.
unsafeWindow
Due to the more limited abilities that the new extension system gives us, we are currently unable to make
@grant none scripts work in the same way. Most importantly, they have a different connection to
unsafeWindow. For the short term at least, it's a good idea to adopt
cloneInto and exportFunction.
Naming
Do note that new style APIs have slightly different names. In short, a consistent naming style has been adopted: all words and acronyms get a consistent case. So what was before
GM_getResourceURL is now
GM.getResourceUrl. What was
GM_xmlhttpRequest is now
GM_xmlHttpRequest. In the near future the
Greasespot Wiki will be updated with all details of the new APIs, including exact names with case.
Feedback
We welcome feedback, reach out via the
greasemonkey-users discussion group. Please also keep in mind the volunteer nature of this open source project when doing so!