Browse Source

refactor: use separate ipc-renderer-internal / ipc-main-internal APIs for Electron internals (#13940)

Milan Burda 6 years ago
parent
commit
b50f86ef43
49 changed files with 322 additions and 133 deletions
  1. 11 6
      atom/browser/api/atom_api_web_contents.cc
  2. 5 2
      atom/browser/api/atom_api_web_contents.h
  3. 4 2
      atom/common/api/api_messages.h
  4. 2 2
      atom/common/api/remote_callback_freer.cc
  5. 1 1
      atom/common/api/remote_object_freer.cc
  6. 4 3
      atom/renderer/api/atom_api_renderer_ipc.cc
  7. 1 0
      atom/renderer/api/atom_api_renderer_ipc.h
  8. 10 5
      atom/renderer/atom_render_frame_observer.cc
  9. 3 1
      atom/renderer/atom_render_frame_observer.h
  10. 2 1
      atom/renderer/atom_sandboxed_renderer_client.cc
  11. 5 1
      filenames.gni
  12. 2 1
      lib/browser/api/browser-window.js
  13. 14 0
      lib/browser/api/crash-reporter.js
  14. 1 9
      lib/browser/api/ipc-main.js
  15. 1 0
      lib/browser/api/module-list.js
  16. 1 1
      lib/browser/api/navigation-controller.js
  17. 54 7
      lib/browser/api/web-contents.js
  18. 10 9
      lib/browser/chrome-extension.js
  19. 5 3
      lib/browser/desktop-capturer.js
  20. 7 6
      lib/browser/guest-view-manager.js
  21. 4 3
      lib/browser/guest-window-manager.js
  22. 10 0
      lib/browser/ipc-main-internal.js
  23. 14 5
      lib/browser/rpc-server.js
  24. 0 1
      lib/common/api/module-list.js
  25. 17 12
      lib/common/crash-reporter.js
  26. 2 2
      lib/isolated_renderer/init.js
  27. 12 0
      lib/renderer/api/crash-reporter.js
  28. 2 1
      lib/renderer/api/desktop-capturer.js
  29. 3 10
      lib/renderer/api/ipc-renderer.js
  30. 1 0
      lib/renderer/api/module-list.js
  31. 2 1
      lib/renderer/api/remote.js
  32. 3 3
      lib/renderer/chrome-api.js
  33. 1 1
      lib/renderer/content-scripts-injector.js
  34. 1 1
      lib/renderer/extensions/i18n.js
  35. 1 1
      lib/renderer/extensions/web-navigation.js
  36. 4 3
      lib/renderer/init.js
  37. 26 0
      lib/renderer/ipc-renderer-internal.js
  38. 1 1
      lib/renderer/override.js
  39. 1 1
      lib/renderer/security-warnings.js
  40. 2 1
      lib/renderer/web-frame-init.js
  41. 2 1
      lib/renderer/web-view/guest-view-internal.js
  42. 1 1
      lib/renderer/web-view/web-view-attributes.js
  43. 2 1
      lib/renderer/web-view/web-view.js
  44. 2 2
      lib/sandboxed_renderer/api/module-list.js
  45. 23 1
      lib/sandboxed_renderer/init.js
  46. 18 12
      spec/api-ipc-main-spec.js
  47. 10 8
      spec/api-ipc-renderer-spec.js
  48. 10 0
      spec/fixtures/api/ipc-main-listeners/main.js
  49. 4 0
      spec/fixtures/api/ipc-main-listeners/package.json

+ 11 - 6
atom/browser/api/atom_api_web_contents.cc

@@ -1578,20 +1578,23 @@ void WebContents::TabTraverse(bool reverse) {
   web_contents()->FocusThroughTabTraversal(reverse);
 }
 
-bool WebContents::SendIPCMessage(bool all_frames,
+bool WebContents::SendIPCMessage(bool internal,
+                                 bool send_to_all,
                                  const std::string& channel,
                                  const base::ListValue& args) {
-  return SendIPCMessageWithSender(all_frames, channel, args);
+  return SendIPCMessageWithSender(internal, send_to_all, channel, args);
 }
 
-bool WebContents::SendIPCMessageWithSender(bool all_frames,
+bool WebContents::SendIPCMessageWithSender(bool internal,
+                                           bool send_to_all,
                                            const std::string& channel,
                                            const base::ListValue& args,
                                            int32_t sender_id) {
   auto* frame_host = web_contents()->GetMainFrame();
   if (frame_host) {
-    return frame_host->Send(new AtomFrameMsg_Message(
-        frame_host->GetRoutingID(), all_frames, channel, args, sender_id));
+    return frame_host->Send(new AtomFrameMsg_Message(frame_host->GetRoutingID(),
+                                                     internal, send_to_all,
+                                                     channel, args, sender_id));
   }
   return false;
 }
@@ -2107,6 +2110,7 @@ void WebContents::OnRendererMessageSync(content::RenderFrameHost* frame_host,
 }
 
 void WebContents::OnRendererMessageTo(content::RenderFrameHost* frame_host,
+                                      bool internal,
                                       bool send_to_all,
                                       int32_t web_contents_id,
                                       const std::string& channel,
@@ -2115,7 +2119,8 @@ void WebContents::OnRendererMessageTo(content::RenderFrameHost* frame_host,
       isolate(), web_contents_id);
 
   if (web_contents) {
-    web_contents->SendIPCMessageWithSender(send_to_all, channel, args, ID());
+    web_contents->SendIPCMessageWithSender(internal, send_to_all, channel, args,
+                                           ID());
   }
 }
 

+ 5 - 2
atom/browser/api/atom_api_web_contents.h

@@ -179,11 +179,13 @@ class WebContents : public mate::TrackableObject<WebContents>,
   void TabTraverse(bool reverse);
 
   // Send messages to browser.
-  bool SendIPCMessage(bool all_frames,
+  bool SendIPCMessage(bool internal,
+                      bool send_to_all,
                       const std::string& channel,
                       const base::ListValue& args);
 
-  bool SendIPCMessageWithSender(bool all_frames,
+  bool SendIPCMessageWithSender(bool internal,
+                                bool send_to_all,
                                 const std::string& channel,
                                 const base::ListValue& args,
                                 int32_t sender_id = 0);
@@ -442,6 +444,7 @@ class WebContents : public mate::TrackableObject<WebContents>,
 
   // Called when received a message from renderer to be forwarded.
   void OnRendererMessageTo(content::RenderFrameHost* frame_host,
+                           bool internal,
                            bool send_to_all,
                            int32_t web_contents_id,
                            const std::string& channel,

+ 4 - 2
atom/common/api/api_messages.h

@@ -34,13 +34,15 @@ IPC_SYNC_MESSAGE_ROUTED2_1(AtomFrameHostMsg_Message_Sync,
                            base::ListValue /* arguments */,
                            base::ListValue /* result */)
 
-IPC_MESSAGE_ROUTED4(AtomFrameHostMsg_Message_To,
+IPC_MESSAGE_ROUTED5(AtomFrameHostMsg_Message_To,
+                    bool /* internal */,
                     bool /* send_to_all */,
                     int32_t /* web_contents_id */,
                     std::string /* channel */,
                     base::ListValue /* arguments */)
 
-IPC_MESSAGE_ROUTED4(AtomFrameMsg_Message,
+IPC_MESSAGE_ROUTED5(AtomFrameMsg_Message,
+                    bool /* internal */,
                     bool /* send_to_all */,
                     std::string /* channel */,
                     base::ListValue /* arguments */,

+ 2 - 2
atom/common/api/remote_callback_freer.cc

@@ -41,8 +41,8 @@ void RemoteCallbackFreer::RunDestructor() {
   args.AppendInteger(object_id_);
   auto* frame_host = web_contents()->GetMainFrame();
   if (frame_host) {
-    frame_host->Send(new AtomFrameMsg_Message(frame_host->GetRoutingID(), false,
-                                              channel, args, sender_id));
+    frame_host->Send(new AtomFrameMsg_Message(frame_host->GetRoutingID(), true,
+                                              false, channel, args, sender_id));
   }
 
   Observe(nullptr);

+ 1 - 1
atom/common/api/remote_object_freer.cc

@@ -56,7 +56,7 @@ void RemoteObjectFreer::RunDestructor() {
   if (!render_frame)
     return;
 
-  auto* channel = "ipc-message";
+  auto* channel = "ipc-internal-message";
   base::ListValue args;
   args.AppendString("ELECTRON_BROWSER_DEREFERENCE");
   args.AppendString(context_id_);

+ 4 - 3
atom/renderer/api/atom_api_renderer_ipc.cc

@@ -61,6 +61,7 @@ base::ListValue SendSync(mate::Arguments* args,
 }
 
 void SendTo(mate::Arguments* args,
+            bool internal,
             bool send_to_all,
             int32_t web_contents_id,
             const std::string& channel,
@@ -69,9 +70,9 @@ void SendTo(mate::Arguments* args,
   if (render_frame == nullptr)
     return;
 
-  bool success = render_frame->Send(
-      new AtomFrameHostMsg_Message_To(render_frame->GetRoutingID(), send_to_all,
-                                      web_contents_id, channel, arguments));
+  bool success = render_frame->Send(new AtomFrameHostMsg_Message_To(
+      render_frame->GetRoutingID(), internal, send_to_all, web_contents_id,
+      channel, arguments));
 
   if (!success)
     args->ThrowError("Unable to send AtomFrameHostMsg_Message_To");

+ 1 - 0
atom/renderer/api/atom_api_renderer_ipc.h

@@ -23,6 +23,7 @@ base::ListValue SendSync(mate::Arguments* args,
                          const base::ListValue& arguments);
 
 void SendTo(mate::Arguments* args,
+            bool internal,
             bool send_to_all,
             int32_t web_contents_id,
             const std::string& channel,

+ 10 - 5
atom/renderer/atom_render_frame_observer.cc

@@ -35,8 +35,10 @@ namespace {
 
 bool GetIPCObject(v8::Isolate* isolate,
                   v8::Local<v8::Context> context,
+                  bool internal,
                   v8::Local<v8::Object>* ipc) {
-  v8::Local<v8::String> key = mate::StringToV8(isolate, "ipc");
+  v8::Local<v8::String> key =
+      mate::StringToV8(isolate, internal ? "ipc-internal" : "ipc");
   v8::Local<v8::Private> privateKey = v8::Private::ForApi(isolate, key);
   v8::Local<v8::Object> global_object = context->Global();
   v8::Local<v8::Value> value;
@@ -170,7 +172,8 @@ bool AtomRenderFrameObserver::OnMessageReceived(const IPC::Message& message) {
   return handled;
 }
 
-void AtomRenderFrameObserver::OnBrowserMessage(bool send_to_all,
+void AtomRenderFrameObserver::OnBrowserMessage(bool internal,
+                                               bool send_to_all,
                                                const std::string& channel,
                                                const base::ListValue& args,
                                                int32_t sender_id) {
@@ -186,14 +189,15 @@ void AtomRenderFrameObserver::OnBrowserMessage(bool send_to_all,
   if (!frame || !render_frame_->IsMainFrame())
     return;
 
-  EmitIPCEvent(frame, channel, args, sender_id);
+  EmitIPCEvent(frame, internal, channel, args, sender_id);
 
   // Also send the message to all sub-frames.
   if (send_to_all) {
     for (blink::WebFrame* child = frame->FirstChild(); child;
          child = child->NextSibling())
       if (child->IsWebLocalFrame()) {
-        EmitIPCEvent(child->ToWebLocalFrame(), channel, args, sender_id);
+        EmitIPCEvent(child->ToWebLocalFrame(), internal, channel, args,
+                     sender_id);
       }
   }
 }
@@ -215,6 +219,7 @@ void AtomRenderFrameObserver::OnTakeHeapSnapshot(
 }
 
 void AtomRenderFrameObserver::EmitIPCEvent(blink::WebLocalFrame* frame,
+                                           bool internal,
                                            const std::string& channel,
                                            const base::ListValue& args,
                                            int32_t sender_id) {
@@ -233,7 +238,7 @@ void AtomRenderFrameObserver::EmitIPCEvent(blink::WebLocalFrame* frame,
     return;
 
   v8::Local<v8::Object> ipc;
-  if (GetIPCObject(isolate, context, &ipc)) {
+  if (GetIPCObject(isolate, context, internal, &ipc)) {
     TRACE_EVENT0("devtools.timeline", "FunctionCall");
     auto args_vector = ListValueToVector(isolate, args);
     // Insert the Event object, event.sender is ipc.

+ 3 - 1
atom/renderer/atom_render_frame_observer.h

@@ -45,6 +45,7 @@ class AtomRenderFrameObserver : public content::RenderFrameObserver {
 
  protected:
   virtual void EmitIPCEvent(blink::WebLocalFrame* frame,
+                            bool internal,
                             const std::string& channel,
                             const base::ListValue& args,
                             int32_t sender_id);
@@ -54,7 +55,8 @@ class AtomRenderFrameObserver : public content::RenderFrameObserver {
   void CreateIsolatedWorldContext();
   bool IsMainWorld(int world_id);
   bool IsIsolatedWorld(int world_id);
-  void OnBrowserMessage(bool send_to_all,
+  void OnBrowserMessage(bool internal,
+                        bool send_to_all,
                         const std::string& channel,
                         const base::ListValue& args,
                         int32_t sender_id);

+ 2 - 1
atom/renderer/atom_sandboxed_renderer_client.cc

@@ -103,6 +103,7 @@ class AtomSandboxedRenderFrameObserver : public AtomRenderFrameObserver {
 
  protected:
   void EmitIPCEvent(blink::WebLocalFrame* frame,
+                    bool internal,
                     const std::string& channel,
                     const base::ListValue& args,
                     int32_t sender_id) override {
@@ -117,7 +118,7 @@ class AtomSandboxedRenderFrameObserver : public AtomRenderFrameObserver {
                                    mate::ConvertToV8(isolate, args),
                                    mate::ConvertToV8(isolate, sender_id)};
     renderer_client_->InvokeIpcCallback(
-        context, "onMessage",
+        context, internal ? "onInternalMessage" : "onMessage",
         std::vector<v8::Local<v8::Value>>(argv, argv + node::arraysize(argv)));
   }
 

+ 5 - 1
filenames.gni

@@ -13,6 +13,7 @@ filenames = {
     "lib/browser/api/browser-view.js",
     "lib/browser/api/browser-window.js",
     "lib/browser/api/content-tracing.js",
+    "lib/browser/api/crash-reporter.js",
     "lib/browser/api/dialog.js",
     "lib/browser/api/exports/electron.js",
     "lib/browser/api/global-shortcut.js",
@@ -43,10 +44,10 @@ filenames = {
     "lib/browser/guest-view-manager.js",
     "lib/browser/guest-window-manager.js",
     "lib/browser/init.js",
+    "lib/browser/ipc-main-internal.js",
     "lib/browser/objects-registry.js",
     "lib/browser/rpc-server.js",
     "lib/common/api/clipboard.js",
-    "lib/common/api/crash-reporter.js",
     "lib/common/api/deprecate.js",
     "lib/common/api/deprecations.js",
     "lib/common/api/is-promise.js",
@@ -56,6 +57,7 @@ filenames = {
     "lib/common/api/shell.js",
     "lib/common/atom-binding-setup.js",
     "lib/common/buffer-utils.js",
+    "lib/common/crash-reporter.js",
     "lib/common/error-utils.js",
     "lib/common/init.js",
     "lib/common/parse-features-string.js",
@@ -65,6 +67,7 @@ filenames = {
     "lib/renderer/content-scripts-injector.js",
     "lib/renderer/init.js",
     "lib/renderer/inspector.js",
+    "lib/renderer/ipc-renderer-internal.js",
     "lib/renderer/override.js",
     "lib/renderer/security-warnings.js",
     "lib/renderer/web-frame-init.js",
@@ -74,6 +77,7 @@ filenames = {
     "lib/renderer/web-view/web-view-attributes.js",
     "lib/renderer/web-view/web-view-constants.js",
     "lib/renderer/api/exports/electron.js",
+    "lib/renderer/api/crash-reporter.js",
     "lib/renderer/api/ipc-renderer.js",
     "lib/renderer/api/module-list.js",
     "lib/renderer/api/remote.js",

+ 2 - 1
lib/browser/api/browser-window.js

@@ -1,9 +1,10 @@
 'use strict'
 
 const electron = require('electron')
-const { ipcMain, WebContentsView, TopLevelWindow } = electron
+const { WebContentsView, TopLevelWindow } = electron
 const { BrowserWindow } = process.atomBinding('window')
 const v8Util = process.atomBinding('v8_util')
+const ipcMain = require('@electron/internal/browser/ipc-main-internal')
 
 Object.setPrototypeOf(BrowserWindow.prototype, TopLevelWindow.prototype)
 

+ 14 - 0
lib/browser/api/crash-reporter.js

@@ -0,0 +1,14 @@
+'use strict'
+
+const CrashReporter = require('@electron/internal/common/crash-reporter')
+const ipcMain = require('@electron/internal/browser/ipc-main-internal')
+
+class CrashReporterMain extends CrashReporter {
+  sendSync (channel, ...args) {
+    const event = {}
+    ipcMain.emit(channel, event, ...args)
+    return event.returnValue
+  }
+}
+
+module.exports = new CrashReporterMain()

+ 1 - 9
lib/browser/api/ipc-main.js

@@ -1,17 +1,9 @@
 'use strict'
 
-const EventEmitter = require('events').EventEmitter
+const { EventEmitter } = require('events')
 
 const emitter = new EventEmitter()
 
-const removeAllListeners = emitter.removeAllListeners.bind(emitter)
-emitter.removeAllListeners = function (...args) {
-  if (args.length === 0) {
-    throw new Error('Removing all listeners from ipcMain will make Electron internals stop working.  Please specify a event name')
-  }
-  removeAllListeners(...args)
-}
-
 // Do not throw exception when channel name is "error".
 emitter.on('error', () => {})
 

+ 1 - 0
lib/browser/api/module-list.js

@@ -9,6 +9,7 @@ module.exports = [
   { name: 'BrowserView', file: 'browser-view' },
   { name: 'BrowserWindow', file: 'browser-window' },
   { name: 'contentTracing', file: 'content-tracing' },
+  { name: 'crashReporter', file: 'crash-reporter' },
   { name: 'dialog', file: 'dialog' },
   { name: 'globalShortcut', file: 'global-shortcut' },
   { name: 'ipcMain', file: 'ipc-main' },

+ 1 - 1
lib/browser/api/navigation-controller.js

@@ -1,6 +1,6 @@
 'use strict'
 
-const { ipcMain } = require('electron')
+const ipcMain = require('@electron/internal/browser/ipc-main-internal')
 
 // The history operation in renderer is redirected to browser.
 ipcMain.on('ELECTRON_NAVIGATION_CONTROLLER', function (event, method, ...args) {

+ 54 - 7
lib/browser/api/web-contents.js

@@ -6,6 +6,7 @@ const path = require('path')
 const url = require('url')
 const { app, ipcMain, session, NavigationController, deprecate } = electron
 
+const ipcMainInternal = require('@electron/internal/browser/ipc-main-internal')
 const errorUtils = require('@electron/internal/common/error-utils')
 
 // session is not used here, the purpose is to make sure session is initalized
@@ -98,12 +99,45 @@ Object.setPrototypeOf(WebContents.prototype, NavigationController.prototype)
 // WebContents::send(channel, args..)
 // WebContents::sendToAll(channel, args..)
 WebContents.prototype.send = function (channel, ...args) {
-  if (channel == null) throw new Error('Missing required channel argument')
-  return this._send(false, channel, args)
+  if (typeof channel !== 'string') {
+    throw new Error('Missing required channel argument')
+  }
+
+  const internal = false
+  const sendToAll = false
+
+  return this._send(internal, sendToAll, channel, args)
 }
 WebContents.prototype.sendToAll = function (channel, ...args) {
-  if (channel == null) throw new Error('Missing required channel argument')
-  return this._send(true, channel, args)
+  if (typeof channel !== 'string') {
+    throw new Error('Missing required channel argument')
+  }
+
+  const internal = false
+  const sendToAll = true
+
+  return this._send(internal, sendToAll, channel, args)
+}
+
+WebContents.prototype._sendInternal = function (channel, ...args) {
+  if (typeof channel !== 'string') {
+    throw new Error('Missing required channel argument')
+  }
+
+  const internal = true
+  const sendToAll = false
+
+  return this._send(internal, sendToAll, channel, args)
+}
+WebContents.prototype._sendInternalToAll = function (channel, ...args) {
+  if (typeof channel !== 'string') {
+    throw new Error('Missing required channel argument')
+  }
+
+  const internal = true
+  const sendToAll = true
+
+  return this._send(internal, sendToAll, channel, args)
 }
 
 // Following methods are mapped to webFrame.
@@ -116,8 +150,7 @@ const webFrameMethods = [
 
 const asyncWebFrameMethods = function (requestId, method, callback, ...args) {
   return new Promise((resolve, reject) => {
-    this.send('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD', requestId, method, args)
-    ipcMain.once(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_${requestId}`, function (event, error, result) {
+    ipcMainInternal.once(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_${requestId}`, function (event, error, result) {
       if (error == null) {
         if (typeof callback === 'function') callback(result)
         resolve(result)
@@ -125,12 +158,13 @@ const asyncWebFrameMethods = function (requestId, method, callback, ...args) {
         reject(errorUtils.deserialize(error))
       }
     })
+    this._sendInternal('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD', requestId, method, args)
   })
 }
 
 for (const method of webFrameMethods) {
   WebContents.prototype[method] = function (...args) {
-    this.send('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', method, args)
+    this._sendInternal('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', method, args)
   }
 }
 
@@ -277,6 +311,19 @@ WebContents.prototype._init = function () {
     ipcMain.emit(channel, event, ...args)
   })
 
+  this.on('ipc-internal-message', function (event, [channel, ...args]) {
+    ipcMainInternal.emit(channel, event, ...args)
+  })
+  this.on('ipc-internal-message-sync', function (event, [channel, ...args]) {
+    Object.defineProperty(event, 'returnValue', {
+      set: function (value) {
+        return event.sendReply([value])
+      },
+      get: function () {}
+    })
+    ipcMainInternal.emit(channel, event, ...args)
+  })
+
   // Handle context menu action request from pepper plugin.
   this.on('pepper-context-menu', function (event, params, callback) {
     // Access Menu via electron.Menu to prevent circular require.

+ 10 - 9
lib/browser/chrome-extension.js

@@ -1,8 +1,9 @@
 'use strict'
 
-const { app, ipcMain, webContents, BrowserWindow } = require('electron')
+const { app, webContents, BrowserWindow } = require('electron')
 const { getAllWebContents } = process.atomBinding('web_contents')
 const renderProcessPreferences = process.atomBinding('render_process_preferences').forAllWebContents()
+const ipcMain = require('@electron/internal/browser/ipc-main-internal')
 
 const { Buffer } = require('buffer')
 const fs = require('fs')
@@ -108,7 +109,7 @@ const removeBackgroundPages = function (manifest) {
 
 const sendToBackgroundPages = function (...args) {
   for (const page of Object.values(backgroundPages)) {
-    page.webContents.sendToAll(...args)
+    page.webContents._sendInternalToAll(...args)
   }
 }
 
@@ -160,9 +161,9 @@ ipcMain.on('CHROME_RUNTIME_CONNECT', function (event, extensionId, connectInfo)
 
   event.sender.once('render-view-deleted', () => {
     if (page.webContents.isDestroyed()) return
-    page.webContents.sendToAll(`CHROME_PORT_DISCONNECT_${portId}`)
+    page.webContents._sendInternalToAll(`CHROME_PORT_DISCONNECT_${portId}`)
   })
-  page.webContents.sendToAll(`CHROME_RUNTIME_ONCONNECT_${extensionId}`, event.sender.id, portId, connectInfo)
+  page.webContents._sendInternalToAll(`CHROME_RUNTIME_ONCONNECT_${extensionId}`, event.sender.id, portId, connectInfo)
 })
 
 ipcMain.on('CHROME_I18N_MANIFEST', function (event, extensionId) {
@@ -177,9 +178,9 @@ ipcMain.on('CHROME_RUNTIME_SENDMESSAGE', function (event, extensionId, message,
     return
   }
 
-  page.webContents.sendToAll(`CHROME_RUNTIME_ONMESSAGE_${extensionId}`, event.sender.id, message, resultID)
+  page.webContents._sendInternalToAll(`CHROME_RUNTIME_ONMESSAGE_${extensionId}`, event.sender.id, message, resultID)
   ipcMain.once(`CHROME_RUNTIME_ONMESSAGE_RESULT_${resultID}`, (event, result) => {
-    event.sender.send(`CHROME_RUNTIME_SENDMESSAGE_RESULT_${originResultID}`, result)
+    event.sender._sendInternal(`CHROME_RUNTIME_SENDMESSAGE_RESULT_${originResultID}`, result)
   })
   resultID++
 })
@@ -193,9 +194,9 @@ ipcMain.on('CHROME_TABS_SEND_MESSAGE', function (event, tabId, extensionId, isBa
 
   const senderTabId = isBackgroundPage ? null : event.sender.id
 
-  contents.sendToAll(`CHROME_RUNTIME_ONMESSAGE_${extensionId}`, senderTabId, message, resultID)
+  contents._sendInternalToAll(`CHROME_RUNTIME_ONMESSAGE_${extensionId}`, senderTabId, message, resultID)
   ipcMain.once(`CHROME_RUNTIME_ONMESSAGE_RESULT_${resultID}`, (event, result) => {
-    event.sender.send(`CHROME_TABS_SEND_MESSAGE_RESULT_${originResultID}`, result)
+    event.sender._sendInternal(`CHROME_TABS_SEND_MESSAGE_RESULT_${originResultID}`, result)
   })
   resultID++
 })
@@ -217,7 +218,7 @@ ipcMain.on('CHROME_TABS_EXECUTESCRIPT', function (event, requestId, tabId, exten
     url = `chrome-extension://${extensionId}/${String(Math.random()).substr(2, 8)}.js`
   }
 
-  contents.send('CHROME_TABS_EXECUTESCRIPT', event.sender.id, requestId, extensionId, url, code)
+  contents._sendInternal('CHROME_TABS_EXECUTESCRIPT', event.sender.id, requestId, extensionId, url, code)
 })
 
 // Transfer the content scripts to renderer.

+ 5 - 3
lib/browser/desktop-capturer.js

@@ -1,6 +1,6 @@
 'use strict'
 
-const { ipcMain } = require('electron')
+const ipcMain = require('@electron/internal/browser/ipc-main-internal')
 const { desktopCapturer } = process.atomBinding('desktop_capturer')
 
 const deepEqual = (a, b) => JSON.stringify(a) === JSON.stringify(b)
@@ -49,14 +49,16 @@ desktopCapturer.emit = (event, name, sources) => {
   })
 
   if (handledWebContents) {
-    handledWebContents.send(capturerResult(handledRequest.id), result)
+    handledWebContents._sendInternal(capturerResult(handledRequest.id), result)
   }
 
   // Check the queue to see whether there is another identical request & handle
   requestsQueue.forEach(request => {
     const webContents = request.webContents
     if (deepEqual(handledRequest.options, request.options)) {
-      if (webContents) webContents.send(capturerResult(request.id), result)
+      if (webContents) {
+        webContents._sendInternal(capturerResult(request.id), result)
+      }
     } else {
       unhandledRequestsQueue.push(request)
     }

+ 7 - 6
lib/browser/guest-view-manager.js

@@ -1,6 +1,7 @@
 'use strict'
 
-const { ipcMain, webContents } = require('electron')
+const { webContents } = require('electron')
+const ipcMain = require('@electron/internal/browser/ipc-main-internal')
 const parseFeaturesString = require('@electron/internal/common/parse-features-string')
 
 // Doesn't exist in early initialization.
@@ -118,7 +119,7 @@ const createGuest = function (embedder, params) {
 
   const sendToEmbedder = (channel, ...args) => {
     if (!embedder.isDestroyed()) {
-      embedder.send(`${channel}-${guest.viewInstanceId}`, ...args)
+      embedder._sendInternal(`${channel}-${guest.viewInstanceId}`, ...args)
     }
   }
 
@@ -142,7 +143,7 @@ const createGuest = function (embedder, params) {
   guest.on('dom-ready', function () {
     const guestInstance = guestInstances[guestInstanceId]
     if (guestInstance != null && guestInstance.visibilityState != null) {
-      guest.send('ELECTRON_GUEST_INSTANCE_VISIBILITY_CHANGE', guestInstance.visibilityState)
+      guest._sendInternal('ELECTRON_GUEST_INSTANCE_VISIBILITY_CHANGE', guestInstance.visibilityState)
     }
   })
 
@@ -201,7 +202,7 @@ const attachGuest = function (event, embedderFrameId, elementInstanceId, guestIn
     // Remove guest from embedder if moving across web views
     if (guest.viewInstanceId !== params.instanceId) {
       webViewManager.removeGuest(guestInstance.embedder, guestInstanceId)
-      guestInstance.embedder.send(`ELECTRON_GUEST_VIEW_INTERNAL_DESTROY_GUEST-${guest.viewInstanceId}`)
+      guestInstance.embedder._sendInternal(`ELECTRON_GUEST_VIEW_INTERNAL_DESTROY_GUEST-${guest.viewInstanceId}`)
     }
   }
 
@@ -302,7 +303,7 @@ const watchEmbedder = function (embedder) {
       const guestInstance = guestInstances[guestInstanceId]
       guestInstance.visibilityState = visibilityState
       if (guestInstance.embedder === embedder) {
-        guestInstance.guest.send('ELECTRON_GUEST_INSTANCE_VISIBILITY_CHANGE', visibilityState)
+        guestInstance.guest._sendInternal('ELECTRON_GUEST_INSTANCE_VISIBILITY_CHANGE', visibilityState)
       }
     }
   }
@@ -325,7 +326,7 @@ const watchEmbedder = function (embedder) {
 }
 
 ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', function (event, params, requestId) {
-  event.sender.send(`ELECTRON_RESPONSE_${requestId}`, createGuest(event.sender, params))
+  event.sender._sendInternal(`ELECTRON_RESPONSE_${requestId}`, createGuest(event.sender, params))
 })
 
 ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST_SYNC', function (event, params) {

+ 4 - 3
lib/browser/guest-window-manager.js

@@ -1,7 +1,8 @@
 'use strict'
 
-const { BrowserWindow, ipcMain, webContents } = require('electron')
+const { BrowserWindow, webContents } = require('electron')
 const { isSameOrigin } = process.atomBinding('v8_util')
+const ipcMain = require('@electron/internal/browser/ipc-main-internal')
 const parseFeaturesString = require('@electron/internal/common/parse-features-string')
 
 const hasProp = {}.hasOwnProperty
@@ -87,7 +88,7 @@ const setupGuest = function (embedder, frameName, guest, options) {
     guest.destroy()
   }
   const closedByUser = function () {
-    embedder.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_' + guestId)
+    embedder._sendInternal('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_' + guestId)
     embedder.removeListener('render-view-deleted', closedByEmbedder)
   }
   embedder.once('render-view-deleted', closedByEmbedder)
@@ -320,7 +321,7 @@ ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', function (event,
   // postMessage across origins is useful and not harmful.
   if (targetOrigin === '*' || isSameOrigin(guestContents.getURL(), targetOrigin)) {
     const sourceId = event.sender.id
-    guestContents.send('ELECTRON_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin)
+    guestContents._sendInternal('ELECTRON_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin)
   }
 })
 

+ 10 - 0
lib/browser/ipc-main-internal.js

@@ -0,0 +1,10 @@
+'use strict'
+
+const { EventEmitter } = require('events')
+
+const emitter = new EventEmitter()
+
+// Do not throw exception when channel name is "error".
+emitter.on('error', () => {})
+
+module.exports = emitter

+ 14 - 5
lib/browser/rpc-server.js

@@ -8,8 +8,9 @@ const os = require('os')
 const path = require('path')
 const v8Util = process.atomBinding('v8_util')
 
-const { ipcMain, isPromise } = electron
+const { isPromise } = electron
 
+const ipcMain = require('@electron/internal/browser/ipc-main-internal')
 const objectsRegistry = require('@electron/internal/browser/objects-registry')
 const bufferUtils = require('@electron/internal/common/buffer-utils')
 const errorUtils = require('@electron/internal/common/error-utils')
@@ -215,7 +216,7 @@ const unwrapArgs = function (sender, contextId, args) {
         const processId = sender.getProcessId()
         const callIntoRenderer = function (...args) {
           if (!sender.isDestroyed() && processId === sender.getProcessId()) {
-            sender.send('ELECTRON_RENDERER_CALLBACK', contextId, meta.id, valueToMeta(sender, contextId, args))
+            sender._sendInternal('ELECTRON_RENDERER_CALLBACK', contextId, meta.id, valueToMeta(sender, contextId, args))
           } else {
             removeRemoteListenersAndLogWarning(this, meta, callIntoRenderer)
           }
@@ -390,7 +391,7 @@ ipcMain.on('ELECTRON_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', function (event, request
   }, error => {
     return [errorUtils.serialize(error)]
   }).then(responseArgs => {
-    event.sender.send(`ELECTRON_RENDERER_ASYNC_CALL_TO_GUEST_VIEW_RESPONSE_${requestId}`, ...responseArgs)
+    event.sender._sendInternal(`ELECTRON_RENDERER_ASYNC_CALL_TO_GUEST_VIEW_RESPONSE_${requestId}`, ...responseArgs)
   })
 })
 
@@ -424,7 +425,7 @@ const getTempDirectory = function () {
   }
 }
 
-ipcMain.on('ELECTRON_CRASH_REPORTER_INIT', function (event, options) {
+const crashReporterInit = function (options) {
   const productName = options.productName || electron.app.getName()
   const crashesDirectory = path.join(getTempDirectory(), `${productName} Crashes`)
 
@@ -445,11 +446,19 @@ ipcMain.on('ELECTRON_CRASH_REPORTER_INIT', function (event, options) {
     })
   }
 
-  event.returnValue = {
+  return {
     productName,
     crashesDirectory,
     appVersion: electron.app.getVersion()
   }
+}
+
+ipcMain.on('ELECTRON_CRASH_REPORTER_INIT', function (event, options) {
+  try {
+    event.returnValue = [null, crashReporterInit(options)]
+  } catch (error) {
+    event.returnValue = [errorUtils.serialize(error)]
+  }
 })
 
 ipcMain.on('ELECTRON_BROWSER_SANDBOX_LOAD', function (event) {

+ 0 - 1
lib/common/api/module-list.js

@@ -3,7 +3,6 @@
 // Common modules, please sort alphabetically
 module.exports = [
   { name: 'clipboard', file: 'clipboard' },
-  { name: 'crashReporter', file: 'crash-reporter' },
   { name: 'nativeImage', file: 'native-image' },
   { name: 'shell', file: 'shell' },
   // The internal modules, invisible unless you know their names.

+ 17 - 12
lib/common/api/crash-reporter.js → lib/common/crash-reporter.js

@@ -1,17 +1,8 @@
 'use strict'
 
-const electron = require('electron')
 const binding = process.atomBinding('crash_reporter')
 
-const sendSync = function (channel, ...args) {
-  if (process.type === 'browser') {
-    const event = {}
-    electron.ipcMain.emit(channel, event, ...args)
-    return event.returnValue
-  } else {
-    return electron.ipcRenderer.sendSync(channel, ...args)
-  }
-}
+const errorUtils = require('@electron/internal/common/error-utils')
 
 class CrashReporter {
   contructor () {
@@ -19,6 +10,20 @@ class CrashReporter {
     this.crashesDirectory = null
   }
 
+  sendSync (channel, ...args) {
+    throw new Error('Not implemented')
+  }
+
+  invoke (command, ...args) {
+    const [ error, result ] = this.sendSync(command, ...args)
+
+    if (error) {
+      throw errorUtils.deserialize(error)
+    }
+
+    return result
+  }
+
   start (options) {
     if (options == null) options = {}
 
@@ -46,7 +51,7 @@ class CrashReporter {
       throw new Error('submitURL is a required option to crashReporter.start')
     }
 
-    const ret = sendSync('ELECTRON_CRASH_REPORTER_INIT', {
+    const ret = this.invoke('ELECTRON_CRASH_REPORTER_INIT', {
       submitURL,
       productName
     })
@@ -114,4 +119,4 @@ class CrashReporter {
   }
 }
 
-module.exports = new CrashReporter()
+module.exports = CrashReporter

+ 2 - 2
lib/isolated_renderer/init.js

@@ -6,11 +6,11 @@ const { send, sendSync } = binding
 
 const ipcRenderer = {
   send (...args) {
-    return send('ipc-message', args)
+    return send('ipc-internal-message', args)
   },
 
   sendSync (...args) {
-    return sendSync('ipc-message-sync', args)[0]
+    return sendSync('ipc-internal-message-sync', args)[0]
   },
 
   // No-ops since events aren't received

+ 12 - 0
lib/renderer/api/crash-reporter.js

@@ -0,0 +1,12 @@
+'use strict'
+
+const CrashReporter = require('@electron/internal/common/crash-reporter')
+const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
+
+class CrashReporterRenderer extends CrashReporter {
+  sendSync (channel, ...args) {
+    return ipcRenderer.sendSync(channel, ...args)
+  }
+}
+
+module.exports = new CrashReporterRenderer()

+ 2 - 1
lib/renderer/api/desktop-capturer.js

@@ -1,6 +1,7 @@
 'use strict'
 
-const { ipcRenderer, nativeImage } = require('electron')
+const { nativeImage } = require('electron')
+const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
 
 const includes = [].includes
 let currentId = 0

+ 3 - 10
lib/renderer/api/ipc-renderer.js

@@ -5,6 +5,7 @@ const v8Util = process.atomBinding('v8_util')
 
 // Created by init.js.
 const ipcRenderer = v8Util.getHiddenValue(global, 'ipc')
+const internal = false
 
 ipcRenderer.send = function (...args) {
   return binding.send('ipc-message', args)
@@ -19,19 +20,11 @@ ipcRenderer.sendToHost = function (...args) {
 }
 
 ipcRenderer.sendTo = function (webContentsId, channel, ...args) {
-  return binding.sendTo(false, webContentsId, channel, args)
+  return binding.sendTo(internal, false, webContentsId, channel, args)
 }
 
 ipcRenderer.sendToAll = function (webContentsId, channel, ...args) {
-  return binding.sendTo(true, webContentsId, channel, args)
-}
-
-const removeAllListeners = ipcRenderer.removeAllListeners.bind(ipcRenderer)
-ipcRenderer.removeAllListeners = function (...args) {
-  if (args.length === 0) {
-    throw new Error('Removing all listeners from ipcRenderer will make Electron internals stop working.  Please specify a event name')
-  }
-  removeAllListeners(...args)
+  return binding.sendTo(internal, true, webContentsId, channel, args)
 }
 
 module.exports = ipcRenderer

+ 1 - 0
lib/renderer/api/module-list.js

@@ -5,6 +5,7 @@ const features = process.atomBinding('features')
 // Renderer side modules, please sort alphabetically.
 // A module is `enabled` if there is no explicit condition defined.
 module.exports = [
+  { name: 'crashReporter', file: 'crash-reporter', enabled: true },
   {
     name: 'desktopCapturer',
     file: 'desktop-capturer',

+ 2 - 1
lib/renderer/api/remote.js

@@ -1,12 +1,13 @@
 'use strict'
 
 const v8Util = process.atomBinding('v8_util')
-const { ipcRenderer, isPromise } = require('electron')
+const { isPromise } = require('electron')
 const resolvePromise = Promise.resolve.bind(Promise)
 
 const CallbacksRegistry = require('@electron/internal/renderer/callbacks-registry')
 const bufferUtils = require('@electron/internal/common/buffer-utils')
 const errorUtils = require('@electron/internal/common/error-utils')
+const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
 
 const callbacksRegistry = new CallbacksRegistry()
 const remoteObjectCache = v8Util.createIDWeakMap()

+ 3 - 3
lib/renderer/chrome-api.js

@@ -1,6 +1,6 @@
 'use strict'
 
-const { ipcRenderer } = require('electron')
+const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
 const Event = require('@electron/internal/renderer/extensions/event')
 const url = require('url')
 
@@ -43,12 +43,12 @@ class Port {
   disconnect () {
     if (this.disconnected) return
 
-    ipcRenderer.sendToAll(this.tabId, `CHROME_PORT_DISCONNECT_${this.portId}`)
+    ipcRenderer._sendInternalToAll(this.tabId, `CHROME_PORT_DISCONNECT_${this.portId}`)
     this._onDisconnect()
   }
 
   postMessage (message) {
-    ipcRenderer.sendToAll(this.tabId, `CHROME_PORT_POSTMESSAGE_${this.portId}`, message)
+    ipcRenderer._sendInternalToAll(this.tabId, `CHROME_PORT_POSTMESSAGE_${this.portId}`, message)
   }
 
   _onDisconnect () {

+ 1 - 1
lib/renderer/content-scripts-injector.js

@@ -1,6 +1,6 @@
 'use strict'
 
-const { ipcRenderer } = require('electron')
+const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
 const { runInThisContext } = require('vm')
 
 // Check whether pattern matches.

+ 1 - 1
lib/renderer/extensions/i18n.js

@@ -6,7 +6,7 @@
 // Does not implement predefined messages:
 // https://developer.chrome.com/extensions/i18n#overview-predefined
 
-const { ipcRenderer } = require('electron')
+const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
 const fs = require('fs')
 const path = require('path')
 

+ 1 - 1
lib/renderer/extensions/web-navigation.js

@@ -1,7 +1,7 @@
 'use strict'
 
 const Event = require('@electron/internal/renderer/extensions/event')
-const { ipcRenderer } = require('electron')
+const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
 
 class WebNavigation {
   constructor () {

+ 4 - 3
lib/renderer/init.js

@@ -1,6 +1,6 @@
 'use strict'
 
-const events = require('events')
+const { EventEmitter } = require('events')
 const path = require('path')
 const Module = require('module')
 
@@ -22,10 +22,11 @@ globalPaths.push(path.join(__dirname, 'api', 'exports'))
 // The global variable will be used by ipc for event dispatching
 const v8Util = process.atomBinding('v8_util')
 
-v8Util.setHiddenValue(global, 'ipc', new events.EventEmitter())
+v8Util.setHiddenValue(global, 'ipc', new EventEmitter())
+v8Util.setHiddenValue(global, 'ipc-internal', new EventEmitter())
 
 // Use electron module after everything is ready.
-const { ipcRenderer } = require('electron')
+const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
 
 require('@electron/internal/renderer/web-frame-init')()
 

+ 26 - 0
lib/renderer/ipc-renderer-internal.js

@@ -0,0 +1,26 @@
+'use strict'
+
+const binding = process.atomBinding('ipc')
+const v8Util = process.atomBinding('v8_util')
+
+// Created by init.js.
+const ipcRenderer = v8Util.getHiddenValue(global, 'ipc-internal')
+const internal = true
+
+ipcRenderer.send = function (...args) {
+  return binding.send('ipc-internal-message', args)
+}
+
+ipcRenderer.sendSync = function (...args) {
+  return binding.sendSync('ipc-internal-message-sync', args)[0]
+}
+
+ipcRenderer.sendTo = function (webContentsId, channel, ...args) {
+  return binding.sendTo(internal, false, webContentsId, channel, args)
+}
+
+ipcRenderer.sendToAll = function (webContentsId, channel, ...args) {
+  return binding.sendTo(internal, true, webContentsId, channel, args)
+}
+
+module.exports = ipcRenderer

+ 1 - 1
lib/renderer/override.js

@@ -1,6 +1,6 @@
 'use strict'
 
-const { ipcRenderer } = require('electron')
+const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
 
 const { guestInstanceId, openerId } = process
 const hiddenPage = process.argv.includes('--hidden-page')

+ 1 - 1
lib/renderer/security-warnings.js

@@ -264,7 +264,7 @@ const logSecurityWarnings = function (webPreferences, nodeIntegration) {
 }
 
 const getWebPreferences = function () {
-  const { ipcRenderer } = require('electron')
+  const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
   const errorUtils = require('@electron/internal/common/error-utils')
 
   const [ error, result ] = ipcRenderer.sendSync('ELECTRON_BROWSER_GET_LAST_WEB_PREFERENCES')

+ 2 - 1
lib/renderer/web-frame-init.js

@@ -1,6 +1,7 @@
 'use strict'
 
-const { ipcRenderer, webFrame } = require('electron')
+const { webFrame } = require('electron')
+const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
 const errorUtils = require('@electron/internal/common/error-utils')
 
 module.exports = () => {

+ 2 - 1
lib/renderer/web-view/guest-view-internal.js

@@ -1,6 +1,7 @@
 'use strict'
 
-const { ipcRenderer, webFrame } = require('electron')
+const { webFrame } = require('electron')
+const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
 
 let requestId = 0
 

+ 1 - 1
lib/renderer/web-view/web-view-attributes.js

@@ -1,6 +1,6 @@
 'use strict'
 
-const { ipcRenderer } = require('electron')
+const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
 const WebViewImpl = require('@electron/internal/renderer/web-view/web-view')
 const webViewConstants = require('@electron/internal/renderer/web-view/web-view-constants')
 const errorUtils = require('@electron/internal/common/error-utils')

+ 2 - 1
lib/renderer/web-view/web-view.js

@@ -1,8 +1,9 @@
 'use strict'
 
-const { ipcRenderer, remote, webFrame } = require('electron')
+const { remote, webFrame } = require('electron')
 
 const v8Util = process.atomBinding('v8_util')
+const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
 const guestViewInternal = require('@electron/internal/renderer/web-view/guest-view-internal')
 const webViewConstants = require('@electron/internal/renderer/web-view/web-view-constants')
 const errorUtils = require('@electron/internal/common/error-utils')

+ 2 - 2
lib/sandboxed_renderer/api/module-list.js

@@ -5,7 +5,7 @@ const features = process.atomBinding('features')
 module.exports = [
   {
     name: 'crashReporter',
-    load: () => require('@electron/internal/common/api/crash-reporter')
+    load: () => require('@electron/internal/renderer/api/crash-reporter')
   },
   {
     name: 'desktopCapturer',
@@ -14,7 +14,7 @@ module.exports = [
   },
   {
     name: 'ipcRenderer',
-    load: () => require('@electron/internal/sandboxed_renderer/api/ipc-renderer')
+    load: () => require('@electron/internal/renderer/api/ipc-renderer')
   },
   {
     name: 'isPromise',

+ 23 - 1
lib/sandboxed_renderer/init.js

@@ -17,6 +17,9 @@ v8Util.setHiddenValue(global, 'Buffer', Buffer)
 // The `lib/renderer/api/ipc-renderer.js` module looks for the ipc object in the
 // "ipc" hidden value
 v8Util.setHiddenValue(global, 'ipc', new EventEmitter())
+// The `lib/renderer/ipc-renderer-internal.js` module looks for the ipc object in the
+// "ipc-internal" hidden value
+v8Util.setHiddenValue(global, 'ipc-internal', new EventEmitter())
 // The process object created by browserify is not an event emitter, fix it so
 // the API is more compatible with non-sandboxed renderers.
 for (const prop of Object.keys(EventEmitter.prototype)) {
@@ -40,9 +43,28 @@ const loadedModules = new Map([
   ['url', require('url')]
 ])
 
+// AtomSandboxedRendererClient will look for the "ipcNative" hidden object when
+// invoking the `onMessage`/`onExit` callbacks.
+const ipcNative = process.atomBinding('ipc')
+v8Util.setHiddenValue(global, 'ipcNative', ipcNative)
+
+const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
+
+ipcNative.onInternalMessage = function (channel, args, senderId) {
+  ipcRenderer.emit(channel, { sender: ipcRenderer, senderId }, ...args)
+}
+
+ipcNative.onMessage = function (channel, args, senderId) {
+  electron.ipcRenderer.emit(channel, { sender: electron.ipcRenderer, senderId }, ...args)
+}
+
+ipcNative.onExit = function () {
+  process.emit('exit')
+}
+
 const {
   preloadSrc, preloadError, process: processProps
-} = electron.ipcRenderer.sendSync('ELECTRON_BROWSER_SANDBOX_LOAD')
+} = ipcRenderer.sendSync('ELECTRON_BROWSER_SANDBOX_LOAD')
 
 require('@electron/internal/renderer/web-frame-init')()
 

+ 18 - 12
spec/api-ipc-main-spec.js

@@ -3,7 +3,9 @@
 const chai = require('chai')
 const dirtyChai = require('dirty-chai')
 const path = require('path')
+const cp = require('child_process')
 const { closeWindow } = require('./window-helpers')
+const { emittedOnce } = require('./events-helpers')
 
 const { expect } = chai
 chai.use(dirtyChai)
@@ -55,18 +57,6 @@ describe('ipc main module', () => {
     })
   })
 
-  it('throws an error when removing all the listeners', () => {
-    ipcMain.on('test-event', () => {})
-    expect(ipcMain.listenerCount('test-event')).to.equal(1)
-
-    expect(() => {
-      ipcMain.removeAllListeners()
-    }).to.throw(/Removing all listeners from ipcMain will make Electron internals stop working/)
-
-    ipcMain.removeAllListeners('test-event')
-    expect(ipcMain.listenerCount('test-event')).to.equal(0)
-  })
-
   describe('remote objects registry', () => {
     it('does not dereference until the render view is deleted (regression)', (done) => {
       w = new BrowserWindow({ show: false })
@@ -80,4 +70,20 @@ describe('ipc main module', () => {
       w.loadFile(path.join(fixtures, 'api', 'render-view-deleted.html'))
     })
   })
+
+  describe('ipcMain.on', () => {
+    it('is not used for internals', async () => {
+      const appPath = path.join(__dirname, 'fixtures', 'api', 'ipc-main-listeners')
+      const electronPath = remote.getGlobal('process').execPath
+      const appProcess = cp.spawn(electronPath, [appPath])
+
+      let output = ''
+      appProcess.stdout.on('data', (data) => { output += data })
+
+      await emittedOnce(appProcess.stdout, 'end')
+
+      output = JSON.parse(output)
+      expect(output).to.deep.equal(['error'])
+    })
+  })
 })

+ 10 - 8
spec/api-ipc-renderer-spec.js

@@ -5,6 +5,7 @@ const dirtyChai = require('dirty-chai')
 const http = require('http')
 const path = require('path')
 const { closeWindow } = require('./window-helpers')
+const { emittedOnce } = require('./events-helpers')
 
 const { expect } = chai
 chai.use(dirtyChai)
@@ -225,15 +226,16 @@ describe('ipc renderer module', () => {
     })
   })
 
-  it('throws an error when removing all the listeners', () => {
-    ipcRenderer.on('test-event', () => {})
-    expect(ipcRenderer.listenerCount('test-event')).to.equal(1)
+  describe('ipcRenderer.on', () => {
+    it('is not used for internals', async () => {
+      w = new BrowserWindow({ show: false })
+      w.loadURL('about:blank')
 
-    expect(() => {
-      ipcRenderer.removeAllListeners()
-    }).to.throw(/Removing all listeners from ipcRenderer will make Electron internals stop working/)
+      await emittedOnce(w.webContents, 'did-finish-load')
 
-    ipcRenderer.removeAllListeners('test-event')
-    expect(ipcRenderer.listenerCount('test-event')).to.equal(0)
+      const script = `require('electron').ipcRenderer.eventNames()`
+      const result = await w.webContents.executeJavaScript(script)
+      expect(result).to.deep.equal([])
+    })
   })
 })

+ 10 - 0
spec/fixtures/api/ipc-main-listeners/main.js

@@ -0,0 +1,10 @@
+const { app, ipcMain } = require('electron')
+
+app.on('ready', () => {
+  process.stdout.write(JSON.stringify(ipcMain.eventNames()))
+  process.stdout.end()
+
+  setImmediate(() => {
+    app.quit()
+  })
+})

+ 4 - 0
spec/fixtures/api/ipc-main-listeners/package.json

@@ -0,0 +1,4 @@
+{
+  "name": "ipc-main-listeners",
+  "main": "main.js"
+}