diff --git a/shell/renderer/api/electron_api_ipc_renderer.cc b/shell/renderer/api/electron_api_ipc_renderer.cc index a70ebfa240dd..c0808fa698b5 100644 --- a/shell/renderer/api/electron_api_ipc_renderer.cc +++ b/shell/renderer/api/electron_api_ipc_renderer.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/function_template_extensions.h" #include "shell/common/gin_helper/promise.h" #include "shell/common/node_bindings.h" @@ -29,6 +30,9 @@ using content::RenderFrame; namespace { +const char kIPCMethodCalledAfterContextReleasedError[] = + "IPC method called after context was released"; + RenderFrame* GetCurrentRenderFrame() { WebLocalFrame* frame = WebLocalFrame::FrameForCurrentContext(); if (!frame) @@ -83,9 +87,14 @@ class IPCRenderer : public gin::Wrappable, private: void SendMessage(v8::Isolate* isolate, + gin_helper::ErrorThrower thrower, bool internal, const std::string& channel, v8::Local arguments) { + if (!electron_browser_ptr_) { + thrower.ThrowError(kIPCMethodCalledAfterContextReleasedError); + return; + } blink::CloneableMessage message; if (!electron::SerializeV8Value(isolate, arguments, &message)) { return; @@ -94,9 +103,14 @@ class IPCRenderer : public gin::Wrappable, } v8::Local Invoke(v8::Isolate* isolate, + gin_helper::ErrorThrower thrower, bool internal, const std::string& channel, v8::Local arguments) { + if (!electron_browser_ptr_) { + thrower.ThrowError(kIPCMethodCalledAfterContextReleasedError); + return v8::Local(); + } blink::CloneableMessage message; if (!electron::SerializeV8Value(isolate, arguments, &message)) { return v8::Local(); @@ -119,6 +133,10 @@ class IPCRenderer : public gin::Wrappable, const std::string& channel, v8::Local message_value, base::Optional> transfer) { + if (!electron_browser_ptr_) { + thrower.ThrowError(kIPCMethodCalledAfterContextReleasedError); + return; + } blink::TransferableMessage transferable_message; if (!electron::SerializeV8Value(isolate, message_value, &transferable_message)) { @@ -152,11 +170,16 @@ class IPCRenderer : public gin::Wrappable, } void SendTo(v8::Isolate* isolate, + gin_helper::ErrorThrower thrower, bool internal, bool send_to_all, int32_t web_contents_id, const std::string& channel, v8::Local arguments) { + if (!electron_browser_ptr_) { + thrower.ThrowError(kIPCMethodCalledAfterContextReleasedError); + return; + } blink::CloneableMessage message; if (!electron::SerializeV8Value(isolate, arguments, &message)) { return; @@ -166,8 +189,13 @@ class IPCRenderer : public gin::Wrappable, } void SendToHost(v8::Isolate* isolate, + gin_helper::ErrorThrower thrower, const std::string& channel, v8::Local arguments) { + if (!electron_browser_ptr_) { + thrower.ThrowError(kIPCMethodCalledAfterContextReleasedError); + return; + } blink::CloneableMessage message; if (!electron::SerializeV8Value(isolate, arguments, &message)) { return; @@ -176,9 +204,14 @@ class IPCRenderer : public gin::Wrappable, } v8::Local SendSync(v8::Isolate* isolate, + gin_helper::ErrorThrower thrower, bool internal, const std::string& channel, v8::Local arguments) { + if (!electron_browser_ptr_) { + thrower.ThrowError(kIPCMethodCalledAfterContextReleasedError); + return v8::Local(); + } blink::CloneableMessage message; if (!electron::SerializeV8Value(isolate, arguments, &message)) { return v8::Local(); diff --git a/spec-main/api-ipc-renderer-spec.ts b/spec-main/api-ipc-renderer-spec.ts index 99675e66fa76..51f97e48e39d 100644 --- a/spec-main/api-ipc-renderer-spec.ts +++ b/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,25 @@ 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 => { + setTimeout(() => { + try { + childIpc.send('hello'); + } catch (e) { + resolve(e); + } + resolve(false); + }, 100); + }); + }})()`); + expect(error).to.have.property('message', 'IPC method called after context was released'); + }); + }); });