How to make GM_getValue existent in Greasemonkey on Firefox?

peterh picture peterh · Nov 24, 2017 · Viewed 8.2k times · Source

The dupe candidate is for previous GM versions. The problem is likely somewhere around the different scopes where the userscripts can run, as described here. However, as described here, this functionality is currently undocumented for Greasemonkey 4.0.

I have this Greasemonkey demo script:

// ==UserScript==
// @name         GM_getValue, GM_setValue don't work demo
// @version      0.2
// @author       You
// @include      /^https:\/\/stackoverflow.com/$/
// @grant        GM_getValue
// @grant        GM_setValue
// @run-at       document-end
// ==/UserScript==

console.log('script started');
var id = GM_getValue('testName', 0);
console.log('got ' + id);
id++;
GM_setValue('testName', id);

Calling this with the https://stackoverflow.com/, it is well visible that it is called.

However, I get this error on the console:

Script error:  
ReferenceError: GM_getValue is not defined
Stack trace:
userScript@user-script:demosrv/GM_getValue%2C%20GM_setValue%20don%27t%20work%20demo:372:5
scopeWrapper@user-script:demosrv/GM_getValue%2C%20GM_setValue%20don%27t%20work%20demo:381:9
@user-script:demosrv/GM_getValue%2C%20GM_setValue%20don%27t%20work%20demo:361:17

I've digged docs a lot, but it seems GM_{get,set}Value simply don't want to exist.

Why is it so? How to make it working?

I am using Firefox.

Answer

peterh picture peterh · Nov 24, 2017

GM_getValue and GM_setValue are now obsolete in the GreaseMonkey. The correct methods are GM.setValue and GM.getValue.

The GreaseMonkey documentation uses often the old API call names, which is a continuous error in it. Probably it wasn't correctly updated.

As the documentation here says, GM functions can run in different scopes. Unfortunately, I didn't find any info until now, which scopes are existing and how can we switch between them.

The old references manywhere on the net, using GM_getValue are all obsolete.

The important things:

  • While GM_getValue were functions with a return value, GM.getValue and GM.setValue return Promises.
  • You can use them nearly as you used in the old, nice versions, using the await call.

This example on the remote link, works:

// ==UserScript==
// @name        Greasemonkey set-and-get Example
// @description Stores and logs a counter of executions.
// @grant       GM.setValue
// @grant       GM.getValue
// ==/UserScript==

(async () => {
  let count_before = await GM.getValue('count', 0);

  // Note awaiting the set -- required so the next get sees this set.
  await GM.setValue('count', count_before + 1);

  // Get the value again, just to demonstrate order-of-operations.
  let count_after = await GM.getValue('count');

  console.log('Greasemonkey set-and-get Example has run', count_after, 'times');
})();

However, there is still no more clear documentation about the scopes and how can we interact with them.

It seems, there are at least two scopes:

  • In one of them, you can manipulate the DOM, but don't have access to the GM.* API.
  • In another, you have access to the GM.* API (and thus, you can make script-local persistent storage), but don't have access to the DOM.
  • Between the scopes, we can communicate through asychronous calls.
  • Why would this trouble increase the security, probably not even the GM developer(s) could say.

Thus, the way to develop new GreaseMonkey scripts for 4.0, is to start with their example script and then follow an incremental trial & error path.


Extension: another trouble I've found: the existence of await in the script seems to make greasemonkey to ignore the whole script et al. In the example, it doesn't seem to happen, but in more complex scripts, it does. I didn't debug it too deeply - I simply ignore await and use the Promises on the old way (GM.getValue("myValue").then(function(myValue) { ... });). It makes the code more crappy, but so is it.