Greasemonkey: Provide script author migration documentation

Created on 13 Sep 2017  路  16Comments  路  Source: greasemonkey/greasemonkey

We're switching to an async/promise API system. Provide some documentation/examples for script authors to use. See e.g.

https://github.com/mozilla/webextension-polyfill

The "Or, using an async function:" section especially. If we work just a bit, we can show how things can be done in a manner that's nearly as easy to code with.

All 16 comments

Since user script codes are actually wrapped in a async function, Providing a polyfill using @require don't work if the user script has await at the top level.

Maybe a polyfill like:

async function main (GM) {
  // UserScript code goes here...
  await GM.setValue('foobar', 123)
  let value = await GM.getValue('foobar') // -> 123
}

typeof GM === 'object' ? main(GM) : main({
  info: GM_info,
  getValue: function (key, def) {
    return Promise.resolve(GM_getValue(key, def))
  },
  setValue: function (key, value) {
    return Promise.resolve(GM_setValue(key, value))
  }
})

You need to make sure, that you wrap you whole code in an async function. Otherwise it will not work in legacy GM!

(async () => {

if (typeof GM === "undefined") {
    GM = new Object();
    GM.info = GM_info;
    GM.listValues = function() {
        return Promise.resolve(GM_listValues.apply(null, arguments));
    };
    GM.getValue = function() {
        return Promise.resolve(GM_getValue.apply(null, arguments));
    };
    GM.setValue = function() {
        return Promise.resolve(GM_setValue.apply(null, arguments));
    };
    GM.deleteValue = function() {
        return Promise.resolve(GM_deleteValue.apply(null, arguments));
    };
    GM.xmlhttpRequest = function() {
        return Promise.resolve(GM_xmlhttpRequest.apply(null, arguments));
    };
}

console.log(GM.info);
console.log(await GM.listValues());
})();

I just made my biggest script (about 5000 lines) backward compatibel with GM3 and can say: It is not nice.

The script uses a lot of functions and only some of them are using the GM API. So this functions have to be async. But if the calling function relies on the work of the called function, you need to await that function too, so you need to async the calling function...and so on.

Best would to be, to rewrite the whole script and just make it async as a whole. But this would be a pain in the ass...

A) I've started the polyfill script, if you'd like a preview:
https://arantius.com/misc/greasemonkey/imports/greasemonkey4-polyfill.js

B) Make sure to include in documentation case changes: GM_xmlhttpRequest becomes GM.xmlHttpRequest; GM_getResourceURL becomes GM.getResourceUrl; maybe others.

Will test the polyfill, but it only saves about ~30 lines of code. The bigger problem is to make an syncron script run with asyncron parts ^^

Polyfill does work, but:

a) GM.info is a function, but needs to be an array/object
b) I, personally, think it would be better, to reverse the polyfill: So you need to call GM.listValues() instead of GM_listValues(). It shows, that the script is ready for GM4.

@arantius I saw that GM_registerMenuCommand is replaced with HTML5 context menu. Is there any plan to add it in GM 4.x? I can't find the tracking issue.

I haven't test it, but it might be a good idea to not overwrite existing APIs

function GM_addStyle(...) {...}
// v.s.
if (!this.GM_addStyle) {
    this.GM_addStyle = function(...) {...};
}

is openInTab also excluded from gm4 ???

same as https://github.com/greasemonkey/greasemonkey/issues/2559#issuecomment-332227253 b) i would prefer to code in GM4 style as for now and for ever instead of sticking to the old API style

and same as https://github.com/greasemonkey/greasemonkey/issues/2559#issuecomment-332382317 i actually don't understand how that would work with gm4

and a bogus in the second comment in the file, there are 2 gm4, one should be "gm before 4" or something

I, personally, think it would be better, to reverse the polyfill: So you need to call GM.listValues() instead of GM_listValues().

I'm not sure what you mean, that (call new style APIs) is already the intended model. What the polyfill does is make new style API calls work in old-API script managers.

I saw that GM_registerMenuCommand is replaced with HTML5 context menu. Is there any plan to add it in GM 4.x?

No. Greasemonkey's policy has been (for a long time) not to implement "user space" features. Any script can do this, or @require something like this polyfill which does it. Greasemonkey doesn't need to build nor support this feature to enable scripts to use it. The point here is just to do what the polyfill does: make it easier to update scripts to be compatible with both old and new versions of Greasemonkey.

No. Greasemonkey's policy has been (for a long time) not to implement "user space" features. Any script can do this, or @require something like this polyfill which does it. Greasemonkey doesn't need to build nor support this feature to enable scripts to use it. The point here is just to do what the polyfill does: make it easier to update scripts to be compatible with both old and new versions of Greasemonkey.

I meant if GM4 will also provide an API to execute script commands from the UI of userscript manager like old GM_registerMenuCommand, not asking whether there will be a HTML5 contextmenu API in GM. GM_registerMenuCommand is often used to launch script configuration dialog, which shouldn't be polyfilled as a HTML5 contextmenu IMHO.

BTW, I have created a library to work with HTML contextmenu just few months ago, which would reuse contextmenu property on the page if it is already set:
https://github.com/eight04/GM_context

GM_registerMenuCommand is often used to launch script configuration dialog, which shouldn't be polyfilled as a HTML5 contextmenu IMHO

Why not?

There's more work to do, especially updating the wiki, but the blog post is enough progress for now ...

Why not?

  1. The command on the contextmenu should be:

    • An action which works on specified context (element). For example, when some text is selected, "Copy" command, which is an action to work with the selection, is shown.
    • A shortcut to execute specified command for convenience.

    A "My userscript setting" command doesn't fall into both categories. It does not depend on the context, also there is no need to use a shortcut for configuration.

  2. It is not reliable. It may be blocked/replaced by page scripts.

I agree with the conceptual level difficultires. I do not have any extensions anymore that use the context menu for settings. It would get pretty busy pretty quickly if they did, since I run a lot of extensions.

But the main reason I see is that Chrome has removed the HTML5 context menu from the spec, which means it's probably going to be removed from Firefox, too. I didn't even know it existed, seeing as I've not seen any site use it. Apps just spin their own, blocking the real context menu (which is a huge, huge annoyance in most cases. That removes functionality.)

Also, a question: will there be any way to inject at document start? Or is that now asyncronous too? Injection at document-start is the main reason I still treat Greasemonkey as my primary development engine (even though Chrome is my main browser now). I hate pop-in.

Also, a question: will there be any way to inject at document start?

2526

I pushed my first FF57 userscript with downward compatibility to upstream today.

It was quite some work to get everything to run, but I think I got it.

You might want to take a look at it: http://wod.zerosgaming.de/scripts/mm_skills/mm_skills_0_23.user.js

Was this page helpful?
0 / 5 - 0 ratings

Related issues

the8472 picture the8472  路  36Comments

erikvold picture erikvold  路  26Comments

GuardianMajor picture GuardianMajor  路  11Comments

Rick-74 picture Rick-74  路  29Comments

obskyr picture obskyr  路  14Comments