-
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
Proposal: dom.execute() #678
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,174 @@ | ||||||||||||||
# Proposal: browser.dom.execute() | ||||||||||||||
|
||||||||||||||
**Summary** | ||||||||||||||
|
||||||||||||||
This API allows an isolated world (such as a content script world or user | ||||||||||||||
script world) to synchronously execute code in the main world of the document | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this proposal limited to the main world? I was imagining this API to also be used from content scripts to execute in a user script world. |
||||||||||||||
its injected in. | ||||||||||||||
|
||||||||||||||
**Document Metadata** | ||||||||||||||
|
||||||||||||||
**Author:** rdcronin | ||||||||||||||
|
||||||||||||||
**Sponsoring Browser:** Chromee | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||
|
||||||||||||||
**Contributors:** Rob--W, ... | ||||||||||||||
|
||||||||||||||
**Created:** 2024-06-24 | ||||||||||||||
|
||||||||||||||
**Related Issues:** <TODO> | ||||||||||||||
|
||||||||||||||
## Motivation | ||||||||||||||
|
||||||||||||||
### Objective | ||||||||||||||
|
||||||||||||||
This allows isolated worlds to synchronously inject in the main world. This is | ||||||||||||||
both beneficial by itself and also a prerequisite for our current plan to | ||||||||||||||
implement inter-world communication (the latter of which will be a separate | ||||||||||||||
proposal). | ||||||||||||||
|
||||||||||||||
#### Use Cases | ||||||||||||||
|
||||||||||||||
##### Executing script in the main world | ||||||||||||||
|
||||||||||||||
Today, extensions can execute script in the main world by: | ||||||||||||||
* Leveraging scripting.executeScript() or registering content scripts or user | ||||||||||||||
scripts, or | ||||||||||||||
* Appending a new script tag to the document | ||||||||||||||
|
||||||||||||||
The former is heavily asynchronous or requires knowledge of whether to inject | ||||||||||||||
beforehand (to register a script). The latter is very visible to the site and | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The latter is also asynchronous, because the default CSP of content scripts in MV3 forbids inline script elements, leaving the only option to be |
||||||||||||||
has more potential side effects. While there's no way to fully "hide" a script | ||||||||||||||
from a site, it is desirable to avoid adding DOM elements (which could | ||||||||||||||
interfere with sites with certain properties). | ||||||||||||||
|
||||||||||||||
### Known Consumers | ||||||||||||||
|
||||||||||||||
User script managers have expressed an interest in this API, but this is also | ||||||||||||||
useful to any extension that wishes to execute script in the main world while | ||||||||||||||
leveraging information from the isolated world. | ||||||||||||||
|
||||||||||||||
## Specification | ||||||||||||||
|
||||||||||||||
### Schema | ||||||||||||||
|
||||||||||||||
``` | ||||||||||||||
declare namespace dom { | ||||||||||||||
interface ScriptInjection { | ||||||||||||||
func: () => any; | ||||||||||||||
args: any[]; | ||||||||||||||
}; | ||||||||||||||
|
||||||||||||||
export function execute( | ||||||||||||||
injection: ScriptInjection | ||||||||||||||
): any | ||||||||||||||
} | ||||||||||||||
``` | ||||||||||||||
|
||||||||||||||
### Behavior | ||||||||||||||
|
||||||||||||||
#### Function execution | ||||||||||||||
|
||||||||||||||
The function is executed _synchronously_ in the main world. The return value | ||||||||||||||
is the serialized return value from the function execution. | ||||||||||||||
|
||||||||||||||
#### Cross-world contamination | ||||||||||||||
|
||||||||||||||
To prevent any "piercing" of the different worlds, all pieces are serialized in | ||||||||||||||
their original world, then a copy is deserialized in the target world. | ||||||||||||||
Otherwise, there would be a risk of sharing variables between worlds (even when | ||||||||||||||
non-obvious, such as by accessing the prototype of an item). | ||||||||||||||
|
||||||||||||||
##### Argument serialization | ||||||||||||||
|
||||||||||||||
Arguments are serialized and deserialized using the Structured Cloning | ||||||||||||||
algorithm. This allows for more flexibility than simply JSON conversion. In | ||||||||||||||
the future, some arguments may have custom serialization. | ||||||||||||||
Comment on lines
+84
to
+86
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Have the proposal authors considered whether arguments should support transferable objects? |
||||||||||||||
|
||||||||||||||
##### Function serialization | ||||||||||||||
|
||||||||||||||
The injected function is serialized to and parsed from a string. Bound | ||||||||||||||
parameters are not supported. Arguments must be curried in through the `args` | ||||||||||||||
key. | ||||||||||||||
|
||||||||||||||
##### Return value serialization | ||||||||||||||
|
||||||||||||||
Like arguments, the return value of the execution is serialized and | ||||||||||||||
deserialized using the Structured Cloning algorithm. | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
I elaborated on the serialization semantics. Although it would be nice to support |
||||||||||||||
|
||||||||||||||
##### Injected script privileges | ||||||||||||||
|
||||||||||||||
The injected script has the same privileges has other script in the main world. | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||
This goes for API access, Content Security Policy, origin access, and more. | ||||||||||||||
|
||||||||||||||
##### CSP Application | ||||||||||||||
|
||||||||||||||
The injected script is not considered inline and does not have a corresponding | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would this also apply if JavaScript in general is disabled? With |
||||||||||||||
source; as such, CSP restrictions on script-src (and similar) do not apply. | ||||||||||||||
However, since the script executes in the main world, other CSP restrictions | ||||||||||||||
(including connect-src and which sources may be added to the document, among | ||||||||||||||
many others) *may* apply to the injected script. | ||||||||||||||
|
||||||||||||||
### New Permissions | ||||||||||||||
|
||||||||||||||
There are no new permissions for this capability. This does not allow any new | ||||||||||||||
data access, since it is only accessible once the extension has already | ||||||||||||||
injected a script into the document. Extensions can also already interact with | ||||||||||||||
the main world of the document through either appending script tags or directly | ||||||||||||||
injecting with registered content or user scripts, or the | ||||||||||||||
scripting.executeScript() method. | ||||||||||||||
|
||||||||||||||
### Manifest File Changes | ||||||||||||||
|
||||||||||||||
There are no necessary manifest changes. | ||||||||||||||
|
||||||||||||||
## Security and Privacy | ||||||||||||||
|
||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Having this API in it's current form would not make sense outside isolated worlds.
Suggested change
|
||||||||||||||
### Exposed Sensitive Data | ||||||||||||||
|
||||||||||||||
This does not result in any new data being exposed. | ||||||||||||||
|
||||||||||||||
### Abuse Mitigations | ||||||||||||||
|
||||||||||||||
This doesn't enable any new data access. To prevent any risk of cross-world | ||||||||||||||
interaction, all data that passes between worlds is serialized in the original | ||||||||||||||
world, then deserialized (creating a new copy) in the target world. The | ||||||||||||||
executed script has no additional capabilities or APIs; it has the same | ||||||||||||||
privilege as the document's own main world code. | ||||||||||||||
|
||||||||||||||
### Additional Security Considerations | ||||||||||||||
|
||||||||||||||
N/A. | ||||||||||||||
|
||||||||||||||
## Alternatives | ||||||||||||||
|
||||||||||||||
### Existing Workarounds | ||||||||||||||
|
||||||||||||||
Extension developers can inject in the main world through registered content or | ||||||||||||||
user scripts, along with the scripting.executeScript() API. They can also | ||||||||||||||
inject code in the main world by adding a script tag to the DOM. However, the | ||||||||||||||
API-powered options are asynchronous, and a script tag is inherently visible to | ||||||||||||||
other scripts running in the main world. | ||||||||||||||
|
||||||||||||||
### Open Web API | ||||||||||||||
|
||||||||||||||
The open web is unaware of the concept of multiple Javascript worlds, so this | ||||||||||||||
wouldn't belong as an open web API. | ||||||||||||||
|
||||||||||||||
## Implementation Notes | ||||||||||||||
|
||||||||||||||
N/A. | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The whole proposal sketches this as a feature designed for isolated worlds. While the dom namespace is available to content scripts, it is not restricted to content scripts - regular extension documents can also access the API. Should we restrict the availability of this new functionality to content scripts only, or do you want it to have the same availability as the current |
||||||||||||||
|
||||||||||||||
## Future Work | ||||||||||||||
|
||||||||||||||
### Inter-world communication | ||||||||||||||
|
||||||||||||||
This API is a necessary first step before introducing inter-world communication | ||||||||||||||
channels. We plan to do that alongside the implementation for this API. | ||||||||||||||
|
||||||||||||||
### Specifying a target world | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Which means not specifying the target world in the future would default to the main world. If that is a downside or if explicitly specifying a target world right from the start is preferred specifying the target world right now world be good. |
||||||||||||||
|
||||||||||||||
For now, this API is intended to let scripts running in content or user script | ||||||||||||||
worlds inject in the main world. In the future, we will expand this to allow a | ||||||||||||||
script to inject in additional other worlds, such as a content script injecting | ||||||||||||||
in a user script world. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the motivation to use the
dom
namespace instead of thescripting
namespace? Considering it's similarities withscripting.executeScript
and the seemingly lack of connection with the DOM. Consideringexecute()
,createPort()
and alsoopenOrClosedShadowRoot()
, would it not make sense to usebrowser.document
instead?