From c17a1b37ead51d70908bb19ad7424fdcd699821d Mon Sep 17 00:00:00 2001 From: Milan Burda Date: Sat, 1 Sep 2018 03:13:51 +0200 Subject: [PATCH] feat: add event.senderId property to IPCs sent via ipcRenderer.sendTo (#14395) --- atom/browser/api/atom_api_web_contents.cc | 11 ++++- atom/browser/api/atom_api_web_contents.h | 5 ++ atom/common/api/api_messages.h | 5 +- atom/common/api/remote_callback_freer.cc | 3 +- atom/renderer/atom_render_frame_observer.cc | 11 +++-- atom/renderer/atom_render_frame_observer.h | 6 ++- .../atom_sandboxed_renderer_client.cc | 6 ++- docs/api/ipc-renderer.md | 14 ++++++ lib/sandboxed_renderer/api/ipc-renderer.js | 4 +- spec/api-ipc-renderer-spec.js | 46 +++++++++++++++---- spec/fixtures/pages/ping-pong.html | 10 ++-- 11 files changed, 90 insertions(+), 31 deletions(-) diff --git a/atom/browser/api/atom_api_web_contents.cc b/atom/browser/api/atom_api_web_contents.cc index 4e50833f3b..ebf5723533 100644 --- a/atom/browser/api/atom_api_web_contents.cc +++ b/atom/browser/api/atom_api_web_contents.cc @@ -1556,10 +1556,17 @@ void WebContents::TabTraverse(bool reverse) { bool WebContents::SendIPCMessage(bool all_frames, const std::string& channel, const base::ListValue& args) { + return SendIPCMessageWithSender(all_frames, channel, args); +} + +bool WebContents::SendIPCMessageWithSender(bool all_frames, + 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)); + frame_host->GetRoutingID(), all_frames, channel, args, sender_id)); } return false; } @@ -2090,7 +2097,7 @@ void WebContents::OnRendererMessageTo(content::RenderFrameHost* frame_host, isolate(), web_contents_id); if (web_contents) { - web_contents->SendIPCMessage(send_to_all, channel, args); + web_contents->SendIPCMessageWithSender(send_to_all, channel, args, ID()); } } diff --git a/atom/browser/api/atom_api_web_contents.h b/atom/browser/api/atom_api_web_contents.h index fe19a5b249..ec5a4a756e 100644 --- a/atom/browser/api/atom_api_web_contents.h +++ b/atom/browser/api/atom_api_web_contents.h @@ -181,6 +181,11 @@ class WebContents : public mate::TrackableObject, const std::string& channel, const base::ListValue& args); + bool SendIPCMessageWithSender(bool all_frames, + const std::string& channel, + const base::ListValue& args, + int32_t sender_id = 0); + // Send WebInputEvent to the page. void SendInputEvent(v8::Isolate* isolate, v8::Local input_event); diff --git a/atom/common/api/api_messages.h b/atom/common/api/api_messages.h index 2163e744e4..e7317736e0 100644 --- a/atom/common/api/api_messages.h +++ b/atom/common/api/api_messages.h @@ -39,10 +39,11 @@ IPC_MESSAGE_ROUTED4(AtomFrameHostMsg_Message_To, std::string /* channel */, base::ListValue /* arguments */) -IPC_MESSAGE_ROUTED3(AtomFrameMsg_Message, +IPC_MESSAGE_ROUTED4(AtomFrameMsg_Message, bool /* send_to_all */, std::string /* channel */, - base::ListValue /* arguments */) + base::ListValue /* arguments */, + int32_t /* sender_id */) IPC_MESSAGE_ROUTED0(AtomViewMsg_Offscreen) diff --git a/atom/common/api/remote_callback_freer.cc b/atom/common/api/remote_callback_freer.cc index b136d7ce82..43f77a797d 100644 --- a/atom/common/api/remote_callback_freer.cc +++ b/atom/common/api/remote_callback_freer.cc @@ -36,12 +36,13 @@ RemoteCallbackFreer::~RemoteCallbackFreer() {} void RemoteCallbackFreer::RunDestructor() { auto* channel = "ELECTRON_RENDERER_RELEASE_CALLBACK"; base::ListValue args; + int32_t sender_id = 0; args.AppendString(context_id_); 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)); + channel, args, sender_id)); } Observe(nullptr); diff --git a/atom/renderer/atom_render_frame_observer.cc b/atom/renderer/atom_render_frame_observer.cc index 7849ba05c6..a7eea2e7c0 100644 --- a/atom/renderer/atom_render_frame_observer.cc +++ b/atom/renderer/atom_render_frame_observer.cc @@ -169,7 +169,8 @@ bool AtomRenderFrameObserver::OnMessageReceived(const IPC::Message& message) { void AtomRenderFrameObserver::OnBrowserMessage(bool send_to_all, const std::string& channel, - const base::ListValue& args) { + const base::ListValue& args, + int32_t sender_id) { // Don't handle browser messages before document element is created. // When we receive a message from the browser, we try to transfer it // to a web page, and when we do that Blink creates an empty @@ -182,21 +183,22 @@ void AtomRenderFrameObserver::OnBrowserMessage(bool send_to_all, if (!frame || !render_frame_->IsMainFrame()) return; - EmitIPCEvent(frame, channel, args); + EmitIPCEvent(frame, 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); + EmitIPCEvent(child->ToWebLocalFrame(), channel, args, sender_id); } } } void AtomRenderFrameObserver::EmitIPCEvent(blink::WebLocalFrame* frame, const std::string& channel, - const base::ListValue& args) { + const base::ListValue& args, + int32_t sender_id) { if (!frame) return; @@ -218,6 +220,7 @@ void AtomRenderFrameObserver::EmitIPCEvent(blink::WebLocalFrame* frame, // Insert the Event object, event.sender is ipc. mate::Dictionary event = mate::Dictionary::CreateEmpty(isolate); event.Set("sender", ipc); + event.Set("senderId", sender_id); args_vector.insert(args_vector.begin(), event.GetHandle()); mate::EmitEvent(isolate, ipc, channel, args_vector); } diff --git a/atom/renderer/atom_render_frame_observer.h b/atom/renderer/atom_render_frame_observer.h index f944fb5c24..8c0ed6b4c0 100644 --- a/atom/renderer/atom_render_frame_observer.h +++ b/atom/renderer/atom_render_frame_observer.h @@ -45,7 +45,8 @@ class AtomRenderFrameObserver : public content::RenderFrameObserver { protected: virtual void EmitIPCEvent(blink::WebLocalFrame* frame, const std::string& channel, - const base::ListValue& args); + const base::ListValue& args, + int32_t sender_id); private: bool ShouldNotifyClient(int world_id); @@ -54,7 +55,8 @@ class AtomRenderFrameObserver : public content::RenderFrameObserver { bool IsIsolatedWorld(int world_id); void OnBrowserMessage(bool send_to_all, const std::string& channel, - const base::ListValue& args); + const base::ListValue& args, + int32_t sender_id); content::RenderFrame* render_frame_; RendererClientBase* renderer_client_; diff --git a/atom/renderer/atom_sandboxed_renderer_client.cc b/atom/renderer/atom_sandboxed_renderer_client.cc index eefc3fa9c6..f820b88cf2 100644 --- a/atom/renderer/atom_sandboxed_renderer_client.cc +++ b/atom/renderer/atom_sandboxed_renderer_client.cc @@ -107,7 +107,8 @@ class AtomSandboxedRenderFrameObserver : public AtomRenderFrameObserver { protected: void EmitIPCEvent(blink::WebLocalFrame* frame, const std::string& channel, - const base::ListValue& args) override { + const base::ListValue& args, + int32_t sender_id) override { if (!frame) return; @@ -116,7 +117,8 @@ class AtomSandboxedRenderFrameObserver : public AtomRenderFrameObserver { auto context = frame->MainWorldScriptContext(); v8::Context::Scope context_scope(context); v8::Local argv[] = {mate::ConvertToV8(isolate, channel), - mate::ConvertToV8(isolate, args)}; + mate::ConvertToV8(isolate, args), + mate::ConvertToV8(isolate, sender_id)}; renderer_client_->InvokeIpcCallback( context, "onMessage", std::vector>(argv, argv + node::arraysize(argv))); diff --git a/docs/api/ipc-renderer.md b/docs/api/ipc-renderer.md index fb76c2d973..252d73eaae 100644 --- a/docs/api/ipc-renderer.md +++ b/docs/api/ipc-renderer.md @@ -89,3 +89,17 @@ Sends a message to a window with `windowid` via `channel`. Like `ipcRenderer.send` but the event will be sent to the `` element in the host page instead of the main process. + +## Event object + +The `event` object passed to the `callback` has the following methods: + +### `event.senderId` + +Returns the `webContents.id` that sent the message, you can call +`event.sender.sendTo(event.senderId, ...)` to reply to the message, see +[ipcRenderer.sendTo][ipc-renderer-sendto] for more information. +This only applies to messages sent from a different renderer. +Messages sent directly from the main process set `event.senderId` to `0`. + +[ipc-renderer-sendto]: #ipcrenderersendtowindowid-channel--arg1-arg2- diff --git a/lib/sandboxed_renderer/api/ipc-renderer.js b/lib/sandboxed_renderer/api/ipc-renderer.js index 008372b4aa..45cbdb7786 100644 --- a/lib/sandboxed_renderer/api/ipc-renderer.js +++ b/lib/sandboxed_renderer/api/ipc-renderer.js @@ -9,8 +9,8 @@ const ipcNative = process.atomBinding('ipc') // private/public APIs. v8Util.setHiddenValue(global, 'ipcNative', ipcNative) -ipcNative.onMessage = function (channel, args) { - ipcRenderer.emit(channel, {sender: ipcRenderer}, ...args) +ipcNative.onMessage = function (channel, args, senderId) { + ipcRenderer.emit(channel, {sender: ipcRenderer, senderId}, ...args) } ipcNative.onExit = function () { diff --git a/spec/api-ipc-renderer-spec.js b/spec/api-ipc-renderer-spec.js index b55e1404a9..3ef57c06aa 100644 --- a/spec/api-ipc-renderer-spec.js +++ b/spec/api-ipc-renderer-spec.js @@ -132,8 +132,6 @@ describe('ipc renderer module', () => { describe('ipcRenderer.sendTo', () => { let contents = null - beforeEach(() => { contents = webContents.create({}) }) - afterEach(() => { ipcRenderer.removeAllListeners('pong') contents.destroy() @@ -141,30 +139,58 @@ describe('ipc renderer module', () => { }) it('sends message to WebContents', done => { - const webContentsId = remote.getCurrentWebContents().id + contents = webContents.create({ + preload: path.join(fixtures, 'module', 'preload-inject-ipc.js') + }) - ipcRenderer.once('pong', (event, id) => { - expect(webContentsId).to.equal(id) + const payload = 'Hello World!' + + ipcRenderer.once('pong', (event, data) => { + expect(payload).to.equal(data) done() }) contents.once('did-finish-load', () => { - ipcRenderer.sendTo(contents.id, 'ping', webContentsId) + ipcRenderer.sendTo(contents.id, 'ping', payload) + }) + + contents.loadURL(`file://${path.join(fixtures, 'pages', 'ping-pong.html')}`) + }) + + it('sends message to WebContents (sanboxed renderer)', done => { + contents = webContents.create({ + preload: path.join(fixtures, 'module', 'preload-inject-ipc.js'), + sandbox: true + }) + + const payload = 'Hello World!' + + ipcRenderer.once('pong', (event, data) => { + expect(payload).to.equal(data) + done() + }) + + contents.once('did-finish-load', () => { + ipcRenderer.sendTo(contents.id, 'ping', payload) }) contents.loadURL(`file://${path.join(fixtures, 'pages', 'ping-pong.html')}`) }) it('sends message to WebContents (channel has special chars)', done => { - const webContentsId = remote.getCurrentWebContents().id + contents = webContents.create({ + preload: path.join(fixtures, 'module', 'preload-inject-ipc.js') + }) - ipcRenderer.once('pong-æøåü', (event, id) => { - expect(webContentsId).to.equal(id) + const payload = 'Hello World!' + + ipcRenderer.once('pong-æøåü', (event, data) => { + expect(payload).to.equal(data) done() }) contents.once('did-finish-load', () => { - ipcRenderer.sendTo(contents.id, 'ping-æøåü', webContentsId) + ipcRenderer.sendTo(contents.id, 'ping-æøåü', payload) }) contents.loadURL(`file://${path.join(fixtures, 'pages', 'ping-pong.html')}`) diff --git a/spec/fixtures/pages/ping-pong.html b/spec/fixtures/pages/ping-pong.html index be62d4b8d5..743131e0d1 100644 --- a/spec/fixtures/pages/ping-pong.html +++ b/spec/fixtures/pages/ping-pong.html @@ -1,14 +1,12 @@ -