Browse Source

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

Co-authored-by: Samuel Attard <[email protected]>
trop[bot] 4 years ago
parent
commit
2d747acd67

+ 2 - 1
.eslintrc.json

@@ -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": [
     {

+ 11 - 0
shell/renderer/api/electron_api_context_bridge.cc

@@ -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()) {

+ 18 - 0
spec-main/api-context-bridge-spec.ts

@@ -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', {
@@ -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',
@@ -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],

+ 1 - 1
tsconfig.json

@@ -1,7 +1,7 @@
 {
   "compilerOptions": {
     "module": "commonjs",
-    "target": "es2017",
+    "target": "es2020",
     "lib": [
       "es2019",
       "dom",