Open Bug 1516478 Opened 5 years ago Updated 3 months ago

implement tabs.executeUserScript() or allow injecting user scripts in certain tabs

Categories

(WebExtensions :: General, enhancement, P3)

64 Branch
enhancement

Tracking

(Not tracked)

UNCONFIRMED

People

(Reporter: robbendebiene, Unassigned)

References

Details

(Whiteboard: [wecg])

User Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:64.0) Gecko/20100101 Firefox/64.0

Steps to reproduce:

I want to run custom user scripts in a specific tab on certain actions. Currently the only way to do this is using tabs.executeScript() which has several problems.

My use case: Run a user scripts on a specified mouse gesture (Gesturefy: https://addons.mozilla.org/firefox/addon/gesturefy/)


Actual results:

Problems with tabs.executeScript():
- user scripts are not isolated from the extensions content script code
- user scripts are not isolated from each other
- user scripts have access to webextension APIs
(possible workarounds with eval or script tags could be by bypassed by "malicious" scripts)


Expected results:

Since there is a new user scripts API on the way (https://bugzilla.mozilla.org/show_bug.cgi?id=1437098) I would love to see an option to inject a user script in a specific tab in addition to an URL/match pattern.
Depends on: 1514809
Priority: -- → P3
Blocks: 1595853
See Also: → 1458947

This is interesting for Firemonkey, too (without this it's not possible for a script manager to deactivate all scripts for just a tab temporarily):
https://github.com/erosman/support/issues/138#issuecomment-582540978

I might be overconfident with this, but I would like to give this a try in the near future.
The question is, would this functionality be accepted and what is the best way to integrate it?

Besides tabs.executeUserScript() I could also imagine a tabIdproperty for the userScripts.register method.

const registeredUserScript = await browser.userScripts.register({
  tabId: 3
});
Flags: needinfo?(rob)
Flags: needinfo?(lgreco)

I'll defer to Luca on whether this is something that we'd accept.

The API has a design flaw (along with the contentScripts API) where the script is unregistered upon unload of the background script (generally, the script that calls the API). We may redesign the API, and as a result this bug is probably not a good one to work on.

Flags: needinfo?(rob)

As Rob anticipated in comment 3, we expect these two APIs (userScripts and contentScripts) to be soon going through some redesign (related to the support for manifest_version: 3 extensions).

We (Rob and I) did briefly discussed about this issue with the rest of the team today and we all agree that it would be better to defer expanding the API (until the next quarter, at that point we should have already done the other necessary re-design and be a bit more free to evaluate enhancements to these APIs).


Nevertheless, we are quite happy about your contributions to the WebExtensions internals and we would really love to guide you on contributing this enhancement (once we don't have potentially conflicting changes that may make your experience less enjoyable and potentially blocking your contribution for too long) and so I didn't want to close this comment without also providing you my perspective about how we could expose this enhancement through the extension API (and so the notes that follows are not about the implementation details yet, but more about ideas around the API design part).

API Design ideas and notes

  • registering a (user or content) script that does only match some particular tab ids (and I guess that something similar for window ids or user context ids may also make sense) looks like an extended matching capability (and so it may also fit into Bug 1445909 enhancement request)

  • when content scripts and/or user scripts are registered, they are being sent to all the web child processes, and there is no 1-1 mapping between tab and child process (and that is the case right now with e10s and it will also be the case in the future with Fission):

    • instead of registering a script (user or content) for a particular tab we may instead evaluating of extending the API exposed by the script objects that these two APIs return (which currently does only provide a single script.unregister method) to allow the extension to change the matching properties (and maybe also other ones), something like:
const script = await browser.userScripts.register({...});
...
script.update({ includeTabIds: [...], ... })
  • for an imperative way of injecting a userScript (instead of declarative as in rcontentScripts/userScripts.register), I was originally thinking to one of the following two alternatives (not something I discussed with the rest of the team to reach an agreement yet, just part of my own ideas about this particular enhancement request):
// Another method exposed on the script API object (this would allow the same script to be used to inject
// the same user script in other tabs).
const script = await browser.userScripts.register({... /* a script without any matching options yet */});
script.execute({tabIds: ...}); 

// or another tabs API method (which wouldn't allow to re-inject the same script again in some other tabs,
// we would be technically creating a new one):
browser.tabs.executeUserScript([optional tabId], {... /* user scripts options */});
Flags: needinfo?(lgreco)

Re: API Design ideas and notes
Few ideas for consideration (that have been discussed previously)

  • Provide a per tab (tabId) register and unregister method to inject|nuke contentScript|userScript without a need to refresh the tab.
    The APIs already have the method but they are not acessible to the extension.
  • Add a break|abort method to userScripts.onBeforeScript to prevent userScript injection
  • Add Add cssOrigin to contentScripts API (Bug 1679997)
  • Add a standard method for some forced workarounds in userScripts.onBeforeScript (e.g. process callback to defined functions)

💡It may be feasible to merge the 2 API into a single API browser.contentScripts.register and instead add a flag, if it makes it easier to maintain in the long run. e.g.

browser.contentScripts.register({
  matches: [hosts],
  js: [{code}],
  userScript: true,
  runAt: 'document_idle'
});

Also ....

browser.tabs.executeScript([optional tabId], {
  file: "/content-script.js",
  userScript: true,
  allFrames: true
});

All right, thanks for the detailed responses!
I'll wait for your starting signal :)

See Also: → 1587883

re Comment 5
I think the following would be better

browser.contentScripts.register({
  matches: [hosts],
  js: [{code}],
  context: 'userScript', // defaults to 'content'
  runAt: 'document_idle'
});

Please don't forget this issue. Users are waiting for a solution in multiple extensions that are currently blocked by Firefox.

Further consideration in future development of the API

A combined contentScripts/userScripts API would reduce browser load if both CSS & JS need to be injected.

Currently, a single contentScripts registration is required to inject CSS & JS.

browser.contentScripts.register({
  matches: [hosts],
  css: [{code: css}],
  js: [{code: js}],
});

However if the target is userScripts, 2 almost identical registrations/processes have to be created.

browser.contentScripts.register({
  matches: [hosts],
  css: [{code: css}]
});

browser.userScripts.register.register({
  matches: [hosts],
  js: [{code: js}]
});
Severity: normal → --
Severity: -- → N/A

Moving this enhacement request to a seealso for Bug 1875475, the userScripts API this bug was originally reported for is deprecated in MV3 and meant to be replaced by the API tracked by Bug 1875475.

Based on a quick look to the Google Chrome API reference for the MV3 userScripts API, it doesn't seem this enhancement request is already covered by the API as is, and so instead of wontfixing it (along with Bug 1595853) I'm linking it as a seealso to the bug tracking the new API to be considered for the new MV3 API.

No longer blocks: 1595853
See Also: → 1875475

The ability to execute on demand in the new userScripts API is covered by the feature request at https://github.com/w3c/webextensions/issues/477

Whiteboard: [wecg]
You need to log in before you can comment on or make changes to this bug.