Groups keyboard shortcuts have been updated
Dismiss
See shortcuts

Allow string script in chrome.scripting.executeScript()

1,399 views
Skip to first unread message

Liang Cui

unread,
Apr 8, 2024, 7:38:27 AM4/8/24
to Chromium Extensions

Dear Chrome Developers,

Hello.

I have a highly popular extension, Quicker Connector, with a substantial user base. Its primary function is to integrate with a PC-side automation tool (a software named Quicker) to allow the insertion of control steps over web pages or the browser itself into user-defined "actions" (which can be understood as scripts).

Recently, I've seen the upcoming mandate to transition to MV3, so I took some time to learn about the MV3 API. I've discovered that a very important feature, widely used by users, cannot be implemented in MV3: executing custom scripts on the current tab (or a tab with a specified id) and returning data.

Previously, this was primarily achieved through chrome.tabs.executeScript(). I understand that, for the sake of protecting user privacy, MV3 will no longer support this method and will require the use of the chrome.scripting API instead. However, chrome.scripting does not allow the execution of user-defined scripts. Yet, chrome.userScripts, when developer mode is enabled, does allow the execution of user-defined scripts.

Based on my current understanding, userScripts has 2 insurmountable limitations: 1) It cannot execute scripts on a specified tab immediately. 2) It cannot return data to the caller.

Since users' needs are highly diverse, running custom scripts is often essential. These scripts, provided by the users themselves, are typically used for retrieving web page elements information or for automating control over web page elements. As everyone's needs are different, it's difficult to create pre-defined functions.

I hope developers will consider, consistent with the userScript API, allowing chrome.scripting to execute user-defined scripts specified in text form when developer mode is enabled and users have provided permission authorization. This would achieve the following objectives: 1) Run scripts on the current or specified tabs. 2) Return data.

This requirement is extremely important for users, Thank you!





====
尊敬的chrome开发者,
你们好。

我有一个具有大量用户的扩展 [Quicker Connector](https://chromewebstore.google.com/detail/quicker-connector/klggbkjfmbonefdcfkiidhcmfjdfnepa?hl=zh-CN )。 它的主要功能是与PC端的自动化工具(一个名为Quicker的软件)打通,允许在用户自定义的“动作”(可以理解为脚本)中插入对网页或浏览器本身的控制步骤。

最近我看到了即将强制要求转换为MV3的消息,因此对MV3的API进行了一点学习。
我发现将会有一个非常重要的、被用户广泛使用的功能无法在MV3中实现:对当前标签页(或指定id的标签页)执行自定义的脚本(并返回数据)。

此功能之前主要是通过chrome.tabs.executeScript()实现。我理解,基于对用户隐私保护的考虑,MV3 不再支持此方法,而必须使用chrome.scripting API。但是chrome.scripting不允许执行用户自定义的脚本。但同时,chrome.userScripts 在开启开发者模式的情况下,允许执行用户自定义的脚本。

根据我目前的学习,userScripts 有2个无法解决的限制:1)无法对指定的标签页立即运行脚本。 2)无法向调用方返回数据。

因为用户的需求千变万化,运行自定义脚本在很多情况下是非常需要的。这些脚本由用户自己提供,通常用于获取网页元素信息,或者自动化控制网页元素。 由于每个人的需求不同,很难做成预先定义的函数。

希望开发者考虑,与userScript API 一致,在开启开发者模式,以及用户提供权限授权的情况下,允许chrome.scripting执行通过文本方式指定的用户自定义脚本,以实现如下目标:1)对当前或指定标签页运行脚本。 2)返回数据。

这个需求对用户非常重要,谢谢!

Oliver Dunk

unread,
Apr 8, 2024, 8:01:27 AM4/8/24
to Liang Cui, Chromium Extensions
Hi Liang,

Thanks for reaching out. This is definitely functionality we want to extend the User Scripts API with. In fact, we have been working on a proposal in the Web Extensions Community Group for a userScripts.execute method: https://github.com/w3c/webextensions/blob/main/proposals/user-scripts-execute-api.md. It allows one-time injection at an arbitrary time and returns a result from the script.

This is in a good state now and it is something we are actively working on implementing.

I don't have a release date to share I'm afraid, but we definitely see the use case, and want to try and get this out soon. We'll likely post here when this is ready but https://developer.chrome.com/docs/extensions/whats-new is also a good place to watch.
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/7b13d12d-e229-4da3-ba0e-c5e29c3b08f1n%40chromium.org.
Message has been deleted
Message has been deleted
Message has been deleted
Message has been deleted

