-
Notifications
You must be signed in to change notification settings - Fork 56
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support multiple user script worlds in the user scripts API #565
Comments
I am currently working on the implementation of multiple user script worlds in Firefox, and while examining Chrome's implementation more closely, I found an issue with the currently specified/implemented behavior for The current spec is at https://github.com/w3c/webextensions/blob/main/proposals/multiple_user_script_worlds.md and describes
This description (and the spec in general) does not specify the worldId to use for the "default user script world". Chrome's current implementation seems to use Example (requires starting Chrome with await chrome.userScripts.register([{
id: "id",
js: [{ code: "alert(0)" }],
matches: ["https://example.com/*"],
worldId: "x",
}]);
await chrome.userScripts.update([{
id: "id",
// Also weird: Chrome requires js to be set in update()
js: [{ code: "alert(1)" }],
worldId: null, // intent to change to default world
}]);
var scripts = await chrome.userScripts.getScripts({ ids: ["id"] });
console.log(scripts[0].worldId);
// Expected: null (default?)
// Actual: "x" (unchanged) If we want to keep the default at null, it is technically possible to distinguish "property set" (=set to null) from "property not set" (= do not update) in Firefox's implementation. However, this is unusual and I would prefer a different solution that is easier to explain. Last time we discussed the worldId format, I mentioned two options at #560 (comment)
Given this all, I propose to establish |
Thanks, @Rob--W ! Agreed this is something we should fix, and I'm supportive of using the empty string ( |
Looks like this change was made in https://issues.chromium.org/issues/331680187#comment22 |
Another aspect that becomes particularly more relevant with multiple worlds is what should happen when Considering that there is now a dedicated See the following example. Note that I pass a worldId in this example, but the same issue also occurs when worldId is not specified. // Relax CSP:
await browser.userScripts.configureWorld({ worldId: "hey", csp: "script-src 'unsafe-eval'" });
// Enable messaging:
await browser.userScripts.configureWorld({ worldId: "hey", messaging: true });
// Check whether CSP and messaging have been configured:
console.log(await browser.userScripts.getWorldConfigurations());
// Chrome: [{ messaging: true, worldId: "hey" }] // csp is gone!
// Firefox: [{ worldId: "hey", csp: "script-src 'unsafe-eval'", messaging: true }] |
- Use empty string instead of unspecified/null as the default worldId, following #565 (comment) - Declare "js" property as optional with remark that it is required in "register", to enable `userScripts.update()` without requiring "js". - Expand on RegisteredUserScript validation, notably on validating matches+includeGlobs after an update. - Update `resetWorldConfiguration()` signature to match Chrome's and Firefox's actual implementation: `worldId` is optional. - Create a new section "World Configurations" and specify world configuration behavior more precisely. In particular, clarify fallback behavior, following #565 (comment) - Mention Firefox's optional-only "userScripts" permission design.
I chatted with @rdcronin and he agreed with the behavior proposed above (not overwriting properties that are omitted/null). |
... and that has been implemented at https://issues.chromium.org/issues/40938749#comment7 I have been working on creating a demo extension to demonstrate the use of the userScripts API with multiple worlds, and based on that I have some feedback:
Firefox has not enabled the userScripts API by default yet, so if Chrome is willing to change these behaviors, I would be willing to make the same changes in Firefox. |
I'm opposed to this change. Everywhere else, an empty
Similar to above, I don't know how often extensions are going to be bulk-editing these worlds. I'm not opposed to relaxing the signature to take a single-or-an-array, but I'd want to see some concrete data on how often that happens. If this is relatively rare, there's not going to be a meaningful performance benefit by batching the calls vs serially calling them (and, in Chrome, we'd probably just do a serial update anyway, unless we found that these were on a "hot path" frequently). Since adding an array option to the signature is backwards compatible (and since configureWorld has already shipped), I'd propose we hold off on these as a potential future enhancement if we see compelling data indicating their value. WDYT? |
Preface: I am fine with either outcome; I merely wanted to double-check that this is the direction that we want with the API before shipping (and then being locked into the API signatures).
The way I look at it, an empty worldId means unspecified, and the default value is filled in, which is In Firefox, when
I'd rather not introduce yet another method. Like you said, extensions can iterate over
I expect proper user script managers to generate a new world for each script to achieve full isolation between individual user scripts to minimize conflicts (and avoid the need to somehow capture and sanitize prototypes). Thus, as part of registering N scripts, there would be up to N world configurations (minus scripts that run in the main world, which is the default in user script managers). An alternative approach to avoid the need to configure worlds individually is to just configure the default world (with enableMessaging:true), and let each world do its own thing. With I'm not opposed to tabling support for bulk-editing world configurations. I just noticed the inconsistency in these methods taking one input only, whereas In Firefox I have currently not implemented world limits. In Chrome, I would imagine extensions to want to know whether they are hitting a limit, and perhaps regroup scripts by worldId based on availability. For this, bulk-editing would be useful, to quickly tell whether a proposed configuration would fit within the limits. |
I agree. And here, if omitted, it should use the default value, which is for the default world. : ) (runAt being omitted defaults to document_idle, not to running in all worlds)
I'm definitely fine holding off on this and revisiting in the future.
I agree. However, my expectation is also that, most of the time, users are (un)installing one user script at a time, so the delta of registration is still 1 world. There are exceptions to this -- e.g., user script managers that may support sync state or otherwise need to reinstantiate state; however, I expect these to be relatively rare (on new machines, after updates, etc), where performing a serial update flow is not going to meaningfully affect performance.
Yes, this is one case I do think can potentially happen, and could theoretically benefit from batched updates. However, I'd like to wait for data before we make that optimization. We should determine both how often this happens as well as if bulk-registering/unregistering worlds is the approach managers would want to take (I think a better approach for most use cases would be to instead intelligently group user scripts so that multiple can run in the same world if necessary. This isn't new; I think every major browser does this today for e.g. tab grouping in processing. It's nice when you can have everything in its own process, but resource constraints require letting grouping things in different circumstances).
Understood, and it's good to discuss! It seems like we're okay with the following:
Are you good with that, Rob? |
SGTM! I see potential issues with the lack of control over the world that the script is executing in when a world limit is reached, but that can be overcome by the userscript manager extensions by wrapping the actual JS code in a function call that does some pre-processing. E.g. (() => {
if (some_var_only_defined_in_default_userscript_world) {
// Oops, over quota. This script is sensitive and should
// not share execution environment, so return now.
return;
}
// Copy-paste actual user script code here
})(); |
- Use empty string instead of unspecified/null as the default worldId, following #565 (comment) - Declare "js" property as optional with remark that it is required in "register", to enable `userScripts.update()` without requiring "js". - Expand on RegisteredUserScript validation, notably on validating matches+includeGlobs after an update. - Update `resetWorldConfiguration()` signature to match Chrome's and Firefox's actual implementation: `worldId` is optional. - Create a new section "World Configurations" and specify world configuration behavior more precisely. In particular, clarify fallback behavior, following #565 (comment) - Mention Firefox's optional-only "userScripts" permission design.
The user scripts API allows scripts to inject in a new "user script" world (in addition to the main world), which has slightly different properties than the isolated world content scripts inject in. From early discussions (e.g. this comment from issue #279 ), we have supported the idea of multiple user script worlds.
This issue tracks that work.
The text was updated successfully, but these errors were encountered: