Why does the chrome.userScripts API support only one use case?

469 views
Skip to first unread message

AutoControl support

unread,
Jan 15, 2024, 6:31:30 AM1/15/24
to Chromium Extensions
During the past few years in this forum developers have given feedback about the use cases of user scripts. It became clear after those discussions that user scripts have two main use cases.

Use case 1:
The user wants to inject a script whenever a given URL is loaded.
That is, the parameter that determines the injection is a URL pattern.

Use case 2:
The user wants to inject a script on the page currently loaded in a given tab.
That is, the parameter that determines the injection is a tab ID.

Our extension, AutoControl, happens to require use case 2. We were about to start implementing our port to MV3 but, after reading the documentation for the new chrome.userScripts API, we realized that this API only supports 1 use case.

We assumed the Extensions Team had all the necessary feedback to implement user scripts in MV3 given how long it took for this functionality to be added. You would agree that there's no time for even more feedback given that the MV2 end-of-life is just a few months away. MV3 should be feature-complete by now.

Our users rely on the ability to inject their own scripts on any tab at any given moment. Without "Use case 2" our extension cannot be fully ported to MV3. Our users will be left stranded.

May we ask, please, why was only one use case implemented in the userScripts API?
Is this intentional or is it an oversight?
If it's intentional, can you please explain what the problem is with "Use case 2"?

Oliver Dunk

unread,
Jan 15, 2024, 6:58:42 AM1/15/24
to AutoControl support, Chromium Extensions
Hi,

Thanks for the question! We're definitely aware of both use cases, and actually mentioned the latter in the proposal we put together when working with other browsers and the community in the Web Extensions Community Group.

What we have at the moment is just the first version. It isn't complete and we plan to add more, but we do believe it should be possible to implement all of the functionality that extensions require. In this case, our suggestion would be to always inject scripts, but to add a wrapper around the user script code that prevents it from executing immediately. You can then use a message to actually fire the code when needed.

This does have a cost of course, but we have decided to accept that tradeoff, at least in this first version. We plan to continue working on the API in 2024 so it's definitely possible we'll have a better path in the future - but using the workaround would be the suggested approach for now.

I hope that helps :)
Oliver Dunk | DevRel, Chrome Extensions | https://developer.chrome.com/ | London, GB


--
You received this message because you are subscribed to the Google Groups "Chromium Extensions" group.
To unsubscribe from this group and stop receiving emails from it, send an email to chromium-extens...@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/chromium-extensions/063f7d94-a9f4-42dc-a0f8-27474e4b0595n%40chromium.org.

wOxxOm

unread,
Jan 15, 2024, 10:02:25 AM1/15/24
to Chromium Extensions, Oliver Dunk, Chromium Extensions, AutoControl support
>  This does have a cost of course

Like turning a warning-less extension based on activeTab into one that requires access to all_urls and 1) possibly adds tons of code into each tab on document_start slowing the page loads and/or browsing or 2) resorts to relaxing the CSP in the USERSCRIPT world to use messaging + eval(). Finding this acceptable seems entirely unreasonable.

AutoControl support

unread,
Jan 15, 2024, 10:58:59 AM1/15/24
to Chromium Extensions, Oliver Dunk, Chromium Extensions, AutoControl support
Hi Oliver. Thanks for the prompt reply.

Unfortunately, the suggested workaround is not enough. That will work only for pages that were loaded after the extension registers the user script on <all_urls>. It won't work on pages that were loaded before the extension was installed or while the extension was disabled.
Many of our users have the habit of keeping the extension disabled and only when they need to execute an action they enable it temporarily.
The only way for the suggested workaround to work would be to reload the page or pages the user wants to work with. This is not acceptable as pages usually have a complex state that is either lost on reload or takes too long to rebuild, such as games or single page websites like Gmail, Facebook or Twitter.
We can't ask our users to reload the page when they are in the middle of writing an email or having a video chat or playing a video game.

Is this really the trade-off the Extensions Team had in mind?
i.e. avoid providing proper support for "Use case 2" at the expense of forcing extension developers to implement a workaround that doesn't really work.
What is the benefit here?
A trade-off is supposed to have a benefit and a drawback. The drawback is quite clear, i.e. there's no reliable way of implementing "Use case 2".
What would be the benefit?

Oliver Dunk

unread,
Jan 15, 2024, 12:15:24 PM1/15/24
to AutoControl support, Chromium Extensions
Thanks for the extra context. In that case, my suggestion would be to advise those users to leave the extension enabled. We're continuing to work on making sure extensions only have access to data when needed, and if users trust your extension, they should feel comfortable leaving it running.

One example of the trade-offs here is making the ecosystem safer for the majority of users while still allowing user script extensions to migrate. Running arbitrary code is a very powerful capability - and we've certainly seen abuse as a result. While removal of the MV2 APIs for executing code does slightly limit things in the short term, it means that for most users we can be much more confident about what the extensions they run are doing.

As I mentioned we're definitely not against adding a way to execute code one time, it just hasn't been implemented quite yet. I apologise for the extra user education this might require on your side but hopefully we can provide more flexibility soon.

As a final note - you might also like to look at the chrome.debugger API. This shows a high visibility banner when running, since it's another dangerous capability, but would allow you to inject code at any time. You might decide that would be preferred by your users.
Oliver Dunk | DevRel, Chrome Extensions | https://developer.chrome.com/ | London, GB

wOxxOm

unread,
Jan 15, 2024, 1:26:51 PM1/15/24
to Chromium Extensions, Oliver Dunk, Chromium Extensions, AutoControl support
> We're continuing to work on making sure extensions only have access to data when needed, and if users trust your extension, they should feel comfortable leaving it running.

When people don't trust an extension they remove it. As for disabling, it's usually done for other reasons e.g. if it's rarely used, like once a month/year.

> While removal of the MV2 APIs for executing code does slightly limit things in the short term, it means that for most users we can be much more confident about what the extensions they run are doing.

This is a glamorous way of saying that your team sacrificed the convenience of users of such extensions as this one. Security too, since now they would have to grant the extension extra broad host permissions. And performance, as a consequence.

> I apologise for the extra user education this might require on your side

The problem is not a probable need in education, but the definite inconvenience and lack of any benefits for the users of such extensions.

> the chrome.debugger API

This is a huge escalation of privilege for any extension, so many/most users won't consider even trying it, and many/most of those who do would abandon it due to the constant warning at the top. Some might even resort to silencing the warning, which would make them more vulnerable to malware extensions unless they also use blocked_permissions policy to forbid the debugger permissions for all extensions by default.

> hopefully we can provide more flexibility soon

Yay, let's cheer and hope! OTOH, one might have expected here an estimated date or at least an officially issued assurance that a better solution/workaround would be available by the time MV2 is removed from the web store.

AutoControl support

unread,
Jan 15, 2024, 3:03:05 PM1/15/24
to Chromium Extensions, Oliver Dunk, Chromium Extensions, AutoControl support
if users trust your extension, they should feel comfortable leaving it running.

Users disable the extension to avoid consuming system resources unnecessarily. Our extension in particular runs a persistent background page as well as a native messaging host. Those are two processes that even though they consume very little, some users still prefer to keep it disabled because of basic common sense. Why keep a program running if your are not using it.
Running a native host means the extension has full access to the entire computer, so trust is definitely not an issue in our case. Our users have learned to trust the extension based on years of good reputation.
So, it's not reasonable that we ask users to keep the extension running permanently if they are using it sparsely.

Additionally, the suggested workaround for achieving "Use case 2" requires injecting a script into every single page the user visits. This goes against one of the key advantages of being a native extension in our case, which is that we don't need to inject code into every page in order to provide unrestricted keyboard and mouse shortcuts.
Here's a quote directly from the extension's description:
"AutoControl doesn't inject code into every page you visit, thus leaving their functionality intact, which avoids sluggish pages and conserves CPU and memory."
AutoControl inject code only when the user executes an action that requires doing so. If we had to inject code over an over again into every page, then one of the key advantages of using a native host would vanished.


you might also like to look at the chrome.debugger API.

Unfortunately, we cannot use that API since it requires a non-optional permission. We cannot force all users to accept such a powerful permission without a good reason.
If the "debugger" permission was optional we could consider the possibility, but unfortunately it's not.


One example of the trade-offs here is making the ecosystem safer for the majority of users

You already suggested a workaround that allows arbitrary code injection at any moment. So, how is the ecosystem safer by not supporting this use case in a proper way?

Oliver Dunk

unread,
Jan 16, 2024, 7:00:51 AM1/16/24
to AutoControl support, Chromium Extensions
AutoControl inject code only when the user executes an action that requires doing so. If we had to inject code over an over again into every page, then one of the key advantages of using a native host would vanished.

Have you tried injecting on each page, and if not, could I suggest that you give it a go? While there is of course some performance impact I don't agree that it should cause a page to become sluggish, especially if you are only injecting a small amount of code. There is still a benefit to using a native host in that you can listen to a wider range of shortcuts.

You already suggested a workaround that allows arbitrary code injection at any moment. So, how is the ecosystem safer by not supporting this use case in a proper way?

Many extensions have host permissions for a site, which meant that in MV2 many extensions had the ability to run arbitrary code. While there are still ways to do this, they require additional permissions and in some cases developer mode. We still have some gaps to close but it is now much easier for us to spot an extension which is running untrusted code unexpectedly.
Oliver Dunk | DevRel, Chrome Extensions | https://developer.chrome.com/ | London, GB

wOxxOm

unread,
Jan 16, 2024, 7:23:16 AM1/16/24
to Chromium Extensions, Oliver Dunk, Chromium Extensions, AutoControl support
>   Have you tried injecting on each page

