Browse Source

fix: errors thrown in functions over the `contextBridge` (#28447)

* fix: errors thrown in functions over the contextBridge

* spec: add a test

* fix: ensure exception is a v8::Object
Shelley Vohr 4 years ago
parent
commit
deb667054c

+ 20 - 10
shell/renderer/api/electron_api_context_bridge.cc

@@ -391,21 +391,31 @@ void ProxyFunctionWrapper(const v8::FunctionCallbackInfo<v8::Value>& info) {
 
     v8::MaybeLocal<v8::Value> maybe_return_value;
     bool did_error = false;
-    std::string error_message;
+    v8::Local<v8::Value> error_message;
     {
       v8::TryCatch try_catch(args.isolate());
       maybe_return_value = func->Call(func_owning_context, func,
                                       proxied_args.size(), proxied_args.data());
       if (try_catch.HasCaught()) {
         did_error = true;
-        auto message = try_catch.Message();
-
-        if (message.IsEmpty() ||
-            !gin::ConvertFromV8(args.isolate(), message->Get(),
-                                &error_message)) {
-          error_message =
-              "An unknown exception occurred in the isolated context, an error "
-              "occurred but a valid exception was not thrown.";
+        v8::Local<v8::Value> exception = try_catch.Exception();
+
+        const char* err_msg =
+            "An unknown exception occurred in the isolated context, an error "
+            "occurred but a valid exception was not thrown.";
+
+        if (!exception->IsNull() && exception->IsObject()) {
+          v8::MaybeLocal<v8::Value> maybe_message =
+              exception.As<v8::Object>()->Get(
+                  func_owning_context,
+                  gin::ConvertToV8(args.isolate(), "message"));
+
+          if (!maybe_message.ToLocal(&error_message) ||
+              !error_message->IsString()) {
+            error_message = gin::StringToV8(args.isolate(), err_msg);
+          }
+        } else {
+          error_message = gin::StringToV8(args.isolate(), err_msg);
         }
       }
     }
@@ -413,7 +423,7 @@ void ProxyFunctionWrapper(const v8::FunctionCallbackInfo<v8::Value>& info) {
     if (did_error) {
       v8::Context::Scope calling_context_scope(calling_context);
       args.isolate()->ThrowException(
-          v8::Exception::Error(gin::StringToV8(args.isolate(), error_message)));
+          v8::Exception::Error(error_message.As<v8::String>()));
       return;
     }
 

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

@@ -352,6 +352,20 @@ describe('contextBridge', () => {
         expect(result).equal('return-value');
       });
 
+      it('should properly handle errors thrown in proxied functions', async () => {
+        await makeBindingWindow(() => {
+          contextBridge.exposeInMainWorld('example', () => { throw new Error('oh no'); });
+        });
+        const result = await callWithBindings(async (root: any) => {
+          try {
+            root.example();
+          } catch (e) {
+            return e.message;
+          }
+        });
+        expect(result).equal('oh no');
+      });
+
       it('should proxy methods that are callable multiple times', async () => {
         await makeBindingWindow(() => {
           contextBridge.exposeInMainWorld('example', {