diff --git a/.eslintrc.json b/.eslintrc.json index 85222dd945f88..33ab42f1eff17 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -32,7 +32,8 @@ "ENABLE_DESKTOP_CAPTURER": "readonly", "ENABLE_ELECTRON_EXTENSIONS": "readonly", "ENABLE_REMOTE_MODULE": "readonly", - "ENABLE_VIEW_API": "readonly" + "ENABLE_VIEW_API": "readonly", + "BigInt": "readonly" }, "overrides": [ { diff --git a/shell/renderer/api/electron_api_context_bridge.cc b/shell/renderer/api/electron_api_context_bridge.cc index 1eaeb17442fcf..e2a74894039d8 100644 --- a/shell/renderer/api/electron_api_context_bridge.cc +++ b/shell/renderer/api/electron_api_context_bridge.cc @@ -147,6 +147,17 @@ v8::MaybeLocal PassValueToOtherContext( return v8::MaybeLocal(); } } + + // 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(value); + } + // Check Cache auto cached_value = object_cache->GetCachedProxiedObject(value); if (!cached_value.IsEmpty()) { diff --git a/spec-main/api-context-bridge-spec.ts b/spec-main/api-context-bridge-spec.ts index dd453bd64b7d0..bb33e2cde90ed 100644 --- a/spec-main/api-context-bridge-spec.ts +++ b/spec-main/api-context-bridge-spec.ts @@ -289,7 +289,7 @@ describe('contextBridge', () => { contextBridge.exposeInMainWorld('example', { 1: 123, 2: 456, - '3': 789 + 3: 789 }); }); const result = await callWithBindings(async (root: any) => { @@ -312,6 +312,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', { @@ -447,7 +461,7 @@ describe('contextBridge', () => { try { let a: any = []; for (let i = 0; i < 999; i++) { - a = [ a ]; + a = [a]; } root.example.doThing(a); return false; @@ -460,7 +474,7 @@ describe('contextBridge', () => { try { let a: any = []; for (let i = 0; i < 1000; i++) { - a = [ a ]; + a = [a]; } root.example.doThing(a); return false; @@ -478,6 +492,7 @@ describe('contextBridge', () => { string: 'string', boolean: true, arr: [123, 'string', true, ['foo']], + symbol: Symbol('foo'), getObject: () => ({ thing: 123 }), getNumber: () => 123, getString: () => 'string', @@ -510,6 +525,7 @@ describe('contextBridge', () => { [example.arr[2], Boolean], [example.arr[3], Array], [example.arr[3][0], String], + [example.symbol, Symbol], [example.getNumber, Function], [example.getNumber(), Number], [example.getObject(), Object], diff --git a/tsconfig.json b/tsconfig.json index 0abc258da4566..b886d28038c47 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "module": "commonjs", - "target": "es2017", + "target": "es2020", "lib": [ "es2019", "dom",