Even an empty content script in the isolated or userscript world is already consuming a considerable amount of memory (~300kB) for the world's JS globals/prototypes, so when the user has dozens or hundreds of tabs it becomes a problem. Multiply it by the amount of installed productivity extensions that have to use this terrible workaround.

> While there is of course some performance impact I don't agree that it should cause a page to become sluggish, especially if you are only injecting a small amount of code

You are dismissing the inconvenient case of complex/big code that takes a noticeable amount of time just to be parsed by V8 (it may be even a second) and since code compilation cache is intentionally disabled for extensions it happens every time any page is navigated, thousands of times a day, possibly, which also has an impact on power consumption and battery life.

> While there are still ways to do this, they require additional permissions and in some cases developer mode. We still have some gaps to close but it is now much easier for us to spot an extension which is running untrusted code unexpectedly.

MV3 extensions can still run arbitrary code in the MAIN world of a web page by adding a script element or adding an inline attribute like onreset or onclick, which doesn't require any special permissions on top of host access, and it's not a gap to close since you intentionally allow per the MV3 design document. Of course this won't work in sites that use a strict CSP, but these are rare.

wOxxOm

unread,
Jan 16, 2024, 7:28:06 AM1/16/24
to Chromium Extensions, wOxxOm, Oliver Dunk, Chromium Extensions, AutoControl support
> so when the user has dozens or hundreds of tabs it becomes a problem

Iframes too, many sites display ads, so there's often at least one iframe on a page, consuming the memory/CPU for the additional content script's world and code.

AutoControl support

unread,
Jan 17, 2024, 11:00:38 AM1/17/24
to Oliver Dunk, Chromium Extensions
I don't agree that it should cause a page to become sluggish

One page will not become sluggish. The entire browsing experience starts to become sluggish the more tabs you open. Some of our users work with multiple windows with hundreds of tabs on each window (no exageration, we've seen this multiple times). Our extension does not overload the browser regardless of how many tabs the user opens. This is a key feature of our extension and our users will not be happy to know that the new MV3 version will start injecting a script on every single tab (plus all iframes on each tab), which most of the time will be a waste of CPU and memory.

 
they require developer mode.

Requiring developer mode for the userScripts API is really a bad solution. User permissions should be granted per extension and not globally to all extensions. Developer mode is a single global checkbox which has a completely different purpose. Users have learned for more than a decade that this checkbox does nothing more than displaying a few extra info and some buttons.
We, for example, have been recommending our users for years to enable developer mode because that gives them access to the "Update" button which allows them to force all extensions to update immediately instead of waiting for the browser to update at some arbitrary moment in the future.
Turning this checkbox suddenly into a user authorization mechanism is disastrous.

Please consider replacing the developer mode requirement with a "userScripts" permission which users must grant per extension and not globally.
And, please, make that permission optional (unlike the "debugger" permission which is non-optional). Forcing users to approve a permission unnecessarily is not acceptable.

 
it is now much easier for us to spot an extension which is running untrusted code unexpectedly.

In order to properly support "Use case 2", all that's need is the following API function:

chrome.userScripts.executeScript(tabId, {code: "..."})

Is this more difficult to spot than the workaround that you suggested?

Oliver Dunk

unread,
Jan 18, 2024, 8:48:58 AM1/18/24
to AutoControl support, Chromium Extensions
I think we're talking past each other a bit so I'm not sure if we're going to make any more progress here. I definitely appreciate the feedback though, and I'm passing it along - I see many of your points and hope we can get this method as mentioned, but this is ultimately a decision for the team as a whole.

In order to properly support "Use case 2", all that's need is the following API function:

chrome.userScripts.executeScript(tabId, {code: "..."})

Is this more difficult to spot than the workaround that you suggested?

Sorry, to clarify here - I was just pointing out that both the workaround and a userScripts.executeScript are easier to spot abuse of than a tabs.executeScript which many extensions have access to. Certainly prefer the method to the workaround :)

Oliver Dunk | DevRel, Chrome Extensions | https://developer.chrome.com/ | London, GB

AutoControl support

unread,
Jan 18, 2024, 11:58:07 AM1/18/24
to Chromium Extensions, Oliver Dunk, Chromium Extensions, AutoControl support
 
both the workaround and a userScripts.executeScript are easier to spot abuse of
Certainly prefer the method to the workaround :)

It seems there was a misunderstanding because now we agree perfectly. The message we were trying to communicate in this conversation is that the userScripts API should support "Use case 2" in a proper way instead of having to resort to an unreliable and wasteful workaround.
The executeScript() function and a "userScripts" permission to guard the API (instead of the global "developer mode") would be valuable additions. Fingers crossed.

Thanks for your time.

Emilia Paz

unread,
Feb 7, 2024, 4:27:13 PM2/7/24
to Chromium Extensions, AutoControl support, Oliver Dunk, Chromium Extensions
Just wanted to share that there is a proposal for userScripts.execute() in WECG. Any comments can be made on the pull request
Reply all
Reply to author
Forward
0 new messages