Devashish Salgaonkar

unread,
Aug 14, 2024, 2:52:02 AM8/14/24
to Chromium Extensions, Oliver Dunk, Chromium Extensions, Liang Cui
Hello Oliver - Just checking if we are close to releasing this?

Oliver Dunk

unread,
Aug 14, 2024, 2:55:03 AM8/14/24
to Devashish Salgaonkar, Chromium Extensions, Liang Cui
Hi Devashish,

Unfortunately this has been pushed back a little based on some other priorities, and isn't currently being worked on.

I do still know how needed it is, and will make sure it isn't forgotten on our side. Hopefully we can still finish implementation in the near future.
Oliver Dunk | DevRel, Chrome Extensions | https://developer.chrome.com/ | London, GB

hrg...@gmail.com

unread,
Aug 14, 2024, 3:08:57 AM8/14/24
to Chromium Extensions, Oliver Dunk, Chromium Extensions, Liang Cui, Devashish Salgaonkar
Hopefully the proposal #540 is implemented before Chrome removes MV2 support, otherwise many users will be forced to keep using an outdated browser or to switch to a different Chromium browser that keeps MV2 support.

Oliver Dunk

unread,
Aug 14, 2024, 3:11:18 AM8/14/24
to hrg...@gmail.com, Chromium Extensions, Liang Cui, Devashish Salgaonkar
It should be possible to work around this by injecting a wrapped script that waits for a message / event before running. I'd be happy to share some example code if that would be useful.

It definitely isn't the most elegant, which I completely understand, and may have a minor performance impact. We don't expect this to be significant though and are comfortable with extensions taking that approach for the time being.

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

Vibhash Chandra

unread,
Aug 14, 2024, 3:22:50 AM8/14/24
to Oliver Dunk, hrg...@gmail.com, Chromium Extensions, Liang Cui, Devashish Salgaonkar
It will be great if you could share some examples.

We are looking for a workaround for this limitation.

Regards,
Vibhash

hrg...@gmail.com

unread,
Aug 14, 2024, 5:31:58 AM8/14/24
to Chromium Extensions, Vibhash Chandra, hrg...@gmail.com, Chromium Extensions, Liang Cui, Devashish Salgaonkar, Oliver Dunk
I think this was discussed before somewhere. There's no workaround for the lack of chrome.userScripts.execute(). An MV3 extension cannot inject code in pages that are already loaded. The only solution is to reload every single tab the user has open. This is unacceptable because pages are quite often complex programs (such as video games) with internal state that's either lost when the page is reloaded or it's time consuming to get back to. It's no different than asking the user to restart the whole browser.

Vibhash Chandra

unread,
Aug 14, 2024, 5:36:31 AM8/14/24
to hrg...@gmail.com, Chromium Extensions, Liang Cui, Devashish Salgaonkar, Oliver Dunk
In my case, MV2 extension was creating a window and injecting string script in that window.

Is there a way to achieve that in MV3?

Regards,
Vibhash

hrg...@gmail.com

unread,
Aug 14, 2024, 5:44:07 AM8/14/24
to Chromium Extensions, Vibhash Chandra, Chromium Extensions, Liang Cui, Devashish Salgaonkar, Oliver Dunk, hrg...@gmail.com
In my case, MV2 extension was creating a window and injecting string script in that window.
Is there a way to achieve that in MV3?

Yes. Use chrome.userScripts.register() to register the script string right before creating the window. Then you can unregister the script with chrome.userScripts.unregister()

 

Mou Xiao

unread,
Aug 15, 2024, 8:31:06 AM8/15/24
to Chromium Extensions, hrg...@gmail.com, Vibhash Chandra, Chromium Extensions, Liang Cui, Devashish Salgaonkar, Oliver Dunk
Currently I am using WebWorker for this purpose. The process looks like this:

// content-script.js
const SCRIPT_TEMPLATE = `
onmessage = (e) => {
    let response = main(e.data);
    postMessage(response);
};
function main(arg) {
    /* MAIN */
}`;
let {script_to_run} = await chrome.storage.sync.get();
let worker = new Worker(URL.createObjectURL(new Blob([
    SCRIPT_TEMPLATE.replace('/* MAIN */', script_to_run),
], {type: 'text/javascript'})));
worker.onmessage = (e) => {/* handle returned data */};
worker.postMessage(/* arg to pass */);

User-specified code will be executed in this WebWorker. The problem with this approach is that WebWorker has no DOM access, so when DOM access is necessary, we need to implement a set of bridged DOM APIs via postMessage calls. I think it is also possible to implement similar things with a sandboxed frame, but bridging the DOM access to the original webpage is still needed.

It would be nice to see a native chrome.userScripts.execute API so we don't need this mess anymore.

AutoControl support

unread,
Aug 17, 2024, 2:15:25 AM8/17/24
to Chromium Extensions, hrg...@gmail.com, Vibhash Chandra, Chromium Extensions, Liang Cui, Devashish Salgaonkar, Oliver Dunk
I think this was discussed before somewhere.

It was discussed at length here:

We are really sadden to hear that the Extensions Team "is comfortable with extensions taking that approach for the time being".
It might be confortable for them who don't have the responsibility of providing a good experience to the user, whereas it ruins the possibility of us extension developers to provide such good experience.
In summary, the suggested workaround has two giant problems:

FIRST:
It doesn't work on pages loaded before the extension was installed or while the extension is disabled.
Users who make use of scripts tend to be tech-savvy people. That kind of people know that disabling an extension is like closing a program. So they use extension managers like this or this or this. This allows them to enable/disable extensions with a single click or keyboard shortcut.
If we implemented the suggested workaround, we would have to "encourage" the user to never disable the extension, which is unreasonable because people like to have control over their machine. Some people like to enable an extension only at the moment they need it (which is a wise thing to do). Therefore, it's not acceptable for the user that an extension that executes scripts forces them to reload the page in which they want to execute the script and only then the extension will work on that page.
This is more that just a bad user experience, it ruins the "RUN SCRIPT" functionality of any extension as it can no longer be used conveniently.

SECOND:
The extension must require the userScripts and <all_urls> permissions at install time, i.e. they cannot be optional.
Executing scripts is quite often an optional feature. The user will enable the feature in the extension's options, then the permission request box will pop up and then the feature will just work. That's how it should be.
If we implemented the suggested workaround, we would have to require these two permission at all times so that the extension can inject the listening daemon script on all pages at all times.
How are we supposed to explain to the user that they must accept those permissions even if they don't need the associated feature?

Getfree

unread,
Aug 19, 2024, 5:10:11 PM8/19/24
to Chromium Extensions, AutoControl support, hrg...@gmail.com, Vibhash Chandra, Chromium Extensions, Liang Cui, Devashish Salgaonkar, Oliver Dunk
How are we supposed to explain to the user that they must accept those permissions even if they don't need the associated feature?

Just tell users the truth. e.g. "These permissions are mandatory because the Extensions Team is comfortable with this nasty workaround" download.png
Jokes aside, being confortable with a bad solution is really code for "we have a deadline to comply with and we are running out of time".
Basically, they just don't care. They have to turn off MV2 by June 2025 no matter what, otherwise the team leader's reputation is on the line.

Simeon Vincent

unread,
Aug 19, 2024, 8:13:50 PM8/19/24
to Mou Xiao, Chromium Extensions, hrg...@gmail.com, Vibhash Chandra, Liang Cui, Devashish Salgaonkar, Oliver Dunk
Instantiating a web worker using a Blob script as you described doesn't comply with CWS policy. The Additional Requirements for Manifest V3 section says (highlights mine):

Execution of logic from a remote source is permissible only when accomplished through a documented API that explicitly allows this practice and the use is inline with the documented purpose of the API, as detailed in the API Use policy. The permitted APIs for such remote execution are:
Note that exemptions apply solely to the specific section of code covered by these APIs. Extensions may still be in violation of this policy if they employ alternative methods to execute logic from remote sources elsewhere in their code.

Additionally, code run in contexts that are isolated from extension APIs (such as iframes and sandboxed pages) are exempt from the restriction on loading code from remote sources; however, these are treated similarly to our policy on communication with external servers. That is, it must still be possible to determine the full functionality of your extension and the interaction must still comply with our user data policies, including Limited Use and the extension's Privacy Policy.

While an extension's WebWorkers do not (currently) have access to the "chrome" global, they still execute on the extension's origin and therefore are not completely isolated from the rest of the extension.

