-
Notifications
You must be signed in to change notification settings - Fork 15k
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
feat: add a new contextBridge module #20307
Conversation
c67a93e
to
ca9f573
Compare
edebc37
to
d1a0b44
Compare
|
### Isolated World | ||
|
||
When `contextIsolation` is enabled in your `webPreferences` your `preload` scripts run in an "Isolated World". You can read more about | ||
context isolation and what it affects in the [BrowserWindow](browser-window.md) docs. |
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.
I think since contextIsolation is mentioned in a BW constructor option and isn't super obvious from just a link to browser-window.md
that it would be worthwhile to lower that barrier and re-copy it here or move it into a standalone document we could link to
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.
Can we implement the primitives that would be necessary to build this as a 3rd-party library and experiment with the specific API independently of the Electron release cycle until we can settle on an API that's good? I have some reservations about the API as written (particularly regarding the "reverse bindings" part), and I don't want to enshrine this in Electron without giving it a bit of a test-drive first.
To keep this answer short, no, the way this is written it requires access to v8 contexts that native modules don't have access to let alone raw JS. Also to be fair this is already very barebones, we don't prescribe API shape or structure, just "give us some functions and we'll put them in the other context".
This API is flagged as "experimental" to make it clear the API (a) is new and relatively untested and (b) may be changed in the future. If you have specific usability / API concerns I'd love to hear the specifics so that they can be addressed. |
> Create a safe, bi-directional, synchronous bridge across isolated contexts | ||
|
||
Process: [Renderer](../glossary.md#renderer-process) | ||
|
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.
Add a short paragraph explaining why the reader might want to use this. What problems does this solve?
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.
This didn't get addressed...?
namespace context_bridge { | ||
|
||
using FunctionContextPair = | ||
std::tuple<v8::Global<v8::Function>, v8::Global<v8::Context>>; |
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.
i thought i commented on it before but it seems to have gotten lost; can we use a struct for this instead of std::tuple
?
std::tuple<v8::Global<v8::Function>, v8::Global<v8::Context>>; | |
struct FunctionContextPair { | |
v8::Global<v8::Function> function; | |
v8::Global<v8::Context> context; | |
}; |
using FunctionContextPair = | ||
std::tuple<v8::Global<v8::Function>, v8::Global<v8::Context>>; | ||
|
||
using WeakGlobalPair = std::tuple<v8::Global<v8::Value>, v8::Global<v8::Value>>; |
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.
here too
using WeakGlobalPair = std::tuple<v8::Global<v8::Value>, v8::Global<v8::Value>>; | |
struct WeakGlobalPair { | |
v8::Global<v8::Value> value_in_isolated_context; | |
v8::Global<v8::Value> value_in_main_context; | |
}; |
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.
Approving with above comments with the consideration that this code doesn't affect apps that don't call contextBridge.exposeInMainWorld
. I still have some concerns about this implementation, but in the interests of moving this forward, let's land and iterate.
Release Notes Persisted
|
* feat: add a new contextBridge module * chore: fix docs linting * feat: add support for function arguments being proxied * chore: ensure that contextBridge can only be used when contextIsolation is enabled * docs: getReverseBinding can be null * docs: fix broken links in md file * feat: add support for promises in function parameters * fix: linting failure for explicit constructor * Update atom_api_context_bridge.cc * chore: update docs and API design as per feedback * refactor: remove reverse bindings and handle GC'able functions across the bridge * chore: only expose debugGC in testing builds * fix: do not proxy promises as objects * spec: add complete spec coverage for contextBridge * spec: add tests for null/undefined and the anti-overwrite logic * chore: fix linting * spec: add complex nested back-and-forth function calling * fix: expose contextBridge in sandboxed renderers * refactor: improve security of default_app using the new contextBridge module * s/bindAPIInMainWorld/exposeInMainWorld * chore: sorry for this commit, its a big one, I fixed like everything and refactored a lot * chore: remove PassedValueCache as it is unused now Values transferred from context A to context B are now cachde in the RenderFramePersistenceStore * chore: move to anonymous namespace * refactor: remove PassValueToOtherContextWithCache * chore: remove commented unused code blocks * chore: remove .only * chore: remote commented code * refactor: extract RenderFramePersistenceStore * spec: ensure it works with numbered keys * fix: handle number keys correctly * fix: sort out the linter * spec: update default_app asar spec for removed file * refactor: change signatures to return v8 objects directly rather than the mate dictionary handle * refactor: use the v8 serializer to support cloneable buffers and other object types * chore: fix linting * fix: handle hash collisions with a linked list in the map * fix: enforce a recursion limit on the context bridge * chore: fix linting * chore: remove TODO * chore: adapt for PR feedback * chore: remove .only * chore: clean up docs and clean up the proxy map when objects are released * chore: ensure we cache object values that are cloned through the V8 serializer
@MarshallOfSound has manually backported this PR to "6-0-x", please check out #20639 |
@MarshallOfSound has manually backported this PR to "6-1-x", please check out #20639 |
* feat: add a new contextBridge module (#20307) * feat: add a new contextBridge module * chore: fix docs linting * feat: add support for function arguments being proxied * chore: ensure that contextBridge can only be used when contextIsolation is enabled * docs: getReverseBinding can be null * docs: fix broken links in md file * feat: add support for promises in function parameters * fix: linting failure for explicit constructor * Update atom_api_context_bridge.cc * chore: update docs and API design as per feedback * refactor: remove reverse bindings and handle GC'able functions across the bridge * chore: only expose debugGC in testing builds * fix: do not proxy promises as objects * spec: add complete spec coverage for contextBridge * spec: add tests for null/undefined and the anti-overwrite logic * chore: fix linting * spec: add complex nested back-and-forth function calling * fix: expose contextBridge in sandboxed renderers * refactor: improve security of default_app using the new contextBridge module * s/bindAPIInMainWorld/exposeInMainWorld * chore: sorry for this commit, its a big one, I fixed like everything and refactored a lot * chore: remove PassedValueCache as it is unused now Values transferred from context A to context B are now cachde in the RenderFramePersistenceStore * chore: move to anonymous namespace * refactor: remove PassValueToOtherContextWithCache * chore: remove commented unused code blocks * chore: remove .only * chore: remote commented code * refactor: extract RenderFramePersistenceStore * spec: ensure it works with numbered keys * fix: handle number keys correctly * fix: sort out the linter * spec: update default_app asar spec for removed file * refactor: change signatures to return v8 objects directly rather than the mate dictionary handle * refactor: use the v8 serializer to support cloneable buffers and other object types * chore: fix linting * fix: handle hash collisions with a linked list in the map * fix: enforce a recursion limit on the context bridge * chore: fix linting * chore: remove TODO * chore: adapt for PR feedback * chore: remove .only * chore: clean up docs and clean up the proxy map when objects are released * chore: ensure we cache object values that are cloned through the V8 serializer * docs: mark contextBridge as experimental (#20638) * docs: mark contextBridge as experimental This commit didn't make it to the original PR, quick addition here * Update context-bridge.md * chore: touch up the differences between master and 6-0-x * chore: add v8 serializer converter, cherry picked from 2fad53e * chore: support converting OnceCallback to V8 (#17941) * chore: fixup tests * chore: fix linting * chore: add patch for mojo message constructor
* feat: add a new contextBridge module * chore: fix docs linting * feat: add support for function arguments being proxied * chore: ensure that contextBridge can only be used when contextIsolation is enabled * docs: getReverseBinding can be null * docs: fix broken links in md file * feat: add support for promises in function parameters * fix: linting failure for explicit constructor * Update atom_api_context_bridge.cc * chore: update docs and API design as per feedback * refactor: remove reverse bindings and handle GC'able functions across the bridge * chore: only expose debugGC in testing builds * fix: do not proxy promises as objects * spec: add complete spec coverage for contextBridge * spec: add tests for null/undefined and the anti-overwrite logic * chore: fix linting * spec: add complex nested back-and-forth function calling * fix: expose contextBridge in sandboxed renderers * refactor: improve security of default_app using the new contextBridge module * s/bindAPIInMainWorld/exposeInMainWorld * chore: sorry for this commit, its a big one, I fixed like everything and refactored a lot * chore: remove PassedValueCache as it is unused now Values transferred from context A to context B are now cachde in the RenderFramePersistenceStore * chore: move to anonymous namespace * refactor: remove PassValueToOtherContextWithCache * chore: remove commented unused code blocks * chore: remove .only * chore: remote commented code * refactor: extract RenderFramePersistenceStore * spec: ensure it works with numbered keys * fix: handle number keys correctly * fix: sort out the linter * spec: update default_app asar spec for removed file * refactor: change signatures to return v8 objects directly rather than the mate dictionary handle * refactor: use the v8 serializer to support cloneable buffers and other object types * chore: fix linting * fix: handle hash collisions with a linked list in the map * fix: enforce a recursion limit on the context bridge * chore: fix linting * chore: remove TODO * chore: adapt for PR feedback * chore: remove .only * chore: clean up docs and clean up the proxy map when objects are released * chore: ensure we cache object values that are cloned through the V8 serializer
@MarshallOfSound has manually backported this PR to "7-0-x", please check out #20789 |
@MarshallOfSound has manually backported this PR to "7-1-x", please check out #20789 |
* feat: add a new contextBridge module (#20307) * feat: add a new contextBridge module * chore: fix docs linting * feat: add support for function arguments being proxied * chore: ensure that contextBridge can only be used when contextIsolation is enabled * docs: getReverseBinding can be null * docs: fix broken links in md file * feat: add support for promises in function parameters * fix: linting failure for explicit constructor * Update atom_api_context_bridge.cc * chore: update docs and API design as per feedback * refactor: remove reverse bindings and handle GC'able functions across the bridge * chore: only expose debugGC in testing builds * fix: do not proxy promises as objects * spec: add complete spec coverage for contextBridge * spec: add tests for null/undefined and the anti-overwrite logic * chore: fix linting * spec: add complex nested back-and-forth function calling * fix: expose contextBridge in sandboxed renderers * refactor: improve security of default_app using the new contextBridge module * s/bindAPIInMainWorld/exposeInMainWorld * chore: sorry for this commit, its a big one, I fixed like everything and refactored a lot * chore: remove PassedValueCache as it is unused now Values transferred from context A to context B are now cachde in the RenderFramePersistenceStore * chore: move to anonymous namespace * refactor: remove PassValueToOtherContextWithCache * chore: remove commented unused code blocks * chore: remove .only * chore: remote commented code * refactor: extract RenderFramePersistenceStore * spec: ensure it works with numbered keys * fix: handle number keys correctly * fix: sort out the linter * spec: update default_app asar spec for removed file * refactor: change signatures to return v8 objects directly rather than the mate dictionary handle * refactor: use the v8 serializer to support cloneable buffers and other object types * chore: fix linting * fix: handle hash collisions with a linked list in the map * fix: enforce a recursion limit on the context bridge * chore: fix linting * chore: remove TODO * chore: adapt for PR feedback * chore: remove .only * chore: clean up docs and clean up the proxy map when objects are released * chore: ensure we cache object values that are cloned through the V8 serializer * docs: mark contextBridge as experimental (#20638) * docs: mark contextBridge as experimental This commit didn't make it to the original PR, quick addition here * Update context-bridge.md * chore: update for 7-0-x differences * chore: update callback header * chore: add v8 serializer converter, cherry picked from 2fad53e * chore: update for 7-0-x differences
@MarshallOfSound, since you wrote this code, can you think of a case where the values/objects passed through the contextBridge do not fully "come out" in the other context? I'll try to parse through your commits too when I have time. ipcRenderer does not have access to the .on(channel, args) method after coming through the contextBridge.
index.html
|
@reZach This is all in the documentation, prototypes are not sent over the bridge. https://electronjs.org/docs/api/context-bridge#parameter--error--return-type-support Also as a side note, exposing the entire |
Thank you for clarifying that @MarshallOfSound. I missed that bit! There is no other way to configure the The entire Desiredrenderer
main
I've posted a $230 bounty for this issue. I imagine as library authors switch to use WorkaroundFor the time being I'm setting
|
What is this?
This PR adds a new renderer-side module called
contextBridge
that allows you to expose an API synchronously from the isolated context to the main world (and optionally back again) to achieve the following goals.window.api = {}
pattern to enable contextIsolation. The migration becomesbindAPIInMainWorld('api', {})
.contextIsolation
by default no longer seems like a daunting task.How?
The docs outline a lot of how this works from a users perspective so I'd recommend reading those. From a Behind The Scenes perspective it is all implemented using our current converter and binding infrastructure.
Primitives / Values get converted to
base::Value
and then converted back tov8::Value
but in the other context. Functions get stored asv8::Global
and then proxied into a bound cpp method that calls the original function in context A. Then does the primitive move trick and moves it to be the return value for context B. Their is special logic to proxy promise return values but you can see that both in the docs and the source code.TODO:
debugGC
in a testing buildNotes: Added new
contextBridge
module to make it easier to communicate between an isolated context and the main world