Browse Source

fix: throw instead of crash when using ipcRenderer after context released (#23917) (#23978)

* fix: throw instead of crash when using ipcRenderer after context released (#23917)

* meh

* test: reduce flakiness of post-context-release ipc test
Jeremy Rose 4 years ago
parent
commit
4c485e53a4
2 changed files with 50 additions and 1 deletions
  1. 29 0
      shell/renderer/api/electron_api_renderer_ipc.cc
  2. 21 1
      spec-main/api-ipc-renderer-spec.ts

+ 29 - 0
shell/renderer/api/electron_api_renderer_ipc.cc

@@ -16,6 +16,7 @@
 #include "shell/common/api/api.mojom.h"
 #include "shell/common/gin_converters/blink_converter.h"
 #include "shell/common/gin_converters/value_converter.h"
+#include "shell/common/gin_helper/error_thrower.h"
 #include "shell/common/gin_helper/promise.h"
 #include "shell/common/node_bindings.h"
 #include "shell/common/node_includes.h"
@@ -26,6 +27,9 @@ using content::RenderFrame;
 
 namespace {
 
+const char kIPCMethodCalledAfterContextReleasedError[] =
+    "IPC method called after context was released";
+
 RenderFrame* GetCurrentRenderFrame() {
   WebLocalFrame* frame = WebLocalFrame::FrameForCurrentContext();
   if (!frame)
@@ -82,6 +86,11 @@ class IPCRenderer : public gin::Wrappable<IPCRenderer>,
                    bool internal,
                    const std::string& channel,
                    v8::Local<v8::Value> arguments) {
+    if (!electron_browser_ptr_) {
+      gin_helper::ErrorThrower(isolate).ThrowError(
+          kIPCMethodCalledAfterContextReleasedError);
+      return;
+    }
     blink::CloneableMessage message;
     if (!gin::ConvertFromV8(isolate, arguments, &message)) {
       return;
@@ -93,6 +102,11 @@ class IPCRenderer : public gin::Wrappable<IPCRenderer>,
                                 bool internal,
                                 const std::string& channel,
                                 v8::Local<v8::Value> arguments) {
+    if (!electron_browser_ptr_) {
+      gin_helper::ErrorThrower(isolate).ThrowError(
+          kIPCMethodCalledAfterContextReleasedError);
+      return v8::Local<v8::Promise>();
+    }
     blink::CloneableMessage message;
     if (!gin::ConvertFromV8(isolate, arguments, &message)) {
       return v8::Local<v8::Promise>();
@@ -116,6 +130,11 @@ class IPCRenderer : public gin::Wrappable<IPCRenderer>,
               int32_t web_contents_id,
               const std::string& channel,
               v8::Local<v8::Value> arguments) {
+    if (!electron_browser_ptr_) {
+      gin_helper::ErrorThrower(isolate).ThrowError(
+          kIPCMethodCalledAfterContextReleasedError);
+      return;
+    }
     blink::CloneableMessage message;
     if (!gin::ConvertFromV8(isolate, arguments, &message)) {
       return;
@@ -127,6 +146,11 @@ class IPCRenderer : public gin::Wrappable<IPCRenderer>,
   void SendToHost(v8::Isolate* isolate,
                   const std::string& channel,
                   v8::Local<v8::Value> arguments) {
+    if (!electron_browser_ptr_) {
+      gin_helper::ErrorThrower(isolate).ThrowError(
+          kIPCMethodCalledAfterContextReleasedError);
+      return;
+    }
     blink::CloneableMessage message;
     if (!gin::ConvertFromV8(isolate, arguments, &message)) {
       return;
@@ -138,6 +162,11 @@ class IPCRenderer : public gin::Wrappable<IPCRenderer>,
                                    bool internal,
                                    const std::string& channel,
                                    v8::Local<v8::Value> arguments) {
+    if (!electron_browser_ptr_) {
+      gin_helper::ErrorThrower(isolate).ThrowError(
+          kIPCMethodCalledAfterContextReleasedError);
+      return blink::CloneableMessage();
+    }
     blink::CloneableMessage message;
     if (!gin::ConvertFromV8(isolate, arguments, &message)) {
       return blink::CloneableMessage();

+ 21 - 1
spec-main/api-ipc-renderer-spec.ts

@@ -9,7 +9,7 @@ describe('ipcRenderer module', () => {
 
   let w: BrowserWindow;
   before(async () => {
-    w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } });
+    w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, nativeWindowOpen: true } });
     await w.loadURL('about:blank');
   });
   after(async () => {
@@ -182,4 +182,24 @@ describe('ipcRenderer module', () => {
       expect(result).to.deep.equal([]);
     });
   });
+
+  describe('after context is released', () => {
+    it('throws an exception', async () => {
+      const error = await w.webContents.executeJavaScript(`(${() => {
+        const child = window.open('', 'child', 'show=no,nodeIntegration=yes')! as any;
+        const childIpc = child.require('electron').ipcRenderer;
+        child.close();
+        return new Promise(resolve => {
+          setInterval(() => {
+            try {
+              childIpc.send('hello');
+            } catch (e) {
+              resolve(e);
+            }
+          }, 16);
+        });
+      }})()`);
+      expect(error).to.have.property('message', 'IPC method called after context was released');
+    });
+  });
 });