That said, you can still use the same basic approach – sending a string to another context for execution – with a sandboxed page. You can do this in the background by either loading the sandboxed page in an offscreen document, or embedding the sandboxed page in an iframe in an offscreen document. This is a bit more complicated than your current implementation, but the upside is that you shouldn't encounter any issues with CWS review.

Simeon - @dotproto


--
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.

Vibhash Chandra

unread,
Aug 19, 2024, 11:28:44 PM8/19/24
to Chromium Extensions, hrg...@gmail.com, Vibhash Chandra, Chromium Extensions, Liang Cui, Devashish Salgaonkar, Oliver Dunk
In this case, user will have to enable developer mode which I want to avoid.

Our MV2 extension has this functionality where user could fill a form in extension popup and when user submits those details, extension would open a URL in new window and extension would inject string script in that window to proceed further.

This use case is no longer working for in MV3. I had tried with chrome.scripting.executeScript which runs into CSP errors.

I tried with userScripts API, but not able inject script with that either.

Regards,
Vibhash.

Erek Speed

unread,
Aug 19, 2024, 11:43:33 PM8/19/24
to Vibhash Chandra, Chromium Extensions, hrg...@gmail.com, Liang Cui, Devashish Salgaonkar, Oliver Dunk
If the user is inputting structures form inputs and not wanting to run custom scripts you should be able to include the script as an included JS file and then accept the users form inputs as arguments to the static JS file.

Unless your form is: "please input JS to run" it should work well.

--
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.

Mou Xiao

unread,
Aug 20, 2024, 1:33:17 AM8/20/24
to Chromium Extensions, Simeon Vincent, Chromium Extensions, hrg...@gmail.com, Vibhash Chandra, Liang Cui, Devashish Salgaonkar, Oliver Dunk, Mou Xiao
>  they still execute on the extension's origin

