Greasemonkey: WebExt: Support all GM APIs

Created on 3 Mar 2017  ·  19Comments  ·  Source: greasemonkey/greasemonkey

https://wiki.greasespot.net/Greasemonkey_Manual:API

The Greasemonkey APIs are generally privileged actions, and generally all synchronous actions. Content scripts have (almost) no API access, and thus must pass messages to perform privileged actions. Web extensions only get asynchronous message passing (citation needed).

So each method has its own challenges.

Easier:

  • GM_getResourceURL must produce a result synchronously, but is trivial to calculate.
  • GM_addStyle is trivial.
  • GM_log should probably be retired, or just mapped to console.log.
  • GM_openInTab functionally produces no result, even ordering is not critical.
  • GM_registerMenuCommand has no synchronous behaviors.
  • GM_setClipboard produces no result.
  • GM_xmlhttpRequest is fully asynchronous.

Harder:

  • GM_deleteValue produces no result. Ordering still matters (i.e. delete of X must happen before any future set of X).
  • GM_setValue is equivalent to delete. No synchronous result, but ordering matters.

Very hard:

  • GM_getValue must produce a result synchronously.
  • GM_listValues must produce a result synchronously. (Plus AFAICT storage gives no good backing API. The only option is to fetch one value by name, or fetch all names and values. With no way to even segregate e.g. by script.)
  • GM_getResourceText must produce a result synchronously, and they may be very large values. (I.e. pre-caching them all in memory is likely too expensive.)

Most helpful comment

In Tampermonkey, GM_addStyle has a special ability to inject style which can bypass CSP restriction (in case inline <style> is forbidden). I'll be glad if we can have this feature in Greasemonkey too.

All 19 comments

bug1323433 and bug1332273 may be of interest here.

Thanks for the pointers, I agree those are both super useful.

Oh, maybe some amount of synchronous message passing is possible!

https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/runtime/onMessage#Sending_a_synchronous_response

Test this. Does this make it possible to use a simple synchronous implementation (backed by background-only APIs) for those methods called out above?

That returns a promise on the content side, so it is effectively async. i think "synchronous" is a misnomer here, it's more like pairing a message from child to parent with a response so that one does not have to manually keep track of pending responses.

Afaik the only synchronous API available are sync XHRs which then could be intercepted with webrequest or something like that.

Sync XHR not work properly in content script: https://bugzilla.mozilla.org/show_bug.cgi?id=1360968, so at now we don't have any sync channel from content > background.

Looks like GM_setValue and GM_getValue was designed as sync operation and in single procecss browser they works just fine when we operate in multiple tabs, but in e10s no (https://github.com/greasemonkey/greasemonkey/issues/2427). With the old extenstion API it can be easily repaired, but in WebExt not. Without sync message (even for small data, just only emit short value) we can't properly sync value beetwen more tabs. at some point this will always be a race condition.

Regardless how you solve the above issues in WebExt version we should get new set/get method designed in async manner, so where it's important we can write the script in a async way. Documentations should have some information about lack GM_setValue and GM_getValue behaviour when operate on multiple tabs.

imo, it is rare use cases that using GM_setValue / GM_getValue on multiple tabs and the order must be followed. as a result, storage a copy (cache) of _GM_values_ in content script, read always from cache, write back async, and update cache with events triggered by background script should be an acceptable solution.


btw, if so, add an API equivalent to GM_addValueChangeListener is great if possible. (and i dislike the name addValueChangeListener something else should be better maybe

In my dev branch there is support for:

  • GM.getResourceURL
  • GM.deleteValue, GM.getValue, GM.listValues, GM.setValue

I plan to never add:

  • GM_log
  • GM_addStyle

We still need:

  • GM_xmlhttpRequest

I plan to delay (or maybe drop):

  • GM_registerMenuCommand (this one has always been a huge support cost)
  • GM_getResourceText

Which means progress here is actually quite close.

Good feedback at above commit, don't forget.

I just have a try on the new add-on. It seems that cross domain xhr request had already been enabled without GM_xhr features. Is this indeed behavior?

Just try a userscript grant none with following codes:

fetch(prompt()).then(resp => resp.text()).then(text => alert(text)).catch(error => alert(error));

Ugh, confirmed. We're currently executing user scripts as "content scripts" -- of the extension, with all the extension's permissions.

I've looked into this a bit but so far I'm in the dark as to how to execute unprivileged ("web scope" -- what do we call this now that "content" scope is ambiguous?!) code. The closest thing I can find is all about creating script tags, which can/will be blocked by the page's CSP, which I definitely don't want.

Currently the only way to modify CSP is intercepting and modifying the headers for every Document request. There are open issues to exempt webextensions from content CSPs but there doesn't seem to be much activity on those.

Besides injecting <script> tags window.eval should also run in the page scope, I think.

The official terms are "content script scope" vs. "page scope".

Another issue is that they would need string-processing to wrap them in a separate scope that could define the GM APIs.

I have also filed a bug for Sandbox equivalents in webextensions, but it's not high priority either.

AIUI both script and eval are vulnerable to blockage by CSP. But I have confirmed that eval drops privileges.

https://bugzilla.mozilla.org/show_bug.cgi?id=1391669

We must have <all_urls> if we're going to potentially run a content script at any page. If we ask for that, then our content script gets that, and thus can XHR to anywhere.

At least in Firefox ( https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Content_scripts#Using_eval()_in_content_scripts ) we can drop down to page scope, but then we're truly page scope and we can't safely expose APIs to the script without exposing it to anything/everything running in the page (AFAIK), which is even worse.

Can fetch and xhr be overwritten in the content script?
Maybe provide a modified fetch that does its own CORS check.

  • GM.xmlhttpRequest() was added in 60a50d05b1e565571d8a3e638b0683a1a9c2beaa
  • GM.getResourceText() will be covered in #2548
  • GM.registerMenuCommand() is intentionally being skipped.
  • Every script inheriting cross-origin-fetch (as per comment above) is #2549

@the8472 See my implementation of withUnsafeWindow() in https://github.com/greasemonkey/greasemonkey/issues/2232#issuecomment-326841025

It mimics the behavior of the old with (object) ... function, just a little bit safer. (Need modern browsers.)

@arantius wrote on Jul 25:

I plan to never add:

GM_log
GM_addStyle

These were denoted in the creation of this ticket (by @arantius on Mar 3) as trivial (assuming a mapping of GM_log to console.log).

In my experience, these are two of the most commonly used API calls. Wouldn't abandoning them break a lot of old scripts for no reason? Or were you exclusively referring to the dev branch?

In Tampermonkey, GM_addStyle has a special ability to inject style which can bypass CSP restriction (in case inline <style> is forbidden). I'll be glad if we can have this feature in Greasemonkey too.

Was this page helpful?
0 / 5 - 0 ratings