Skip to content
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

perf: pass primitives directly through the context bridge, avoids copying #24551

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion .eslintrc.json
Expand Up @@ -31,7 +31,8 @@
"BUILDFLAG": "readonly",
"ENABLE_DESKTOP_CAPTURER": "readonly",
"ENABLE_REMOTE_MODULE": "readonly",
"ENABLE_VIEWS_API": "readonly"
"ENABLE_VIEWS_API": "readonly",
"BigInt": "readonly"
},
"overrides": [
{
Expand Down
11 changes: 11 additions & 0 deletions shell/renderer/api/electron_api_context_bridge.cc
Expand Up @@ -146,6 +146,17 @@ v8::MaybeLocal<v8::Value> PassValueToOtherContext(
"deeper than 1000 are not supported.")));
return v8::MaybeLocal<v8::Value>();
}

// Certain primitives always use the current contexts prototype and we can
// pass these through directly which is significantly more performant than
// copying them. This list of primitives is based on the classification of
// "primitive value" as defined in the ECMA262 spec
// https://tc39.es/ecma262/#sec-primitive-value
if (value->IsString() || value->IsNumber() || value->IsNullOrUndefined() ||
value->IsBoolean() || value->IsSymbol() || value->IsBigInt()) {
return v8::MaybeLocal<v8::Value>(value);
}

// Check Cache
auto cached_value = object_cache->GetCachedProxiedObject(value);
if (!cached_value.IsEmpty()) {
Expand Down
18 changes: 18 additions & 0 deletions spec-main/api-context-bridge-spec.ts
Expand Up @@ -313,6 +313,20 @@ describe('contextBridge', () => {
expect(result).to.deep.equal(['null', 'undefined']);
});

it('should proxy symbols such that symbol equality works', async () => {
await makeBindingWindow(() => {
const mySymbol = Symbol('unique');
contextBridge.exposeInMainWorld('example', {
getSymbol: () => mySymbol,
isSymbol: (s: Symbol) => s === mySymbol
});
});
const result = await callWithBindings((root: any) => {
return root.example.isSymbol(root.example.getSymbol());
});
expect(result).to.equal(true, 'symbols should be equal across contexts');
});

it('should proxy typed arrays and regexps through the serializer', async () => {
await makeBindingWindow(() => {
contextBridge.exposeInMainWorld('example', {
Expand Down Expand Up @@ -479,6 +493,8 @@ describe('contextBridge', () => {
string: 'string',
boolean: true,
arr: [123, 'string', true, ['foo']],
symbol: Symbol('foo'),
bigInt: 10n,
getObject: () => ({ thing: 123 }),
getNumber: () => 123,
getString: () => 'string',
Expand Down Expand Up @@ -511,6 +527,8 @@ describe('contextBridge', () => {
[example.arr[2], Boolean],
[example.arr[3], Array],
[example.arr[3][0], String],
[example.symbol, Symbol],
[example.bigInt, BigInt],
[example.getNumber, Function],
[example.getNumber(), Number],
[example.getObject(), Object],
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
@@ -1,7 +1,7 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es2017",
"target": "es2020",
"lib": [
"es2019",
"dom",
Expand Down