No. Since the worker is registered by the content script, they run on the origin of the target domain. It can be confirmed by `console.log(globalThis.origin)`. Therefore I believe it is a legitimate "context that are isolated from extension APIs". Actually it is even safer than the sandboxed page (which would really run on the extension's origin).

> you can still use the same basic approach – sending a string to another context for execution – with a sandboxed page

Firefox currently doesn't support sandboxed page. Therefore migrating to this approach leads to the burden of maintaining two separate codebase while providing no real benefit.

Simeon Vincent

unread,
Aug 20, 2024, 1:43:07 PM8/20/24
to Chromium Extensions, xiaoyua...@gmail.com, Simeon Vincent, Chromium Extensions, hrg...@gmail.com, vibh...@gmail.com, cuili...@gmail.com, deva...@yahoo.co.in, olive...@google.com
Ahh, I didn't realize that the content script created the worker. I don't know if CWS review would consider that compliant, but I'd still caution against that approach since the policy does not explicitly mention that approach.

> Actually it is even safer than the sandboxed page (which would really run on the extension's origin).

Sandboxes pages do not run on the extension's origin. That's one of the major design considerations for the feature. Chrome's Use eval() in sandboxed iframes documentation states (highglight mine) :

> Whenever a sandboxed page is loaded, it will be moved to a unique origin, and will be denied access to chrome.* APIs. If we load this sandboxed page into our extension via an iframe, we can pass it messages, let it act upon those messages in some way, and wait for it to pass us back a result. This simple messaging mechanism gives us everything we need to safely include eval-driven code in our extension's workflow.

> Firefox currently doesn't support sandboxed page. Therefore migrating to this approach leads to the burden of maintaining two separate codebase while providing no real benefit.

You're right. Unfortunately Firefox does not currently support sandboxed pages. I'm currently working with Mozilla on WebExtensions and this is one of the features I'm advocating that Firefox should support. This is a complex issue, so support is by no means guaranteed.

I regret to say that this implementation also likely violates AMO policy, which states, "Add-ons must be self-contained and not load remote code for execution." This line is a little blurry because AMO does have an allowance for user script managers, so the permissibility of this approach depends on how much control users have over the scripts being injected and other implementation details of the feature.

Simeon - @dotproto

Vibhash Chandra

unread,
Oct 22, 2024, 9:48:28 PM10/22/24
to Oliver Dunk, Devashish Salgaonkar, Chromium Extensions, Liang Cui
Hi Oliver,

Is there any update on this API?

Regards,
Vibhash

Devashish Salgaonkar

unread,
Jan 3, 2025, 12:06:00 AMJan 3
to Chromium Extensions, Chromium Extensions
Team - Is there any update on this?

Oliver Dunk

unread,
Jan 3, 2025, 4:26:11 AMJan 3
to Devashish Salgaonkar, Chromium Extensions
Hi both,

We're actively working on an implementation of this so hopefully it isn't too far out now. I'll make sure we post an update once there's something you can play with in Chrome.

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

Zachary Wharin

unread,
Jan 30, 2025, 11:02:38 AMJan 30
to Chromium Extensions, Oliver Dunk, Chromium Extensions, Devashish Salgaonkar
Good Morning Oliver,

Just checking in to see if there is an update on this. Thank you for the work that you all are doing!

Thanks, Zach

woxxom

unread,
Jan 31, 2025, 2:17:40 AMJan 31
to Chromium Extensions, Zachary Wharin, Oliver Dunk, Chromium Extensions, Devashish Salgaonkar
Per the blog post userScripts.execute() [...] is currently in Canary behind the ApiUserScriptsExecute flag. Technically speaking, it's not a flag though, but a so-called "feature", which is enabled by adding --enable-features=ApiUserScriptsExecute to the browser launcher/shortcut's command line and then fully restarting the browser.

The documentation is still hidden:

chrome.userScripts.execute({
  js: [{code: 'console.log(1)'}],
  target: {tabId: 123},
  injectImmediately: true,
  world: 'MAIN',
});

Oliver Dunk

unread,
Jan 31, 2025, 7:14:37 AMJan 31
to woxxom, Chromium Extensions, Zachary Wharin, Devashish Salgaonkar
Yes, you beat me! I'm planning to post about that shortly with more information on how to test (but the flag wOxxOm suggested is the right approach).

Given it's a fairly simple new API I suspect we can move beyond Canary fairly soon.
Oliver Dunk | DevRel, Chrome Extensions | https://developer.chrome.com/ | London, GB

Zachary Wharin

unread,
Feb 12, 2025, 8:19:29 AMFeb 12
to Chromium Extensions, Oliver Dunk, Chromium Extensions, Devashish Salgaonkar, woxxom
Oliver and Team,

That is great to hear!! We have an upcoming project that that uses MSFT Azure Test and Feedback, and currently the extension is rendered useless according to Microsoft because of this Missing API. Hopefully everything goes smoothly and it can roll it to base soon! Do you have and expected ETA for General Availability?

Thank you again to all who have helped in this project!


2025-02-12 08_17_03-Allow string script in chrome.scripting.executeScript().png

Liang Cui

unread,
Feb 12, 2025, 8:38:51 AMFeb 12
to Chromium Extensions, Oliver Dunk, Chromium Extensions

Can you please help me confirm whether this API can return the value of the script execution result?

something like this:

let result = chrome.userScripts.execute({
  js: [{code: 'console.log(1)'}],
  target: {tabId: 123},
  injectImmediately: true,
  world: 'MAIN',
});

Thank you~

Oliver Dunk

unread,
Feb 12, 2025, 8:40:27 AMFeb 12
to Zachary Wharin, Chromium Extensions, Devashish Salgaonkar, woxxom
Hi Zachary,

Glad you're excited about this!

We don't have an ETA yet, but I haven't heard any specific feedback. With that in mind, I would expect that we should be able to remove the feature flag fairly soon.

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

Oliver Dunk

unread,
Feb 12, 2025, 8:43:45 AMFeb 12
to Liang Cui, Chromium Extensions
Hi,

Yes, the API returns a promise which resolves to the following structure:

```
[{"documentId":"","frameId":0,"result":1}]
```

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

Liang Cui

unread,
Feb 12, 2025, 8:51:25 AMFeb 12
to Chromium Extensions, Oliver Dunk, Chromium Extensions, Liang Cui
That's great, thank you!

Vibhash Chandra

unread,
Mar 17, 2025, 12:44:23 AM (4 days ago) Mar 17
to Chromium Extensions, Liang Cui, Oliver Dunk, Chromium Extensions
Hi,

Was this API made available with Chrome 134?

Regards,
Vibhash.

Oliver Dunk

unread,
Mar 17, 2025, 10:10:10 AM (3 days ago) Mar 17
to Vibhash Chandra, Chromium Extensions, Liang Cui
Hi all,

The `userScripts.execute` method was made available by default in Chrome 135 (currently in beta).

I'll make sure to update what's new and the related threads now. Thanks for the reminder!

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

Reply all
Reply to author
Forward
0 new messages