w3c / webextensions

Charter and administrivia for the WebExtensions Community Group (WECG)
Other
578 stars 50 forks source link

Add userScripts.execute() API proposal #540

Closed EmiliaPaz closed 3 months ago

EmiliaPaz commented 4 months ago

Proposal for adding <browser>.userScripts.execute() API to allow extensions to inject user scripts programmatically into web contents.

Getfree commented 4 months ago

What would be the function's arguments? Does it accept a "code" parameter? Does it accept a "runAt" parameter? Is it going to be affected by the CSP of the target page? Will the script run in the global scope or a local scope? (i.e. can the script create global variables?)

tophf commented 4 months ago

Hopefully you don't mind me posting the answers to these questions which I found while researching the PR as it's something I'll use in my extensions as well:

affected by the CSP of the target page? chrome.userScripts.register runs code in the MAIN world despite github.com's `script-src` CSP, so injection itself is not affected and the same should work for chrome.userScripts.execute, **but a clarification for the `MAIN` world would be helpful anyway** as the [documentation mentions](https://developer.chrome.com/docs/extensions/reference/userScripts/index-2.html#type-ExecutionWorld) CSP exemption only for the USER_SCRIPT world, so it could clarify that the code will be injected in the MAIN world regardless of page's CSP, while things created by this code will be subject to the CSP of the page e.g. a `style` element will be blocked by a restrictive `style-src` CSP.
arguments It's `UserScriptInjection` object in the code of the PR, which expectedly looks like a hybrid of chrome.userScripts.register and chrome.scripting.executeScript.
code Yes, it's `js: [{code: 'console.log(1)'}]` same as chrome.userScripts.register, see [ScriptSource](https://developer.chrome.com/docs/extensions/reference/api/userScripts#type-ScriptSource) definition.
runAt It's a boolean `injectImmediately` same as chrome.scripting.executeScript, false (by default) [means document_idle](https://crsrc.org/chrome/browser/extensions/api/scripting/scripting_api.cc;l=686;drc=c1857d3b72f27e8259b79108160a475b57d5b2ee).
global scope or a local scope chrome.userScripts.register uses global scope, so it should be the same, but either way we can create global variables explicitly like `window.foo = 123`, which would be necessary when `func` is implemented in the future as it'll run in an IIFE.
EmiliaPaz commented 4 months ago

Thanks @Getfree for taking a look at the proposal. And special thanks to @tophf for providing answers. These are mostly correct, but I am expanding them more:

What would be the function's arguments?

userScripts.execute(injection: UserScriptInjection). You can see it in more detail on the API Schema section in the proposal.

Does it accept a "code" parameter?

Yes, injection has a User Script API ScriptSource parameter which includes code.

Does it accept a "runAt" parameter?

No. By default in run at document_idle but can be set to injectImmediately

Is it going to be affected by the CSP of the target page?

Depends on the world it's injected to. User script can be registered/executed in the USER_SCRIPT or MAIN world. The USER_SCRIPT world is an execution environment specific to user scripts and is exempt from the page's CSP. The MAIN world is the execution environment shared with the host page's JavaScript, and thus follows the page's CSP.

@tophf , not sure I follow your answer here. Extension can decide the world in which a script is registered. Script registered in MAIN world will follow the page's CSP, and when registered in USER_SCRIPT world will follow the extension CSP or a customized one For example:

Will the script run in the global scope or a local scope? (i.e. can the script create global variables?)

It runs on the global scope.

tophf commented 4 months ago

@tophf , not sure I follow your answer here.

I was referring to the existing problem in the currently used workaround for MV3 to run arbitrary code by creating a script element inside code that already runs in the MAIN world - this workaround didn't work with a strict CSP of the page. With the new userScripts API the code will run regardless of the CSP of the page (it still affects the artifacts created by this code). I think this is an important info that could be used for an explicit clarification in the documentation.

EmiliaPaz commented 3 months ago

This PR has been approved and is ready to merge (I don't have the power to do so :) )