зеркало из https://github.com/electron/electron.git
fix: restrict sendToFrame to same-process frames by default (#26875)
This commit is contained in:
Родитель
76f721474e
Коммит
07a1c2a3e5
|
@ -1,5 +1,6 @@
|
||||||
# IpcMainEvent Object extends `Event`
|
# IpcMainEvent Object extends `Event`
|
||||||
|
|
||||||
|
* `processId` Integer - The internal ID of the renderer process that sent this message
|
||||||
* `frameId` Integer - The ID of the renderer frame that sent this message
|
* `frameId` Integer - The ID of the renderer frame that sent this message
|
||||||
* `returnValue` any - Set this to the value to be returned in a synchronous message
|
* `returnValue` any - Set this to the value to be returned in a synchronous message
|
||||||
* `sender` WebContents - Returns the `webContents` that sent the message
|
* `sender` WebContents - Returns the `webContents` that sent the message
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# IpcMainInvokeEvent Object extends `Event`
|
# IpcMainInvokeEvent Object extends `Event`
|
||||||
|
|
||||||
|
* `processId` Integer - The internal ID of the renderer process that sent this message
|
||||||
* `frameId` Integer - The ID of the renderer frame that sent this message
|
* `frameId` Integer - The ID of the renderer frame that sent this message
|
||||||
* `sender` WebContents - Returns the `webContents` that sent the message
|
* `sender` WebContents - Returns the `webContents` that sent the message
|
||||||
|
|
|
@ -1696,7 +1696,7 @@ app.whenReady().then(() => {
|
||||||
|
|
||||||
#### `contents.sendToFrame(frameId, channel, ...args)`
|
#### `contents.sendToFrame(frameId, channel, ...args)`
|
||||||
|
|
||||||
* `frameId` Integer
|
* `frameId` Integer | [number, number]
|
||||||
* `channel` String
|
* `channel` String
|
||||||
* `...args` any[]
|
* `...args` any[]
|
||||||
|
|
||||||
|
|
|
@ -148,23 +148,23 @@ WebContents.prototype._sendInternal = function (channel, ...args) {
|
||||||
|
|
||||||
return this._send(true /* internal */, channel, args);
|
return this._send(true /* internal */, channel, args);
|
||||||
};
|
};
|
||||||
WebContents.prototype.sendToFrame = function (frameId, channel, ...args) {
|
WebContents.prototype.sendToFrame = function (frame, channel, ...args) {
|
||||||
if (typeof channel !== 'string') {
|
if (typeof channel !== 'string') {
|
||||||
throw new Error('Missing required channel argument');
|
throw new Error('Missing required channel argument');
|
||||||
} else if (typeof frameId !== 'number') {
|
} else if (!(typeof frame === 'number' || Array.isArray(frame))) {
|
||||||
throw new Error('Missing required frameId argument');
|
throw new Error('Missing required frame argument (must be number or array)');
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._sendToFrame(false /* internal */, frameId, channel, args);
|
return this._sendToFrame(false /* internal */, frame, channel, args);
|
||||||
};
|
};
|
||||||
WebContents.prototype._sendToFrameInternal = function (frameId, channel, ...args) {
|
WebContents.prototype._sendToFrameInternal = function (frame, channel, ...args) {
|
||||||
if (typeof channel !== 'string') {
|
if (typeof channel !== 'string') {
|
||||||
throw new Error('Missing required channel argument');
|
throw new Error('Missing required channel argument');
|
||||||
} else if (typeof frameId !== 'number') {
|
} else if (!(typeof frame === 'number' || Array.isArray(frame))) {
|
||||||
throw new Error('Missing required frameId argument');
|
throw new Error('Missing required frame argument (must be number or array)');
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._sendToFrame(true /* internal */, frameId, channel, args);
|
return this._sendToFrame(true /* internal */, frame, channel, args);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Following methods are mapped to webFrame.
|
// Following methods are mapped to webFrame.
|
||||||
|
@ -456,8 +456,9 @@ WebContents.prototype._callWindowOpenHandler = function (event: any, url: string
|
||||||
};
|
};
|
||||||
|
|
||||||
const addReplyToEvent = (event: any) => {
|
const addReplyToEvent = (event: any) => {
|
||||||
|
const { processId, frameId } = event;
|
||||||
event.reply = (...args: any[]) => {
|
event.reply = (...args: any[]) => {
|
||||||
event.sender.sendToFrame(event.frameId, ...args);
|
event.sender.sendToFrame([processId, frameId], ...args);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ const FUNCTION_PROPERTIES = [
|
||||||
];
|
];
|
||||||
|
|
||||||
type RendererFunctionId = [string, number] // [contextId, funcId]
|
type RendererFunctionId = [string, number] // [contextId, funcId]
|
||||||
type FinalizerInfo = { id: RendererFunctionId, webContents: electron.WebContents, frameId: number };
|
type FinalizerInfo = { id: RendererFunctionId, webContents: electron.WebContents, frameId: [number, number] };
|
||||||
type CallIntoRenderer = (...args: any[]) => void
|
type CallIntoRenderer = (...args: any[]) => void
|
||||||
|
|
||||||
// The remote functions in renderer processes.
|
// The remote functions in renderer processes.
|
||||||
|
@ -43,7 +43,7 @@ function getCachedRendererFunction (id: RendererFunctionId): CallIntoRenderer |
|
||||||
if (deref !== undefined) return deref;
|
if (deref !== undefined) return deref;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function setCachedRendererFunction (id: RendererFunctionId, wc: electron.WebContents, frameId: number, value: CallIntoRenderer) {
|
function setCachedRendererFunction (id: RendererFunctionId, wc: electron.WebContents, frameId: [number, number], value: CallIntoRenderer) {
|
||||||
// eslint-disable-next-line no-undef
|
// eslint-disable-next-line no-undef
|
||||||
const wr = new WeakRef<CallIntoRenderer>(value);
|
const wr = new WeakRef<CallIntoRenderer>(value);
|
||||||
const mapKey = id[0] + '~' + id[1];
|
const mapKey = id[0] + '~' + id[1];
|
||||||
|
@ -220,7 +220,7 @@ const fakeConstructor = (constructor: Function, name: string) =>
|
||||||
});
|
});
|
||||||
|
|
||||||
// Convert array of meta data from renderer into array of real values.
|
// Convert array of meta data from renderer into array of real values.
|
||||||
const unwrapArgs = function (sender: electron.WebContents, frameId: number, contextId: string, args: any[]) {
|
const unwrapArgs = function (sender: electron.WebContents, frameId: [number, number], contextId: string, args: any[]) {
|
||||||
const metaToValue = function (meta: MetaTypeFromRenderer): any {
|
const metaToValue = function (meta: MetaTypeFromRenderer): any {
|
||||||
switch (meta.type) {
|
switch (meta.type) {
|
||||||
case 'nativeimage':
|
case 'nativeimage':
|
||||||
|
@ -423,7 +423,7 @@ handleRemoteCommand(IPC_MESSAGES.BROWSER_GET_CURRENT_WEB_CONTENTS, function (eve
|
||||||
});
|
});
|
||||||
|
|
||||||
handleRemoteCommand(IPC_MESSAGES.BROWSER_CONSTRUCTOR, function (event, contextId, id, args) {
|
handleRemoteCommand(IPC_MESSAGES.BROWSER_CONSTRUCTOR, function (event, contextId, id, args) {
|
||||||
args = unwrapArgs(event.sender, event.frameId, contextId, args);
|
args = unwrapArgs(event.sender, [event.processId, event.frameId], contextId, args);
|
||||||
const constructor = objectsRegistry.get(id);
|
const constructor = objectsRegistry.get(id);
|
||||||
|
|
||||||
if (constructor == null) {
|
if (constructor == null) {
|
||||||
|
@ -434,7 +434,7 @@ handleRemoteCommand(IPC_MESSAGES.BROWSER_CONSTRUCTOR, function (event, contextId
|
||||||
});
|
});
|
||||||
|
|
||||||
handleRemoteCommand(IPC_MESSAGES.BROWSER_FUNCTION_CALL, function (event, contextId, id, args) {
|
handleRemoteCommand(IPC_MESSAGES.BROWSER_FUNCTION_CALL, function (event, contextId, id, args) {
|
||||||
args = unwrapArgs(event.sender, event.frameId, contextId, args);
|
args = unwrapArgs(event.sender, [event.processId, event.frameId], contextId, args);
|
||||||
const func = objectsRegistry.get(id);
|
const func = objectsRegistry.get(id);
|
||||||
|
|
||||||
if (func == null) {
|
if (func == null) {
|
||||||
|
@ -451,7 +451,7 @@ handleRemoteCommand(IPC_MESSAGES.BROWSER_FUNCTION_CALL, function (event, context
|
||||||
});
|
});
|
||||||
|
|
||||||
handleRemoteCommand(IPC_MESSAGES.BROWSER_MEMBER_CONSTRUCTOR, function (event, contextId, id, method, args) {
|
handleRemoteCommand(IPC_MESSAGES.BROWSER_MEMBER_CONSTRUCTOR, function (event, contextId, id, method, args) {
|
||||||
args = unwrapArgs(event.sender, event.frameId, contextId, args);
|
args = unwrapArgs(event.sender, [event.processId, event.frameId], contextId, args);
|
||||||
const object = objectsRegistry.get(id);
|
const object = objectsRegistry.get(id);
|
||||||
|
|
||||||
if (object == null) {
|
if (object == null) {
|
||||||
|
@ -462,7 +462,7 @@ handleRemoteCommand(IPC_MESSAGES.BROWSER_MEMBER_CONSTRUCTOR, function (event, co
|
||||||
});
|
});
|
||||||
|
|
||||||
handleRemoteCommand(IPC_MESSAGES.BROWSER_MEMBER_CALL, function (event, contextId, id, method, args) {
|
handleRemoteCommand(IPC_MESSAGES.BROWSER_MEMBER_CALL, function (event, contextId, id, method, args) {
|
||||||
args = unwrapArgs(event.sender, event.frameId, contextId, args);
|
args = unwrapArgs(event.sender, [event.processId, event.frameId], contextId, args);
|
||||||
const object = objectsRegistry.get(id);
|
const object = objectsRegistry.get(id);
|
||||||
|
|
||||||
if (object == null) {
|
if (object == null) {
|
||||||
|
@ -479,7 +479,7 @@ handleRemoteCommand(IPC_MESSAGES.BROWSER_MEMBER_CALL, function (event, contextId
|
||||||
});
|
});
|
||||||
|
|
||||||
handleRemoteCommand(IPC_MESSAGES.BROWSER_MEMBER_SET, function (event, contextId, id, name, args) {
|
handleRemoteCommand(IPC_MESSAGES.BROWSER_MEMBER_SET, function (event, contextId, id, name, args) {
|
||||||
args = unwrapArgs(event.sender, event.frameId, contextId, args);
|
args = unwrapArgs(event.sender, [event.processId, event.frameId], contextId, args);
|
||||||
const obj = objectsRegistry.get(id);
|
const obj = objectsRegistry.get(id);
|
||||||
|
|
||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
|
|
|
@ -2708,7 +2708,7 @@ bool WebContents::SendIPCMessageWithSender(bool internal,
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WebContents::SendIPCMessageToFrame(bool internal,
|
bool WebContents::SendIPCMessageToFrame(bool internal,
|
||||||
int32_t frame_id,
|
v8::Local<v8::Value> frame,
|
||||||
const std::string& channel,
|
const std::string& channel,
|
||||||
v8::Local<v8::Value> args) {
|
v8::Local<v8::Value> args) {
|
||||||
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
|
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
|
||||||
|
@ -2718,17 +2718,30 @@ bool WebContents::SendIPCMessageToFrame(bool internal,
|
||||||
gin::StringToV8(isolate, "Failed to serialize arguments")));
|
gin::StringToV8(isolate, "Failed to serialize arguments")));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
auto frames = web_contents()->GetAllFrames();
|
int32_t frame_id;
|
||||||
auto iter = std::find_if(frames.begin(), frames.end(), [frame_id](auto* f) {
|
int32_t process_id;
|
||||||
return f->GetRoutingID() == frame_id;
|
if (gin::ConvertFromV8(isolate, frame, &frame_id)) {
|
||||||
});
|
process_id = web_contents()->GetMainFrame()->GetProcess()->GetID();
|
||||||
if (iter == frames.end())
|
} else {
|
||||||
|
std::vector<int32_t> id_pair;
|
||||||
|
if (gin::ConvertFromV8(isolate, frame, &id_pair) && id_pair.size() == 2) {
|
||||||
|
process_id = id_pair[0];
|
||||||
|
frame_id = id_pair[1];
|
||||||
|
} else {
|
||||||
|
isolate->ThrowException(v8::Exception::Error(gin::StringToV8(
|
||||||
|
isolate,
|
||||||
|
"frameId must be a number or a pair of [processId, frameId]")));
|
||||||
return false;
|
return false;
|
||||||
if (!(*iter)->IsRenderFrameLive())
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* rfh = content::RenderFrameHost::FromID(process_id, frame_id);
|
||||||
|
if (!rfh || !rfh->IsRenderFrameLive() ||
|
||||||
|
content::WebContents::FromRenderFrameHost(rfh) != web_contents())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
mojo::AssociatedRemote<mojom::ElectronRenderer> electron_renderer;
|
mojo::AssociatedRemote<mojom::ElectronRenderer> electron_renderer;
|
||||||
(*iter)->GetRemoteAssociatedInterfaces()->GetInterface(&electron_renderer);
|
rfh->GetRemoteAssociatedInterfaces()->GetInterface(&electron_renderer);
|
||||||
electron_renderer->Message(internal, channel, std::move(message),
|
electron_renderer->Message(internal, channel, std::move(message),
|
||||||
0 /* sender_id */);
|
0 /* sender_id */);
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -258,7 +258,7 @@ class WebContents : public gin::Wrappable<WebContents>,
|
||||||
int32_t sender_id = 0);
|
int32_t sender_id = 0);
|
||||||
|
|
||||||
bool SendIPCMessageToFrame(bool internal,
|
bool SendIPCMessageToFrame(bool internal,
|
||||||
int32_t frame_id,
|
v8::Local<v8::Value> frame,
|
||||||
const std::string& channel,
|
const std::string& channel,
|
||||||
v8::Local<v8::Value> args);
|
v8::Local<v8::Value> args);
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "shell/common/gin_helper/event_emitter.h"
|
#include "shell/common/gin_helper/event_emitter.h"
|
||||||
|
|
||||||
#include "content/public/browser/render_frame_host.h"
|
#include "content/public/browser/render_frame_host.h"
|
||||||
|
#include "content/public/browser/render_process_host.h"
|
||||||
#include "shell/browser/api/event.h"
|
#include "shell/browser/api/event.h"
|
||||||
#include "shell/common/gin_helper/dictionary.h"
|
#include "shell/common/gin_helper/dictionary.h"
|
||||||
#include "shell/common/gin_helper/object_template_builder.h"
|
#include "shell/common/gin_helper/object_template_builder.h"
|
||||||
|
@ -67,8 +68,10 @@ v8::Local<v8::Object> CreateNativeEvent(
|
||||||
Dictionary dict(isolate, event);
|
Dictionary dict(isolate, event);
|
||||||
dict.Set("sender", sender);
|
dict.Set("sender", sender);
|
||||||
// Should always set frameId even when callback is null.
|
// Should always set frameId even when callback is null.
|
||||||
if (frame)
|
if (frame) {
|
||||||
dict.Set("frameId", frame->GetRoutingID());
|
dict.Set("frameId", frame->GetRoutingID());
|
||||||
|
dict.Set("processId", frame->GetProcess()->GetID());
|
||||||
|
}
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ import * as path from 'path';
|
||||||
import * as cp from 'child_process';
|
import * as cp from 'child_process';
|
||||||
import { closeAllWindows } from './window-helpers';
|
import { closeAllWindows } from './window-helpers';
|
||||||
import { emittedOnce } from './events-helpers';
|
import { emittedOnce } from './events-helpers';
|
||||||
|
import { defer } from './spec-helpers';
|
||||||
import { ipcMain, BrowserWindow } from 'electron/main';
|
import { ipcMain, BrowserWindow } from 'electron/main';
|
||||||
|
|
||||||
describe('ipc main module', () => {
|
describe('ipc main module', () => {
|
||||||
|
@ -59,5 +60,30 @@ describe('ipc main module', () => {
|
||||||
output = JSON.parse(output);
|
output = JSON.parse(output);
|
||||||
expect(output).to.deep.equal(['error']);
|
expect(output).to.deep.equal(['error']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('can be replied to', async () => {
|
||||||
|
ipcMain.on('test-echo', (e, arg) => {
|
||||||
|
e.reply('test-echo', arg);
|
||||||
|
});
|
||||||
|
defer(() => {
|
||||||
|
ipcMain.removeAllListeners('test-echo');
|
||||||
|
});
|
||||||
|
|
||||||
|
const w = new BrowserWindow({
|
||||||
|
show: false,
|
||||||
|
webPreferences: {
|
||||||
|
nodeIntegration: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
w.loadURL('about:blank');
|
||||||
|
const v = await w.webContents.executeJavaScript(`new Promise((resolve, reject) => {
|
||||||
|
const { ipcRenderer } = require('electron')
|
||||||
|
ipcRenderer.send('test-echo', 'hello')
|
||||||
|
ipcRenderer.on('test-echo', (e, v) => {
|
||||||
|
resolve(v)
|
||||||
|
})
|
||||||
|
})`);
|
||||||
|
expect(v).to.equal('hello');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
"top=5,left=10,resizable=no",
|
"top=5,left=10,resizable=no",
|
||||||
{
|
{
|
||||||
"sender": "[WebContents]",
|
"sender": "[WebContents]",
|
||||||
"frameId": 1
|
"frameId": 1,
|
||||||
|
"processId": "placeholder-process-id"
|
||||||
},
|
},
|
||||||
"about:blank",
|
"about:blank",
|
||||||
"frame name",
|
"frame name",
|
||||||
|
@ -37,7 +38,8 @@
|
||||||
"zoomFactor=2,resizable=0,x=0,y=10",
|
"zoomFactor=2,resizable=0,x=0,y=10",
|
||||||
{
|
{
|
||||||
"sender": "[WebContents]",
|
"sender": "[WebContents]",
|
||||||
"frameId": 1
|
"frameId": 1,
|
||||||
|
"processId": "placeholder-process-id"
|
||||||
},
|
},
|
||||||
"about:blank",
|
"about:blank",
|
||||||
"frame name",
|
"frame name",
|
||||||
|
@ -70,7 +72,8 @@
|
||||||
"backgroundColor=gray,webPreferences=0,x=100,y=100",
|
"backgroundColor=gray,webPreferences=0,x=100,y=100",
|
||||||
{
|
{
|
||||||
"sender": "[WebContents]",
|
"sender": "[WebContents]",
|
||||||
"frameId": 1
|
"frameId": 1,
|
||||||
|
"processId": "placeholder-process-id"
|
||||||
},
|
},
|
||||||
"about:blank",
|
"about:blank",
|
||||||
"frame name",
|
"frame name",
|
||||||
|
@ -103,7 +106,8 @@
|
||||||
"x=50,y=20,title=sup",
|
"x=50,y=20,title=sup",
|
||||||
{
|
{
|
||||||
"sender": "[WebContents]",
|
"sender": "[WebContents]",
|
||||||
"frameId": 1
|
"frameId": 1,
|
||||||
|
"processId": "placeholder-process-id"
|
||||||
},
|
},
|
||||||
"about:blank",
|
"about:blank",
|
||||||
"frame name",
|
"frame name",
|
||||||
|
@ -134,7 +138,8 @@
|
||||||
"show=false,top=1,left=1",
|
"show=false,top=1,left=1",
|
||||||
{
|
{
|
||||||
"sender": "[WebContents]",
|
"sender": "[WebContents]",
|
||||||
"frameId": 1
|
"frameId": 1,
|
||||||
|
"processId": "placeholder-process-id"
|
||||||
},
|
},
|
||||||
"about:blank",
|
"about:blank",
|
||||||
"frame name",
|
"frame name",
|
||||||
|
|
|
@ -189,6 +189,9 @@ function stringifySnapshots (snapshots: any, pretty = false) {
|
||||||
if (key === 'openerId' && typeof value === 'number') {
|
if (key === 'openerId' && typeof value === 'number') {
|
||||||
return 'placeholder-opener-id';
|
return 'placeholder-opener-id';
|
||||||
}
|
}
|
||||||
|
if (key === 'processId' && typeof value === 'number') {
|
||||||
|
return 'placeholder-process-id';
|
||||||
|
}
|
||||||
if (key === 'returnValue') {
|
if (key === 'returnValue') {
|
||||||
return 'placeholder-guest-contents-id';
|
return 'placeholder-guest-contents-id';
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,8 +68,8 @@ declare namespace Electron {
|
||||||
_callWindowOpenHandler(event: any, url: string, frameName: string, rawFeatures: string): Electron.BrowserWindowConstructorOptions | null;
|
_callWindowOpenHandler(event: any, url: string, frameName: string, rawFeatures: string): Electron.BrowserWindowConstructorOptions | null;
|
||||||
_setNextChildWebPreferences(prefs: Partial<Electron.BrowserWindowConstructorOptions['webPreferences']> & Pick<Electron.BrowserWindowConstructorOptions, 'backgroundColor'>): void;
|
_setNextChildWebPreferences(prefs: Partial<Electron.BrowserWindowConstructorOptions['webPreferences']> & Pick<Electron.BrowserWindowConstructorOptions, 'backgroundColor'>): void;
|
||||||
_send(internal: boolean, channel: string, args: any): boolean;
|
_send(internal: boolean, channel: string, args: any): boolean;
|
||||||
_sendToFrame(internal: boolean, frameId: number, channel: string, args: any): boolean;
|
_sendToFrame(internal: boolean, frameId: number | [number, number], channel: string, args: any): boolean;
|
||||||
_sendToFrameInternal(frameId: number, channel: string, ...args: any[]): boolean;
|
_sendToFrameInternal(frameId: number | [number, number], channel: string, ...args: any[]): boolean;
|
||||||
_postMessage(channel: string, message: any, transfer?: any[]): void;
|
_postMessage(channel: string, message: any, transfer?: any[]): void;
|
||||||
_sendInternal(channel: string, ...args: any[]): void;
|
_sendInternal(channel: string, ...args: any[]): void;
|
||||||
_printToPDF(options: any): Promise<Buffer>;
|
_printToPDF(options: any): Promise<Buffer>;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче