зеркало из https://github.com/electron/electron.git
feat: service worker IPC
This commit is contained in:
Родитель
f2a9104437
Коммит
dc6a504c2f
|
@ -0,0 +1,68 @@
|
|||
## Class: IpcMainServiceWorker
|
||||
|
||||
> Communicate asynchronously from the main process to service workers.
|
||||
|
||||
Process: [Main](../glossary.md#main-process)
|
||||
|
||||
### Instance Methods
|
||||
|
||||
#### `ipcMainServiceWorker.on(channel, listener)`
|
||||
|
||||
* `channel` string
|
||||
* `listener` Function
|
||||
* `event` [IpcMainServiceWorkerEvent][ipc-main-service-worker-event]
|
||||
* `...args` any[]
|
||||
|
||||
Listens to `channel`, when a new message arrives `listener` would be called with
|
||||
`listener(event, args...)`.
|
||||
|
||||
#### `ipcMainServiceWorker.once(channel, listener)`
|
||||
|
||||
* `channel` string
|
||||
* `listener` Function
|
||||
* `event` [IpcMainServiceWorkerEvent][ipc-main-service-worker-event]
|
||||
* `...args` any[]
|
||||
|
||||
Adds a one time `listener` function for the event. This `listener` is invoked
|
||||
only the next time a message is sent to `channel`, after which it is removed.
|
||||
|
||||
#### `ipcMainServiceWorker.removeListener(channel, listener)`
|
||||
|
||||
* `channel` string
|
||||
* `listener` Function
|
||||
* `...args` any[]
|
||||
|
||||
Removes the specified `listener` from the listener array for the specified
|
||||
`channel`.
|
||||
|
||||
#### `ipcMainServiceWorker.removeAllListeners([channel])`
|
||||
|
||||
* `channel` string (optional)
|
||||
|
||||
Removes listeners of the specified `channel`.
|
||||
|
||||
#### `ipcMainServiceWorker.handle(channel, listener)`
|
||||
|
||||
* `channel` string
|
||||
* `listener` Function\<Promise\<any\> | any\>
|
||||
* `event` [IpcMainServiceWorkerInvokeEvent][ipc-main-service-worker-invoke-event]
|
||||
* `...args` any[]
|
||||
|
||||
#### `ipcMainServiceWorker.handleOnce(channel, listener)`
|
||||
|
||||
* `channel` string
|
||||
* `listener` Function\<Promise\<any\> | any\>
|
||||
* `event` [IpcMainServiceWorkerInvokeEvent][ipc-main-service-worker-invoke-event]
|
||||
* `...args` any[]
|
||||
|
||||
Handles a single `invoke`able IPC message, then removes the listener. See
|
||||
`ipcMainServiceWorker.handle(channel, listener)`.
|
||||
|
||||
#### `ipcMainServiceWorker.removeHandler(channel)`
|
||||
|
||||
* `channel` string
|
||||
|
||||
Removes any handler for `channel`, if present.
|
||||
|
||||
[ipc-main-service-worker-event]:../api/structures/ipc-main-service-worker-event.md
|
||||
[ipc-main-service-worker-invoke-event]:../api/structures/ipc-main-service-worker-invoke-event.md
|
|
@ -1,5 +1,6 @@
|
|||
# IpcMainEvent Object extends `Event`
|
||||
|
||||
* `type` String - Possible values include `frame`
|
||||
* `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
|
||||
* `returnValue` any - Set this to the value to be returned in a synchronous message
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# IpcMainInvokeEvent Object extends `Event`
|
||||
|
||||
* `type` String - Possible values include `frame`
|
||||
* `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
|
||||
* `sender` [WebContents](../web-contents.md) - Returns the `webContents` that sent the message
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
# IpcMainServiceWorkerEvent Object extends `Event`
|
||||
|
||||
* `type` String - Possible values include `service-worker`.
|
||||
* `serviceWorker` [ServiceWorkerMain](../service-worker-main.md) _Readonly_ - The service worker that sent this message
|
||||
* `versionId` Number - The service worker version ID.
|
||||
* `session` Session - The [`Session`](../session.md) instance with which the event is associated.
|
||||
* `returnValue` any - Set this to the value to be returned in a synchronous message
|
||||
* `ports` [MessagePortMain](../message-port-main.md)[] - A list of MessagePorts that were transferred with this message
|
||||
* `reply` Function - A function that will send an IPC message to the renderer frame that sent the original message that you are currently handling. You should use this method to "reply" to the sent message in order to guarantee the reply will go to the correct process and frame.
|
||||
* `channel` string
|
||||
* `...args` any[]
|
|
@ -0,0 +1,6 @@
|
|||
# IpcMainServiceWorkerInvokeEvent Object extends `Event`
|
||||
|
||||
* `type` String - Possible values include `service-worker`.
|
||||
* `serviceWorker` [ServiceWorkerMain](../service-worker-main.md) _Readonly_ - The service worker that sent this message
|
||||
* `versionId` Number - The service worker version ID.
|
||||
* `session` Session - The [`Session`](../session.md) instance with which the event is associated.
|
|
@ -25,6 +25,7 @@ auto_filenames = {
|
|||
"docs/api/global-shortcut.md",
|
||||
"docs/api/in-app-purchase.md",
|
||||
"docs/api/incoming-message.md",
|
||||
"docs/api/ipc-main-service-worker.md",
|
||||
"docs/api/ipc-main.md",
|
||||
"docs/api/ipc-renderer.md",
|
||||
"docs/api/menu-item.md",
|
||||
|
@ -45,7 +46,7 @@ auto_filenames = {
|
|||
"docs/api/push-notifications.md",
|
||||
"docs/api/safe-storage.md",
|
||||
"docs/api/screen.md",
|
||||
"docs/api/service-worker-main.md",
|
||||
"docs/api/service-worker-main.md",
|
||||
"docs/api/service-workers.md",
|
||||
"docs/api/session.md",
|
||||
"docs/api/share-menu.md",
|
||||
|
@ -95,6 +96,8 @@ auto_filenames = {
|
|||
"docs/api/structures/input-event.md",
|
||||
"docs/api/structures/ipc-main-event.md",
|
||||
"docs/api/structures/ipc-main-invoke-event.md",
|
||||
"docs/api/structures/ipc-main-service-worker-event.md",
|
||||
"docs/api/structures/ipc-main-service-worker-invoke-event.md",
|
||||
"docs/api/structures/ipc-renderer-event.md",
|
||||
"docs/api/structures/jump-list-category.md",
|
||||
"docs/api/structures/jump-list-item.md",
|
||||
|
@ -115,7 +118,7 @@ auto_filenames = {
|
|||
"docs/api/structures/permission-request.md",
|
||||
"docs/api/structures/point.md",
|
||||
"docs/api/structures/post-body.md",
|
||||
"docs/api/structures/preload-script.md",
|
||||
"docs/api/structures/preload-script.md",
|
||||
"docs/api/structures/printer-info.md",
|
||||
"docs/api/structures/process-memory-info.md",
|
||||
"docs/api/structures/process-metric.md",
|
||||
|
@ -168,6 +171,7 @@ auto_filenames = {
|
|||
"lib/renderer/api/web-utils.ts",
|
||||
"lib/renderer/common-init.ts",
|
||||
"lib/renderer/inspector.ts",
|
||||
"lib/renderer/ipc-native-setup.ts",
|
||||
"lib/renderer/ipc-renderer-internal-utils.ts",
|
||||
"lib/renderer/ipc-renderer-internal.ts",
|
||||
"lib/renderer/security-warnings.ts",
|
||||
|
@ -256,6 +260,7 @@ auto_filenames = {
|
|||
"lib/browser/guest-view-manager.ts",
|
||||
"lib/browser/guest-window-manager.ts",
|
||||
"lib/browser/init.ts",
|
||||
"lib/browser/ipc-dispatch.ts",
|
||||
"lib/browser/ipc-main-impl.ts",
|
||||
"lib/browser/ipc-main-internal-utils.ts",
|
||||
"lib/browser/ipc-main-internal.ts",
|
||||
|
@ -300,6 +305,7 @@ auto_filenames = {
|
|||
"lib/renderer/common-init.ts",
|
||||
"lib/renderer/init.ts",
|
||||
"lib/renderer/inspector.ts",
|
||||
"lib/renderer/ipc-native-setup.ts",
|
||||
"lib/renderer/ipc-renderer-internal-utils.ts",
|
||||
"lib/renderer/ipc-renderer-internal.ts",
|
||||
"lib/renderer/security-warnings.ts",
|
||||
|
|
|
@ -304,7 +304,7 @@ filenames = {
|
|||
"shell/browser/api/electron_api_screen.h",
|
||||
"shell/browser/api/electron_api_service_worker_context.cc",
|
||||
"shell/browser/api/electron_api_service_worker_context.h",
|
||||
"shell/browser/api/electron_api_service_worker_main.cc",
|
||||
"shell/browser/api/electron_api_service_worker_main.cc",
|
||||
"shell/browser/api/electron_api_service_worker_main.h",
|
||||
"shell/browser/api/electron_api_session.cc",
|
||||
"shell/browser/api/electron_api_session.h",
|
||||
|
@ -332,6 +332,7 @@ filenames = {
|
|||
"shell/browser/api/gpu_info_enumerator.h",
|
||||
"shell/browser/api/gpuinfo_manager.cc",
|
||||
"shell/browser/api/gpuinfo_manager.h",
|
||||
"shell/browser/api/ipc_dispatcher.h",
|
||||
"shell/browser/api/message_port.cc",
|
||||
"shell/browser/api/message_port.h",
|
||||
"shell/browser/api/process_metric.cc",
|
||||
|
@ -363,6 +364,8 @@ filenames = {
|
|||
"shell/browser/draggable_region_provider.h",
|
||||
"shell/browser/electron_api_ipc_handler_impl.cc",
|
||||
"shell/browser/electron_api_ipc_handler_impl.h",
|
||||
"shell/browser/electron_api_sw_ipc_handler_impl.cc",
|
||||
"shell/browser/electron_api_sw_ipc_handler_impl.h",
|
||||
"shell/browser/electron_autofill_driver.cc",
|
||||
"shell/browser/electron_autofill_driver.h",
|
||||
"shell/browser/electron_autofill_driver_factory.cc",
|
||||
|
@ -620,7 +623,7 @@ filenames = {
|
|||
"shell/common/gin_converters/osr_converter.cc",
|
||||
"shell/common/gin_converters/osr_converter.h",
|
||||
"shell/common/gin_converters/serial_port_info_converter.h",
|
||||
"shell/common/gin_converters/service_worker_converter.cc",
|
||||
"shell/common/gin_converters/service_worker_converter.cc",
|
||||
"shell/common/gin_converters/service_worker_converter.h",
|
||||
"shell/common/gin_converters/std_converter.h",
|
||||
"shell/common/gin_converters/time_converter.cc",
|
||||
|
@ -662,6 +665,8 @@ filenames = {
|
|||
"shell/common/gin_helper/pinnable.h",
|
||||
"shell/common/gin_helper/promise.cc",
|
||||
"shell/common/gin_helper/promise.h",
|
||||
"shell/common/gin_helper/reply_channel.cc",
|
||||
"shell/common/gin_helper/reply_channel.h",
|
||||
"shell/common/gin_helper/trackable_object.cc",
|
||||
"shell/common/gin_helper/trackable_object.h",
|
||||
"shell/common/gin_helper/wrappable.cc",
|
||||
|
@ -711,6 +716,8 @@ filenames = {
|
|||
"shell/renderer/electron_api_service_impl.h",
|
||||
"shell/renderer/electron_autofill_agent.cc",
|
||||
"shell/renderer/electron_autofill_agent.h",
|
||||
"shell/renderer/electron_ipc_native.cc",
|
||||
"shell/renderer/electron_ipc_native.h",
|
||||
"shell/renderer/electron_render_frame_observer.cc",
|
||||
"shell/renderer/electron_render_frame_observer.h",
|
||||
"shell/renderer/electron_renderer_client.cc",
|
||||
|
@ -723,6 +730,8 @@ filenames = {
|
|||
"shell/renderer/preload_utils.h",
|
||||
"shell/renderer/renderer_client_base.cc",
|
||||
"shell/renderer/renderer_client_base.h",
|
||||
"shell/renderer/service_worker_data.cc",
|
||||
"shell/renderer/service_worker_data.h",
|
||||
"shell/renderer/web_worker_observer.cc",
|
||||
"shell/renderer/web_worker_observer.h",
|
||||
"shell/services/node/node_service.cc",
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { fetchWithSession } from '@electron/internal/browser/api/net-fetch';
|
||||
import { addIpcDispatchListeners } from '@electron/internal/browser/ipc-dispatch';
|
||||
import * as deprecate from '@electron/internal/common/deprecate';
|
||||
|
||||
import { net } from 'electron/main';
|
||||
|
@ -21,6 +22,10 @@ Object.defineProperty(systemPickerVideoSource, 'id', {
|
|||
systemPickerVideoSource.name = '';
|
||||
Object.freeze(systemPickerVideoSource);
|
||||
|
||||
Session.prototype._init = function () {
|
||||
addIpcDispatchListeners(this, this.serviceWorkers);
|
||||
};
|
||||
|
||||
Session.prototype.fetch = function (input: RequestInfo, init?: RequestInit) {
|
||||
return fetchWithSession(input, init, this, net.request);
|
||||
};
|
||||
|
|
|
@ -69,6 +69,7 @@ const assertChromeDevTools = function (contents: Electron.WebContents, api: stri
|
|||
|
||||
ipcMainInternal.handle(IPC_MESSAGES.INSPECTOR_CONTEXT_MENU, function (event, items: ContextMenuItem[], isEditMenu: boolean) {
|
||||
return new Promise<number | void>(resolve => {
|
||||
if (event.type !== 'frame') return;
|
||||
assertChromeDevTools(event.sender, 'window.InspectorFrontendHost.showContextMenuAtPoint()');
|
||||
|
||||
const template = isEditMenu ? getEditMenuItems() : convertToMenuTemplate(items, resolve);
|
||||
|
@ -80,6 +81,7 @@ ipcMainInternal.handle(IPC_MESSAGES.INSPECTOR_CONTEXT_MENU, function (event, ite
|
|||
});
|
||||
|
||||
ipcMainInternal.handle(IPC_MESSAGES.INSPECTOR_SELECT_FILE, async function (event) {
|
||||
if (event.type !== 'frame') return [];
|
||||
assertChromeDevTools(event.sender, 'window.UI.createFileSelectorElement()');
|
||||
|
||||
const result = await dialog.showOpenDialog({});
|
||||
|
@ -92,6 +94,7 @@ ipcMainInternal.handle(IPC_MESSAGES.INSPECTOR_SELECT_FILE, async function (event
|
|||
});
|
||||
|
||||
ipcMainUtils.handleSync(IPC_MESSAGES.INSPECTOR_CONFIRM, async function (event, message: string = '', title: string = '') {
|
||||
if (event.type !== 'frame') return;
|
||||
assertChromeDevTools(event.sender, 'window.confirm()');
|
||||
|
||||
const options = {
|
||||
|
|
|
@ -267,9 +267,10 @@ const isWebViewTagEnabled = function (contents: Electron.WebContents) {
|
|||
};
|
||||
|
||||
const makeSafeHandler = function<Event extends { sender: Electron.WebContents }> (channel: string, handler: (event: Event, ...args: any[]) => any) {
|
||||
return (event: Event, ...args: any[]) => {
|
||||
return (event: Electron.IpcMainInvokeEvent | Electron.IpcMainServiceWorkerInvokeEvent, ...args: any[]) => {
|
||||
if (event.type !== 'frame') return;
|
||||
if (isWebViewTagEnabled(event.sender)) {
|
||||
return handler(event, ...args);
|
||||
return handler(event as unknown as Event, ...args);
|
||||
} else {
|
||||
console.error(`<webview> IPC message ${channel} sent by WebContents with <webview> disabled (${event.sender.id})`);
|
||||
throw new Error('<webview> disabled');
|
||||
|
@ -281,7 +282,7 @@ const handleMessage = function (channel: string, handler: (event: Electron.IpcMa
|
|||
ipcMainInternal.handle(channel, makeSafeHandler(channel, handler));
|
||||
};
|
||||
|
||||
const handleMessageSync = function (channel: string, handler: (event: ElectronInternal.IpcMainInternalEvent, ...args: any[]) => any) {
|
||||
const handleMessageSync = function (channel: string, handler: (event: { sender: Electron.WebContents }, ...args: any[]) => any) {
|
||||
ipcMainUtils.handleSync(channel, makeSafeHandler(channel, handler));
|
||||
};
|
||||
|
||||
|
@ -294,8 +295,10 @@ handleMessageSync(IPC_MESSAGES.GUEST_VIEW_MANAGER_DETACH_GUEST, function (event,
|
|||
});
|
||||
|
||||
// this message is sent by the actual <webview>
|
||||
ipcMainInternal.on(IPC_MESSAGES.GUEST_VIEW_MANAGER_FOCUS_CHANGE, function (event: ElectronInternal.IpcMainInternalEvent, focus: boolean) {
|
||||
event.sender.emit('-focus-change', {}, focus);
|
||||
ipcMainInternal.on(IPC_MESSAGES.GUEST_VIEW_MANAGER_FOCUS_CHANGE, function (event, focus: boolean) {
|
||||
if (event.type === 'frame') {
|
||||
event.sender.emit('-focus-change', {}, focus);
|
||||
}
|
||||
});
|
||||
|
||||
handleMessage(IPC_MESSAGES.GUEST_VIEW_MANAGER_CALL, function (event, guestInstanceId: number, method: string, args: any[]) {
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal';
|
||||
import { MessagePortMain } from '@electron/internal/browser/message-port-main';
|
||||
|
||||
import type { ServiceWorkerMain } from 'electron/main';
|
||||
|
||||
const v8Util = process._linkedBinding('electron_common_v8_util');
|
||||
|
||||
const addReturnValueToEvent = (event: Electron.IpcMainEvent | Electron.IpcMainServiceWorkerEvent) => {
|
||||
Object.defineProperty(event, 'returnValue', {
|
||||
set: (value) => event._replyChannel.sendReply(value),
|
||||
get: () => {}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Listens for IPC dispatch events on `api`.
|
||||
*
|
||||
* NOTE: Currently this only supports dispatching IPCs for ServiceWorkerMain.
|
||||
*/
|
||||
export function addIpcDispatchListeners (api: NodeJS.EventEmitter, serviceWorkers: Electron.ServiceWorkers) {
|
||||
const getServiceWorkerFromEvent = (event: Electron.IpcMainServiceWorkerEvent | Electron.IpcMainServiceWorkerInvokeEvent): ServiceWorkerMain | undefined => {
|
||||
return serviceWorkers._fromVersionIDIfExists(event.versionId);
|
||||
};
|
||||
const addServiceWorkerPropertyToEvent = (event: Electron.IpcMainServiceWorkerEvent | Electron.IpcMainServiceWorkerInvokeEvent) => {
|
||||
Object.defineProperty(event, 'serviceWorker', {
|
||||
get: () => serviceWorkers.fromVersionID(event.versionId)
|
||||
});
|
||||
};
|
||||
|
||||
api.on('-ipc-message' as any, function (event: Electron.IpcMainEvent | Electron.IpcMainServiceWorkerEvent, channel: string, args: any[]) {
|
||||
const internal = v8Util.getHiddenValue<boolean>(event, 'internal');
|
||||
|
||||
if (internal) {
|
||||
ipcMainInternal.emit(channel, event, ...args);
|
||||
} else if (event.type === 'service-worker') {
|
||||
addServiceWorkerPropertyToEvent(event);
|
||||
getServiceWorkerFromEvent(event)?.ipc.emit(channel, event, ...args);
|
||||
}
|
||||
} as any);
|
||||
|
||||
api.on('-ipc-invoke' as any, async function (event: Electron.IpcMainInvokeEvent | Electron.IpcMainServiceWorkerInvokeEvent, channel: string, args: any[]) {
|
||||
const internal = v8Util.getHiddenValue<boolean>(event, 'internal');
|
||||
|
||||
const replyWithResult = (result: any) => event._replyChannel.sendReply({ result });
|
||||
const replyWithError = (error: Error) => {
|
||||
console.error(`Error occurred in handler for '${channel}':`, error);
|
||||
event._replyChannel.sendReply({ error: error.toString() });
|
||||
};
|
||||
|
||||
const targets: (Electron.IpcMainServiceWorker | ElectronInternal.IpcMainInternal | undefined)[] = [];
|
||||
|
||||
if (internal) {
|
||||
targets.push(ipcMainInternal);
|
||||
} else if (event.type === 'service-worker') {
|
||||
addServiceWorkerPropertyToEvent(event);
|
||||
const workerIpc = getServiceWorkerFromEvent(event)?.ipc;
|
||||
targets.push(workerIpc);
|
||||
}
|
||||
|
||||
const target = targets.find(target => (target as any)?._invokeHandlers.has(channel));
|
||||
if (target) {
|
||||
const handler = (target as any)._invokeHandlers.get(channel);
|
||||
try {
|
||||
replyWithResult(await Promise.resolve(handler(event, ...args)));
|
||||
} catch (err) {
|
||||
replyWithError(err as Error);
|
||||
}
|
||||
} else {
|
||||
replyWithError(new Error(`No handler registered for '${channel}'`));
|
||||
}
|
||||
} as any);
|
||||
|
||||
api.on('-ipc-message-sync' as any, function (event: Electron.IpcMainEvent | Electron.IpcMainServiceWorkerEvent, channel: string, args: any[]) {
|
||||
const internal = v8Util.getHiddenValue<boolean>(event, 'internal');
|
||||
addReturnValueToEvent(event);
|
||||
if (internal) {
|
||||
ipcMainInternal.emit(channel, event, ...args);
|
||||
} else if (event.type === 'service-worker') {
|
||||
addServiceWorkerPropertyToEvent(event);
|
||||
getServiceWorkerFromEvent(event)?.ipc.emit(channel, event, ...args);
|
||||
}
|
||||
} as any);
|
||||
|
||||
api.on('-ipc-ports' as any, function (event: Electron.IpcMainEvent | Electron.IpcMainServiceWorkerEvent, channel: string, message: any, ports: any[]) {
|
||||
event.ports = ports.map(p => new MessagePortMain(p));
|
||||
if (event.type === 'service-worker') {
|
||||
addServiceWorkerPropertyToEvent(event);
|
||||
getServiceWorkerFromEvent(event)?.ipc.emit(channel, event, message);
|
||||
}
|
||||
} as any);
|
||||
}
|
|
@ -19,7 +19,7 @@ export function invokeInWebContents<T> (sender: Electron.WebContents, command: s
|
|||
const requestId = ++nextId;
|
||||
const channel = `${command}_RESPONSE_${requestId}`;
|
||||
ipcMainInternal.on(channel, function handler (event, error: Error, result: any) {
|
||||
if (event.sender !== sender) {
|
||||
if (event.type === 'frame' && event.sender !== sender) {
|
||||
console.error(`Reply to ${command} sent by unexpected WebContents (${event.sender.id})`);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ import * as path from 'path';
|
|||
|
||||
// Implements window.close()
|
||||
ipcMainInternal.on(IPC_MESSAGES.BROWSER_WINDOW_CLOSE, function (event) {
|
||||
if (event.type !== 'frame') return;
|
||||
|
||||
const window = event.sender.getOwnerBrowserWindow();
|
||||
if (window) {
|
||||
window.close();
|
||||
|
@ -17,10 +19,12 @@ ipcMainInternal.on(IPC_MESSAGES.BROWSER_WINDOW_CLOSE, function (event) {
|
|||
});
|
||||
|
||||
ipcMainInternal.handle(IPC_MESSAGES.BROWSER_GET_LAST_WEB_PREFERENCES, function (event) {
|
||||
if (event.type !== 'frame') return;
|
||||
return event.sender.getLastWebPreferences();
|
||||
});
|
||||
|
||||
ipcMainInternal.handle(IPC_MESSAGES.BROWSER_GET_PROCESS_MEMORY_INFO, function (event) {
|
||||
if (event.type !== 'frame') return;
|
||||
return event.sender._getProcessMemoryInfo();
|
||||
});
|
||||
|
||||
|
@ -101,5 +105,6 @@ ipcMainUtils.handleSync(IPC_MESSAGES.BROWSER_NONSANDBOX_LOAD, function (event) {
|
|||
});
|
||||
|
||||
ipcMainInternal.on(IPC_MESSAGES.BROWSER_PRELOAD_ERROR, function (event, preloadPath: string, error: Error) {
|
||||
event.sender.emit('preload-error', event, preloadPath, error);
|
||||
if (event.type !== 'frame') return;
|
||||
event.sender?.emit('preload-error', event, preloadPath, error);
|
||||
});
|
||||
|
|
|
@ -1,27 +1,16 @@
|
|||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal';
|
||||
import type * as securityWarningsModule from '@electron/internal/renderer/security-warnings';
|
||||
import type * as webFrameInitModule from '@electron/internal/renderer/web-frame-init';
|
||||
import type * as webViewInitModule from '@electron/internal/renderer/web-view/web-view-init';
|
||||
import type * as windowSetupModule from '@electron/internal/renderer/window-setup';
|
||||
|
||||
import { ipcRenderer } from 'electron/renderer';
|
||||
|
||||
const { mainFrame } = process._linkedBinding('electron_renderer_web_frame');
|
||||
const v8Util = process._linkedBinding('electron_common_v8_util');
|
||||
|
||||
const nodeIntegration = mainFrame.getWebPreference('nodeIntegration');
|
||||
const webviewTag = mainFrame.getWebPreference('webviewTag');
|
||||
const isHiddenPage = mainFrame.getWebPreference('hiddenPage');
|
||||
const isWebView = mainFrame.getWebPreference('isWebView');
|
||||
|
||||
// ElectronApiServiceImpl will look for the "ipcNative" hidden object when
|
||||
// invoking the 'onMessage' callback.
|
||||
v8Util.setHiddenValue(global, 'ipcNative', {
|
||||
onMessage (internal: boolean, channel: string, ports: MessagePort[], args: any[]) {
|
||||
const sender = internal ? ipcRendererInternal : ipcRenderer;
|
||||
sender.emit(channel, { sender, ports }, ...args);
|
||||
}
|
||||
});
|
||||
require('@electron/internal/renderer/ipc-native-setup');
|
||||
|
||||
switch (window.location.protocol) {
|
||||
case 'devtools:': {
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal';
|
||||
|
||||
import { ipcRenderer } from 'electron/renderer';
|
||||
|
||||
const v8Util = process._linkedBinding('electron_common_v8_util');
|
||||
|
||||
// ElectronApiServiceImpl will look for the "ipcNative" hidden object when
|
||||
// invoking the 'onMessage' callback.
|
||||
v8Util.setHiddenValue(globalThis, 'ipcNative', {
|
||||
onMessage (internal: boolean, channel: string, ports: MessagePort[], args: any[]) {
|
||||
const sender = internal ? ipcRendererInternal : ipcRenderer;
|
||||
sender.emit(channel, { sender, ports }, ...args);
|
||||
}
|
||||
});
|
|
@ -1585,6 +1585,12 @@ gin::Handle<Session> Session::CreateFrom(
|
|||
// to use partition strings, instead of using the Session object directly.
|
||||
handle->Pin(isolate);
|
||||
|
||||
v8::TryCatch try_catch(isolate);
|
||||
gin_helper::CallMethod(isolate, handle.get(), "_init");
|
||||
if (try_catch.HasCaught()) {
|
||||
node::errors::TriggerUncaughtException(isolate, try_catch);
|
||||
}
|
||||
|
||||
App::Get()->EmitWithoutEvent("session-created", handle);
|
||||
|
||||
return handle;
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "gin/wrappable.h"
|
||||
#include "services/network/public/mojom/host_resolver.mojom-forward.h"
|
||||
#include "services/network/public/mojom/ssl_config.mojom-forward.h"
|
||||
#include "shell/browser/api/ipc_dispatcher.h"
|
||||
#include "shell/browser/event_emitter_mixin.h"
|
||||
#include "shell/browser/net/resolve_proxy_helper.h"
|
||||
#include "shell/common/gin_helper/cleaned_up_at_exit.h"
|
||||
|
@ -66,6 +67,7 @@ class Session final : public gin::Wrappable<Session>,
|
|||
public gin_helper::Constructible<Session>,
|
||||
public gin_helper::EventEmitterMixin<Session>,
|
||||
public gin_helper::CleanedUpAtExit,
|
||||
public IpcDispatcher<Session>,
|
||||
#if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
|
||||
private SpellcheckHunspellDictionary::Observer,
|
||||
#endif
|
||||
|
|
|
@ -130,6 +130,7 @@
|
|||
#include "shell/common/gin_helper/locker.h"
|
||||
#include "shell/common/gin_helper/object_template_builder.h"
|
||||
#include "shell/common/gin_helper/promise.h"
|
||||
#include "shell/common/gin_helper/reply_channel.h"
|
||||
#include "shell/common/language_util.h"
|
||||
#include "shell/common/node_includes.h"
|
||||
#include "shell/common/node_util.h"
|
||||
|
@ -1920,66 +1921,6 @@ void WebContents::OnFirstNonEmptyLayout(
|
|||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// This object wraps the InvokeCallback so that if it gets GC'd by V8, we can
|
||||
// still call the callback and send an error. Not doing so causes a Mojo DCHECK,
|
||||
// since Mojo requires callbacks to be called before they are destroyed.
|
||||
class ReplyChannel final : public gin::Wrappable<ReplyChannel> {
|
||||
public:
|
||||
using InvokeCallback = electron::mojom::ElectronApiIPC::InvokeCallback;
|
||||
static gin::Handle<ReplyChannel> Create(v8::Isolate* isolate,
|
||||
InvokeCallback callback) {
|
||||
return gin::CreateHandle(isolate, new ReplyChannel(std::move(callback)));
|
||||
}
|
||||
|
||||
// gin::Wrappable
|
||||
static gin::WrapperInfo kWrapperInfo;
|
||||
gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
|
||||
v8::Isolate* isolate) override {
|
||||
return gin::Wrappable<ReplyChannel>::GetObjectTemplateBuilder(isolate)
|
||||
.SetMethod("sendReply", &ReplyChannel::SendReply);
|
||||
}
|
||||
const char* GetTypeName() override { return "ReplyChannel"; }
|
||||
|
||||
void SendError(const std::string& msg) {
|
||||
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
|
||||
// If there's no current context, it means we're shutting down, so we
|
||||
// don't need to send an event.
|
||||
if (!isolate->GetCurrentContext().IsEmpty()) {
|
||||
v8::HandleScope scope(isolate);
|
||||
auto message = gin::DataObjectBuilder(isolate).Set("error", msg).Build();
|
||||
SendReply(isolate, message);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
explicit ReplyChannel(InvokeCallback callback)
|
||||
: callback_(std::move(callback)) {}
|
||||
~ReplyChannel() override {
|
||||
if (callback_)
|
||||
SendError("reply was never sent");
|
||||
}
|
||||
|
||||
bool SendReply(v8::Isolate* isolate, v8::Local<v8::Value> arg) {
|
||||
if (!callback_)
|
||||
return false;
|
||||
blink::CloneableMessage message;
|
||||
if (!gin::ConvertFromV8(isolate, arg, &message)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::move(callback_).Run(std::move(message));
|
||||
return true;
|
||||
}
|
||||
|
||||
InvokeCallback callback_;
|
||||
};
|
||||
|
||||
gin::WrapperInfo ReplyChannel::kWrapperInfo = {gin::kEmbedderNativeGin};
|
||||
|
||||
} // namespace
|
||||
|
||||
gin::Handle<gin_helper::internal::Event> WebContents::MakeEventWithSender(
|
||||
v8::Isolate* isolate,
|
||||
content::RenderFrameHost* frame,
|
||||
|
@ -1988,7 +1929,7 @@ gin::Handle<gin_helper::internal::Event> WebContents::MakeEventWithSender(
|
|||
if (!GetWrapper(isolate).ToLocal(&wrapper)) {
|
||||
if (callback) {
|
||||
// We must always invoke the callback if present.
|
||||
ReplyChannel::Create(isolate, std::move(callback))
|
||||
gin_helper::internal::ReplyChannel::Create(isolate, std::move(callback))
|
||||
->SendError("WebContents was destroyed");
|
||||
}
|
||||
return gin::Handle<gin_helper::internal::Event>();
|
||||
|
@ -1996,9 +1937,10 @@ gin::Handle<gin_helper::internal::Event> WebContents::MakeEventWithSender(
|
|||
gin::Handle<gin_helper::internal::Event> event =
|
||||
gin_helper::internal::Event::New(isolate);
|
||||
gin_helper::Dictionary dict(isolate, event.ToV8().As<v8::Object>());
|
||||
dict.Set("type", "frame");
|
||||
if (callback)
|
||||
dict.Set("_replyChannel",
|
||||
ReplyChannel::Create(isolate, std::move(callback)));
|
||||
dict.Set("_replyChannel", gin_helper::internal::ReplyChannel::Create(
|
||||
isolate, std::move(callback)));
|
||||
if (frame) {
|
||||
dict.SetGetter("senderFrame", frame);
|
||||
dict.Set("frameId", frame->GetRoutingID());
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
// Copyright (c) 2024 Samuel Maddock <sam@samuelmaddock.com>.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ELECTRON_SHELL_BROWSER_API_IPC_DISPATCHER_H_
|
||||
#define ELECTRON_SHELL_BROWSER_API_IPC_DISPATCHER_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/trace_event/trace_event.h"
|
||||
#include "base/values.h"
|
||||
#include "gin/handle.h"
|
||||
#include "shell/browser/api/message_port.h"
|
||||
#include "shell/browser/javascript_environment.h"
|
||||
#include "shell/common/api/api.mojom.h"
|
||||
#include "shell/common/gin_converters/blink_converter.h"
|
||||
#include "shell/common/gin_helper/dictionary.h"
|
||||
#include "shell/common/gin_helper/event.h"
|
||||
#include "shell/common/gin_helper/reply_channel.h"
|
||||
#include "shell/common/v8_util.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
// Handles dispatching IPCs to JS.
|
||||
// See ipc-dispatch.ts for JS listeners.
|
||||
template <typename T>
|
||||
class IpcDispatcher {
|
||||
public:
|
||||
void Message(gin::Handle<gin_helper::internal::Event>& event,
|
||||
const std::string& channel,
|
||||
blink::CloneableMessage args) {
|
||||
TRACE_EVENT1("electron", "IpcDispatcher::Message", "channel", channel);
|
||||
emitter()->EmitWithoutEvent("-ipc-message", event, channel, args);
|
||||
}
|
||||
|
||||
void Invoke(gin::Handle<gin_helper::internal::Event>& event,
|
||||
const std::string& channel,
|
||||
blink::CloneableMessage arguments,
|
||||
electron::mojom::ElectronApiIPC::InvokeCallback callback) {
|
||||
TRACE_EVENT1("electron", "IpcHelper::Invoke", "channel", channel);
|
||||
|
||||
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
|
||||
gin_helper::Dictionary dict(isolate, event.ToV8().As<v8::Object>());
|
||||
dict.Set("_replyChannel", gin_helper::internal::ReplyChannel::Create(
|
||||
isolate, std::move(callback)));
|
||||
|
||||
emitter()->EmitWithoutEvent("-ipc-invoke", event, channel,
|
||||
std::move(arguments));
|
||||
}
|
||||
|
||||
void ReceivePostMessage(gin::Handle<gin_helper::internal::Event>& event,
|
||||
const std::string& channel,
|
||||
blink::TransferableMessage message) {
|
||||
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
auto wrapped_ports =
|
||||
MessagePort::EntanglePorts(isolate, std::move(message.ports));
|
||||
v8::Local<v8::Value> message_value =
|
||||
electron::DeserializeV8Value(isolate, message);
|
||||
emitter()->EmitWithoutEvent("-ipc-ports", event, channel, message_value,
|
||||
std::move(wrapped_ports));
|
||||
}
|
||||
|
||||
void MessageSync(
|
||||
gin::Handle<gin_helper::internal::Event>& event,
|
||||
const std::string& channel,
|
||||
blink::CloneableMessage arguments,
|
||||
electron::mojom::ElectronApiIPC::MessageSyncCallback callback) {
|
||||
TRACE_EVENT1("electron", "IpcHelper::MessageSync", "channel", channel);
|
||||
|
||||
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
|
||||
gin_helper::Dictionary dict(isolate, event.ToV8().As<v8::Object>());
|
||||
dict.Set("_replyChannel", gin_helper::internal::ReplyChannel::Create(
|
||||
isolate, std::move(callback)));
|
||||
|
||||
emitter()->EmitWithoutEvent("-ipc-message-sync", event, channel,
|
||||
std::move(arguments));
|
||||
}
|
||||
|
||||
private:
|
||||
inline T* emitter() {
|
||||
// T must inherit from gin_helper::EventEmitterMixin<T>
|
||||
return static_cast<T*>(this);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace electron
|
||||
|
||||
#endif // ELECTRON_SHELL_BROWSER_API_IPC_DISPATCHER_H_
|
|
@ -0,0 +1,199 @@
|
|||
// Copyright (c) 2024 Samuel Maddock <sam@samuelmaddock.com>.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "shell/browser/electron_api_sw_ipc_handler_impl.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "base/containers/unique_ptr_adapters.h"
|
||||
#include "content/public/browser/render_frame_host.h"
|
||||
#include "content/public/browser/render_process_host.h"
|
||||
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
|
||||
#include "shell/browser/api/electron_api_session.h"
|
||||
#include "shell/browser/electron_browser_context.h"
|
||||
#include "shell/browser/javascript_environment.h"
|
||||
#include "shell/common/gin_helper/dictionary.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
namespace {
|
||||
const void* const kUserDataKey = &kUserDataKey;
|
||||
|
||||
class ServiceWorkerIPCList : public base::SupportsUserData::Data {
|
||||
public:
|
||||
std::vector<std::unique_ptr<ElectronApiSWIPCHandlerImpl>> list;
|
||||
|
||||
static ServiceWorkerIPCList* Get(
|
||||
content::RenderProcessHost* render_process_host,
|
||||
bool create_if_not_exists) {
|
||||
auto* service_worker_ipc_list = static_cast<ServiceWorkerIPCList*>(
|
||||
render_process_host->GetUserData(kUserDataKey));
|
||||
if (!service_worker_ipc_list && !create_if_not_exists) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!service_worker_ipc_list) {
|
||||
auto new_ipc_list = std::make_unique<ServiceWorkerIPCList>();
|
||||
service_worker_ipc_list = new_ipc_list.get();
|
||||
render_process_host->SetUserData(kUserDataKey, std::move(new_ipc_list));
|
||||
}
|
||||
return service_worker_ipc_list;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
ElectronApiSWIPCHandlerImpl::ElectronApiSWIPCHandlerImpl(
|
||||
content::RenderProcessHost* render_process_host,
|
||||
int64_t version_id,
|
||||
mojo::PendingAssociatedReceiver<mojom::ElectronApiIPC> receiver)
|
||||
: render_process_host_(render_process_host), version_id_(version_id) {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
|
||||
receiver_.Bind(std::move(receiver));
|
||||
receiver_.set_disconnect_handler(
|
||||
base::BindOnce(&ElectronApiSWIPCHandlerImpl::RemoteDisconnected,
|
||||
base::Unretained(this)));
|
||||
|
||||
render_process_host_->AddObserver(this);
|
||||
}
|
||||
|
||||
ElectronApiSWIPCHandlerImpl::~ElectronApiSWIPCHandlerImpl() {
|
||||
render_process_host_->RemoveObserver(this);
|
||||
}
|
||||
|
||||
void ElectronApiSWIPCHandlerImpl::RemoteDisconnected() {
|
||||
receiver_.reset();
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void ElectronApiSWIPCHandlerImpl::Message(bool internal,
|
||||
const std::string& channel,
|
||||
blink::CloneableMessage arguments) {
|
||||
auto* session = GetSession();
|
||||
if (session) {
|
||||
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
gin::Handle<gin_helper::internal::Event> event =
|
||||
MakeIPCEvent(isolate, internal);
|
||||
session->Message(event, channel, std::move(arguments));
|
||||
}
|
||||
}
|
||||
|
||||
void ElectronApiSWIPCHandlerImpl::Invoke(bool internal,
|
||||
const std::string& channel,
|
||||
blink::CloneableMessage arguments,
|
||||
InvokeCallback callback) {
|
||||
auto* session = GetSession();
|
||||
if (session) {
|
||||
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
gin::Handle<gin_helper::internal::Event> event =
|
||||
MakeIPCEvent(isolate, internal);
|
||||
session->Invoke(event, channel, std::move(arguments), std::move(callback));
|
||||
}
|
||||
}
|
||||
|
||||
void ElectronApiSWIPCHandlerImpl::ReceivePostMessage(
|
||||
const std::string& channel,
|
||||
blink::TransferableMessage message) {
|
||||
auto* session = GetSession();
|
||||
if (session) {
|
||||
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
gin::Handle<gin_helper::internal::Event> event =
|
||||
MakeIPCEvent(isolate, false);
|
||||
session->ReceivePostMessage(event, channel, std::move(message));
|
||||
}
|
||||
}
|
||||
|
||||
void ElectronApiSWIPCHandlerImpl::MessageSync(bool internal,
|
||||
const std::string& channel,
|
||||
blink::CloneableMessage arguments,
|
||||
MessageSyncCallback callback) {
|
||||
auto* session = GetSession();
|
||||
if (session) {
|
||||
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
gin::Handle<gin_helper::internal::Event> event =
|
||||
MakeIPCEvent(isolate, internal);
|
||||
session->MessageSync(event, channel, std::move(arguments),
|
||||
std::move(callback));
|
||||
}
|
||||
}
|
||||
|
||||
void ElectronApiSWIPCHandlerImpl::MessageHost(
|
||||
const std::string& channel,
|
||||
blink::CloneableMessage arguments) {
|
||||
NOTIMPLEMENTED(); // Service workers have no <webview>
|
||||
}
|
||||
|
||||
ElectronBrowserContext* ElectronApiSWIPCHandlerImpl::GetBrowserContext() {
|
||||
auto* browser_context = static_cast<ElectronBrowserContext*>(
|
||||
render_process_host_->GetBrowserContext());
|
||||
return browser_context;
|
||||
}
|
||||
|
||||
api::Session* ElectronApiSWIPCHandlerImpl::GetSession() {
|
||||
return api::Session::FromBrowserContext(GetBrowserContext());
|
||||
}
|
||||
|
||||
gin::Handle<gin_helper::internal::Event>
|
||||
ElectronApiSWIPCHandlerImpl::MakeIPCEvent(v8::Isolate* isolate, bool internal) {
|
||||
gin::Handle<gin_helper::internal::Event> event =
|
||||
gin_helper::internal::Event::New(isolate);
|
||||
v8::Local<v8::Object> event_object = event.ToV8().As<v8::Object>();
|
||||
|
||||
gin_helper::Dictionary dict(isolate, event_object);
|
||||
dict.Set("type", "service-worker");
|
||||
dict.Set("versionId", version_id_);
|
||||
dict.Set("processId", render_process_host_->GetID());
|
||||
|
||||
// Set session to provide context for getting preloads
|
||||
dict.Set("session", GetSession());
|
||||
|
||||
if (internal)
|
||||
dict.SetHidden("internal", internal);
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
void ElectronApiSWIPCHandlerImpl::Destroy() {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
|
||||
auto* service_worker_ipc_list = ServiceWorkerIPCList::Get(
|
||||
render_process_host_, /*create_if_not_exists=*/false);
|
||||
CHECK(service_worker_ipc_list);
|
||||
// std::erase_if will lead to a call to the destructor for this object.
|
||||
std::erase_if(service_worker_ipc_list->list, base::MatchesUniquePtr(this));
|
||||
}
|
||||
|
||||
void ElectronApiSWIPCHandlerImpl::RenderProcessExited(
|
||||
content::RenderProcessHost* host,
|
||||
const content::ChildProcessTerminationInfo& info) {
|
||||
CHECK_EQ(host, render_process_host_);
|
||||
// TODO(crbug.com/1407197): Investigate clearing the user data from
|
||||
// RenderProcessHostImpl::Cleanup.
|
||||
Destroy();
|
||||
// This instance has now been deleted.
|
||||
}
|
||||
|
||||
// static
|
||||
void ElectronApiSWIPCHandlerImpl::BindReceiver(
|
||||
int render_process_id,
|
||||
int64_t version_id,
|
||||
mojo::PendingAssociatedReceiver<mojom::ElectronApiIPC> receiver) {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
auto* render_process_host =
|
||||
content::RenderProcessHost::FromID(render_process_id);
|
||||
if (!render_process_host) {
|
||||
return;
|
||||
}
|
||||
auto* service_worker_ipc_list = ServiceWorkerIPCList::Get(
|
||||
render_process_host, /*create_if_not_exists=*/true);
|
||||
service_worker_ipc_list->list.push_back(
|
||||
std::make_unique<ElectronApiSWIPCHandlerImpl>(
|
||||
render_process_host, version_id, std::move(receiver)));
|
||||
}
|
||||
|
||||
} // namespace electron
|
|
@ -0,0 +1,98 @@
|
|||
// Copyright (c) 2024 Samuel Maddock <sam@samuelmaddock.com>.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ELECTRON_SHELL_BROWSER_ELECTRON_API_SW_IPC_HANDLER_IMPL_H_
|
||||
#define ELECTRON_SHELL_BROWSER_ELECTRON_API_SW_IPC_HANDLER_IMPL_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/render_process_host_observer.h"
|
||||
#include "electron/shell/common/api/api.mojom.h"
|
||||
#include "gin/handle.h"
|
||||
#include "mojo/public/cpp/bindings/associated_receiver.h"
|
||||
#include "shell/common/gin_helper/event.h"
|
||||
|
||||
namespace content {
|
||||
class RenderProcessHost;
|
||||
}
|
||||
|
||||
namespace electron {
|
||||
class ElectronBrowserContext;
|
||||
|
||||
namespace api {
|
||||
class Session;
|
||||
}
|
||||
|
||||
class ElectronApiSWIPCHandlerImpl : public mojom::ElectronApiIPC,
|
||||
public content::RenderProcessHostObserver {
|
||||
public:
|
||||
explicit ElectronApiSWIPCHandlerImpl(
|
||||
content::RenderProcessHost* render_process_host,
|
||||
int64_t version_id,
|
||||
mojo::PendingAssociatedReceiver<mojom::ElectronApiIPC> receiver);
|
||||
|
||||
static void BindReceiver(
|
||||
int render_process_id,
|
||||
int64_t version_id,
|
||||
mojo::PendingAssociatedReceiver<mojom::ElectronApiIPC> receiver);
|
||||
|
||||
// disable copy
|
||||
ElectronApiSWIPCHandlerImpl(const ElectronApiSWIPCHandlerImpl&) = delete;
|
||||
ElectronApiSWIPCHandlerImpl& operator=(const ElectronApiSWIPCHandlerImpl&) =
|
||||
delete;
|
||||
~ElectronApiSWIPCHandlerImpl() override;
|
||||
|
||||
// mojom::ElectronApiIPC:
|
||||
void Message(bool internal,
|
||||
const std::string& channel,
|
||||
blink::CloneableMessage arguments) override;
|
||||
void Invoke(bool internal,
|
||||
const std::string& channel,
|
||||
blink::CloneableMessage arguments,
|
||||
InvokeCallback callback) override;
|
||||
void ReceivePostMessage(const std::string& channel,
|
||||
blink::TransferableMessage message) override;
|
||||
void MessageSync(bool internal,
|
||||
const std::string& channel,
|
||||
blink::CloneableMessage arguments,
|
||||
MessageSyncCallback callback) override;
|
||||
void MessageHost(const std::string& channel,
|
||||
blink::CloneableMessage arguments) override;
|
||||
|
||||
base::WeakPtr<ElectronApiSWIPCHandlerImpl> GetWeakPtr() {
|
||||
return weak_factory_.GetWeakPtr();
|
||||
}
|
||||
|
||||
private:
|
||||
ElectronBrowserContext* GetBrowserContext();
|
||||
api::Session* GetSession();
|
||||
|
||||
gin::Handle<gin_helper::internal::Event> MakeIPCEvent(v8::Isolate* isolate,
|
||||
bool internal);
|
||||
|
||||
// content::RenderProcessHostObserver
|
||||
void RenderProcessExited(
|
||||
content::RenderProcessHost* host,
|
||||
const content::ChildProcessTerminationInfo& info) override;
|
||||
|
||||
void RemoteDisconnected();
|
||||
|
||||
// Destroys this instance by removing it from the ServiceWorkerIPCList.
|
||||
void Destroy();
|
||||
|
||||
// This is safe because ElectronApiSWIPCHandlerImpl is tied to the life time
|
||||
// of RenderProcessHost.
|
||||
const raw_ptr<content::RenderProcessHost> render_process_host_;
|
||||
|
||||
// Service worker version ID.
|
||||
int64_t version_id_;
|
||||
|
||||
mojo::AssociatedReceiver<mojom::ElectronApiIPC> receiver_{this};
|
||||
|
||||
base::WeakPtrFactory<ElectronApiSWIPCHandlerImpl> weak_factory_{this};
|
||||
};
|
||||
} // namespace electron
|
||||
#endif // ELECTRON_SHELL_BROWSER_ELECTRON_API_SW_IPC_HANDLER_IMPL_H_
|
|
@ -78,6 +78,7 @@
|
|||
#include "shell/browser/bluetooth/electron_bluetooth_delegate.h"
|
||||
#include "shell/browser/child_web_contents_tracker.h"
|
||||
#include "shell/browser/electron_api_ipc_handler_impl.h"
|
||||
#include "shell/browser/electron_api_sw_ipc_handler_impl.h"
|
||||
#include "shell/browser/electron_autofill_driver_factory.h"
|
||||
#include "shell/browser/electron_browser_context.h"
|
||||
#include "shell/browser/electron_browser_main_parts.h"
|
||||
|
@ -1421,6 +1422,13 @@ void ElectronBrowserClient::OverrideURLLoaderFactoryParams(
|
|||
void ElectronBrowserClient::RegisterAssociatedInterfaceBindersForServiceWorker(
|
||||
const content::ServiceWorkerVersionBaseInfo& service_worker_version_info,
|
||||
blink::AssociatedInterfaceRegistry& associated_registry) {
|
||||
CHECK(service_worker_version_info.process_id !=
|
||||
content::ChildProcessHost::kInvalidUniqueID);
|
||||
associated_registry.AddInterface<mojom::ElectronApiIPC>(
|
||||
base::BindRepeating(&ElectronApiSWIPCHandlerImpl::BindReceiver,
|
||||
service_worker_version_info.process_id,
|
||||
service_worker_version_info.version_id));
|
||||
|
||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||
associated_registry.AddInterface<extensions::mojom::RendererHost>(
|
||||
base::BindRepeating(&extensions::RendererStartupHelper::BindForRenderer,
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
// Copyright (c) 2023 Salesforce, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "shell/common/gin_helper/reply_channel.h"
|
||||
|
||||
#include "base/debug/stack_trace.h"
|
||||
#include "gin/data_object_builder.h"
|
||||
#include "gin/handle.h"
|
||||
#include "gin/object_template_builder.h"
|
||||
#include "shell/browser/javascript_environment.h"
|
||||
#include "shell/common/gin_converters/blink_converter.h"
|
||||
|
||||
namespace gin_helper::internal {
|
||||
|
||||
// static
|
||||
using InvokeCallback = electron::mojom::ElectronApiIPC::InvokeCallback;
|
||||
gin::Handle<ReplyChannel> ReplyChannel::Create(v8::Isolate* isolate,
|
||||
InvokeCallback callback) {
|
||||
return gin::CreateHandle(isolate, new ReplyChannel(std::move(callback)));
|
||||
}
|
||||
|
||||
gin::ObjectTemplateBuilder ReplyChannel::GetObjectTemplateBuilder(
|
||||
v8::Isolate* isolate) {
|
||||
return gin::Wrappable<ReplyChannel>::GetObjectTemplateBuilder(isolate)
|
||||
.SetMethod("sendReply", &ReplyChannel::SendReply);
|
||||
}
|
||||
|
||||
const char* ReplyChannel::GetTypeName() {
|
||||
return "ReplyChannel";
|
||||
}
|
||||
|
||||
ReplyChannel::ReplyChannel(InvokeCallback callback)
|
||||
: callback_(std::move(callback)) {}
|
||||
|
||||
ReplyChannel::~ReplyChannel() {
|
||||
if (callback_)
|
||||
SendError("reply was never sent");
|
||||
}
|
||||
|
||||
void ReplyChannel::SendError(const std::string& msg) {
|
||||
v8::Isolate* isolate = electron::JavascriptEnvironment::GetIsolate();
|
||||
// If there's no current context, it means we're shutting down, so we
|
||||
// don't need to send an event.
|
||||
if (!isolate->GetCurrentContext().IsEmpty()) {
|
||||
v8::HandleScope scope(isolate);
|
||||
auto message = gin::DataObjectBuilder(isolate).Set("error", msg).Build();
|
||||
SendReply(isolate, message);
|
||||
}
|
||||
}
|
||||
|
||||
bool ReplyChannel::SendReply(v8::Isolate* isolate, v8::Local<v8::Value> arg) {
|
||||
if (!callback_)
|
||||
return false;
|
||||
blink::CloneableMessage message;
|
||||
if (!gin::ConvertFromV8(isolate, arg, &message)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::move(callback_).Run(std::move(message));
|
||||
return true;
|
||||
}
|
||||
|
||||
gin::WrapperInfo ReplyChannel::kWrapperInfo = {gin::kEmbedderNativeGin};
|
||||
|
||||
} // namespace gin_helper::internal
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright (c) 2023 Salesforce, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ELECTRON_SHELL_COMMON_GIN_HELPER_REPLY_CHANNEL_H_
|
||||
#define ELECTRON_SHELL_COMMON_GIN_HELPER_REPLY_CHANNEL_H_
|
||||
|
||||
#include "gin/wrappable.h"
|
||||
#include "shell/common/api/api.mojom.h"
|
||||
|
||||
namespace gin {
|
||||
template <typename T>
|
||||
class Handle;
|
||||
} // namespace gin
|
||||
|
||||
namespace v8 {
|
||||
class Isolate;
|
||||
template <typename T>
|
||||
class Local;
|
||||
class Object;
|
||||
class ObjectTemplate;
|
||||
} // namespace v8
|
||||
|
||||
namespace gin_helper::internal {
|
||||
|
||||
// This object wraps the InvokeCallback so that if it gets GC'd by V8, we can
|
||||
// still call the callback and send an error. Not doing so causes a Mojo DCHECK,
|
||||
// since Mojo requires callbacks to be called before they are destroyed.
|
||||
class ReplyChannel : public gin::Wrappable<ReplyChannel> {
|
||||
public:
|
||||
using InvokeCallback = electron::mojom::ElectronApiIPC::InvokeCallback;
|
||||
static gin::Handle<ReplyChannel> Create(v8::Isolate* isolate,
|
||||
InvokeCallback callback);
|
||||
|
||||
// gin::Wrappable
|
||||
static gin::WrapperInfo kWrapperInfo;
|
||||
gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
|
||||
v8::Isolate* isolate) override;
|
||||
const char* GetTypeName() override;
|
||||
|
||||
void SendError(const std::string& msg);
|
||||
|
||||
private:
|
||||
explicit ReplyChannel(InvokeCallback callback);
|
||||
~ReplyChannel() override;
|
||||
|
||||
bool SendReply(v8::Isolate* isolate, v8::Local<v8::Value> arg);
|
||||
|
||||
InvokeCallback callback_;
|
||||
};
|
||||
|
||||
} // namespace gin_helper::internal
|
||||
|
||||
#endif // ELECTRON_SHELL_COMMON_GIN_HELPER_REPLY_CHANNEL_H_
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "content/public/renderer/render_frame.h"
|
||||
#include "content/public/renderer/render_frame_observer.h"
|
||||
#include "content/public/renderer/worker_thread.h"
|
||||
#include "gin/dictionary.h"
|
||||
#include "gin/handle.h"
|
||||
#include "gin/object_template_builder.h"
|
||||
|
@ -20,9 +21,13 @@
|
|||
#include "shell/common/node_bindings.h"
|
||||
#include "shell/common/node_includes.h"
|
||||
#include "shell/common/v8_util.h"
|
||||
#include "shell/renderer/preload_realm_context.h"
|
||||
#include "shell/renderer/service_worker_data.h"
|
||||
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
|
||||
#include "third_party/blink/public/web/modules/service_worker/web_service_worker_context_proxy.h"
|
||||
#include "third_party/blink/public/web/web_local_frame.h"
|
||||
#include "third_party/blink/public/web/web_message_port_converter.h"
|
||||
#include "third_party/blink/renderer/core/execution_context/execution_context.h" // nogncheck
|
||||
|
||||
using blink::WebLocalFrame;
|
||||
using content::RenderFrame;
|
||||
|
@ -40,7 +45,16 @@ RenderFrame* GetCurrentRenderFrame() {
|
|||
return RenderFrame::FromWebFrame(frame);
|
||||
}
|
||||
|
||||
// Thread identifier for the main renderer thread (as opposed to a service
|
||||
// worker thread).
|
||||
inline constexpr int kMainThreadId = 0;
|
||||
|
||||
bool IsWorkerThread() {
|
||||
return content::WorkerThread::GetCurrentId() != kMainThreadId;
|
||||
}
|
||||
|
||||
class IPCRenderer final : public gin::Wrappable<IPCRenderer>,
|
||||
public content::WorkerThread::Observer,
|
||||
private content::RenderFrameObserver {
|
||||
public:
|
||||
static gin::WrapperInfo kWrapperInfo;
|
||||
|
@ -51,14 +65,31 @@ class IPCRenderer final : public gin::Wrappable<IPCRenderer>,
|
|||
|
||||
explicit IPCRenderer(v8::Isolate* isolate)
|
||||
: content::RenderFrameObserver(GetCurrentRenderFrame()) {
|
||||
RenderFrame* render_frame = GetCurrentRenderFrame();
|
||||
DCHECK(render_frame);
|
||||
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
||||
blink::ExecutionContext* execution_context =
|
||||
blink::ExecutionContext::From(context);
|
||||
|
||||
if (execution_context->IsWindow()) {
|
||||
RenderFrame* render_frame = GetCurrentRenderFrame();
|
||||
DCHECK(render_frame);
|
||||
render_frame->GetRemoteAssociatedInterfaces()->GetInterface(
|
||||
&electron_ipc_remote_);
|
||||
} else if (execution_context->IsShadowRealmGlobalScope()) {
|
||||
DCHECK(IsWorkerThread());
|
||||
content::WorkerThread::AddObserver(this);
|
||||
|
||||
electron::ServiceWorkerData* service_worker_data =
|
||||
electron::preload_realm::GetServiceWorkerData(context);
|
||||
DCHECK(service_worker_data);
|
||||
service_worker_data->proxy()->GetRemoteAssociatedInterface(
|
||||
electron_ipc_remote_.BindNewEndpointAndPassReceiver());
|
||||
} else {
|
||||
NOTREACHED();
|
||||
}
|
||||
|
||||
weak_context_ =
|
||||
v8::Global<v8::Context>(isolate, isolate->GetCurrentContext());
|
||||
weak_context_.SetWeak();
|
||||
|
||||
render_frame->GetRemoteAssociatedInterfaces()->GetInterface(
|
||||
&electron_ipc_remote_);
|
||||
}
|
||||
|
||||
void OnDestruct() override { electron_ipc_remote_.reset(); }
|
||||
|
@ -66,10 +97,13 @@ class IPCRenderer final : public gin::Wrappable<IPCRenderer>,
|
|||
void WillReleaseScriptContext(v8::Local<v8::Context> context,
|
||||
int32_t world_id) override {
|
||||
if (weak_context_.IsEmpty() ||
|
||||
weak_context_.Get(context->GetIsolate()) == context)
|
||||
electron_ipc_remote_.reset();
|
||||
weak_context_.Get(context->GetIsolate()) == context) {
|
||||
OnDestruct();
|
||||
}
|
||||
}
|
||||
|
||||
void WillStopCurrentWorkerThread() override { OnDestruct(); }
|
||||
|
||||
// gin::Wrappable:
|
||||
gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
|
||||
v8::Isolate* isolate) override {
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "shell/common/options_switches.h"
|
||||
#include "shell/common/thread_restrictions.h"
|
||||
#include "shell/common/v8_util.h"
|
||||
#include "shell/renderer/electron_ipc_native.h"
|
||||
#include "shell/renderer/electron_render_frame_observer.h"
|
||||
#include "shell/renderer/renderer_client_base.h"
|
||||
#include "third_party/blink/public/mojom/frame/user_activation_notification_type.mojom-shared.h"
|
||||
|
@ -30,73 +31,6 @@
|
|||
|
||||
namespace electron {
|
||||
|
||||
namespace {
|
||||
|
||||
const char kIpcKey[] = "ipcNative";
|
||||
|
||||
// Gets the private object under kIpcKey
|
||||
v8::Local<v8::Object> GetIpcObject(v8::Local<v8::Context> context) {
|
||||
auto* isolate = context->GetIsolate();
|
||||
auto binding_key = gin::StringToV8(isolate, kIpcKey);
|
||||
auto private_binding_key = v8::Private::ForApi(isolate, binding_key);
|
||||
auto global_object = context->Global();
|
||||
auto value =
|
||||
global_object->GetPrivate(context, private_binding_key).ToLocalChecked();
|
||||
if (value.IsEmpty() || !value->IsObject()) {
|
||||
LOG(ERROR) << "Attempted to get the 'ipcNative' object but it was missing";
|
||||
return v8::Local<v8::Object>();
|
||||
}
|
||||
return value->ToObject(context).ToLocalChecked();
|
||||
}
|
||||
|
||||
void InvokeIpcCallback(v8::Local<v8::Context> context,
|
||||
const std::string& callback_name,
|
||||
std::vector<v8::Local<v8::Value>> args) {
|
||||
TRACE_EVENT0("devtools.timeline", "FunctionCall");
|
||||
auto* isolate = context->GetIsolate();
|
||||
|
||||
auto ipcNative = GetIpcObject(context);
|
||||
if (ipcNative.IsEmpty())
|
||||
return;
|
||||
|
||||
// Only set up the node::CallbackScope if there's a node environment.
|
||||
// Sandboxed renderers don't have a node environment.
|
||||
std::unique_ptr<node::CallbackScope> callback_scope;
|
||||
if (node::Environment::GetCurrent(context)) {
|
||||
callback_scope = std::make_unique<node::CallbackScope>(
|
||||
isolate, ipcNative, node::async_context{0, 0});
|
||||
}
|
||||
|
||||
auto callback_key = gin::ConvertToV8(isolate, callback_name)
|
||||
->ToString(context)
|
||||
.ToLocalChecked();
|
||||
auto callback_value = ipcNative->Get(context, callback_key).ToLocalChecked();
|
||||
DCHECK(callback_value->IsFunction()); // set by init.ts
|
||||
auto callback = callback_value.As<v8::Function>();
|
||||
std::ignore = callback->Call(context, ipcNative, args.size(), args.data());
|
||||
}
|
||||
|
||||
void EmitIPCEvent(v8::Local<v8::Context> context,
|
||||
bool internal,
|
||||
const std::string& channel,
|
||||
std::vector<v8::Local<v8::Value>> ports,
|
||||
v8::Local<v8::Value> args) {
|
||||
auto* isolate = context->GetIsolate();
|
||||
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
v8::Context::Scope context_scope(context);
|
||||
v8::MicrotasksScope script_scope(isolate, context->GetMicrotaskQueue(),
|
||||
v8::MicrotasksScope::kRunMicrotasks);
|
||||
|
||||
std::vector<v8::Local<v8::Value>> argv = {
|
||||
gin::ConvertToV8(isolate, internal), gin::ConvertToV8(isolate, channel),
|
||||
gin::ConvertToV8(isolate, ports), args};
|
||||
|
||||
InvokeIpcCallback(context, "onMessage", argv);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ElectronApiServiceImpl::~ElectronApiServiceImpl() = default;
|
||||
|
||||
ElectronApiServiceImpl::ElectronApiServiceImpl(
|
||||
|
@ -165,7 +99,7 @@ void ElectronApiServiceImpl::Message(bool internal,
|
|||
|
||||
v8::Local<v8::Value> args = gin::ConvertToV8(isolate, arguments);
|
||||
|
||||
EmitIPCEvent(context, internal, channel, {}, args);
|
||||
ipc_native::EmitIPCEvent(context, internal, channel, {}, args);
|
||||
}
|
||||
|
||||
void ElectronApiServiceImpl::ReceivePostMessage(
|
||||
|
@ -192,7 +126,8 @@ void ElectronApiServiceImpl::ReceivePostMessage(
|
|||
|
||||
std::vector<v8::Local<v8::Value>> args = {message_value};
|
||||
|
||||
EmitIPCEvent(context, false, channel, ports, gin::ConvertToV8(isolate, args));
|
||||
ipc_native::EmitIPCEvent(context, false, channel, ports,
|
||||
gin::ConvertToV8(isolate, args));
|
||||
}
|
||||
|
||||
void ElectronApiServiceImpl::TakeHeapSnapshot(
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
// Copyright (c) 2019 Slack Technologies, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "electron/shell/renderer/electron_ipc_native.h"
|
||||
|
||||
#include "base/trace_event/trace_event.h"
|
||||
#include "shell/common/gin_converters/blink_converter.h"
|
||||
#include "shell/common/gin_converters/value_converter.h"
|
||||
#include "shell/common/node_includes.h"
|
||||
#include "shell/common/v8_util.h"
|
||||
#include "third_party/blink/public/web/blink.h"
|
||||
#include "third_party/blink/public/web/web_message_port_converter.h"
|
||||
|
||||
namespace electron::ipc_native {
|
||||
|
||||
namespace {
|
||||
|
||||
const char kIpcKey[] = "ipcNative";
|
||||
|
||||
// Gets the private object under kIpcKey
|
||||
v8::Local<v8::Object> GetIpcObject(const v8::Local<v8::Context>& context) {
|
||||
auto* isolate = context->GetIsolate();
|
||||
auto binding_key = gin::StringToV8(isolate, kIpcKey);
|
||||
auto private_binding_key = v8::Private::ForApi(isolate, binding_key);
|
||||
auto global_object = context->Global();
|
||||
auto value =
|
||||
global_object->GetPrivate(context, private_binding_key).ToLocalChecked();
|
||||
if (value.IsEmpty() || !value->IsObject()) {
|
||||
LOG(ERROR) << "Attempted to get the 'ipcNative' object but it was missing";
|
||||
return v8::Local<v8::Object>();
|
||||
}
|
||||
return value->ToObject(context).ToLocalChecked();
|
||||
}
|
||||
|
||||
void InvokeIpcCallback(const v8::Local<v8::Context>& context,
|
||||
const std::string& callback_name,
|
||||
std::vector<v8::Local<v8::Value>> args) {
|
||||
TRACE_EVENT0("devtools.timeline", "FunctionCall");
|
||||
auto* isolate = context->GetIsolate();
|
||||
|
||||
auto ipcNative = GetIpcObject(context);
|
||||
if (ipcNative.IsEmpty())
|
||||
return;
|
||||
|
||||
// Only set up the node::CallbackScope if there's a node environment.
|
||||
// Sandboxed renderers don't have a node environment.
|
||||
std::unique_ptr<node::CallbackScope> callback_scope;
|
||||
if (node::Environment::GetCurrent(context)) {
|
||||
callback_scope = std::make_unique<node::CallbackScope>(
|
||||
isolate, ipcNative, node::async_context{0, 0});
|
||||
}
|
||||
|
||||
auto callback_key = gin::ConvertToV8(isolate, callback_name)
|
||||
->ToString(context)
|
||||
.ToLocalChecked();
|
||||
auto callback_value = ipcNative->Get(context, callback_key).ToLocalChecked();
|
||||
DCHECK(callback_value->IsFunction()); // set by init.ts
|
||||
auto callback = callback_value.As<v8::Function>();
|
||||
std::ignore = callback->Call(context, ipcNative, args.size(), args.data());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void EmitIPCEvent(const v8::Local<v8::Context>& context,
|
||||
bool internal,
|
||||
const std::string& channel,
|
||||
std::vector<v8::Local<v8::Value>> ports,
|
||||
v8::Local<v8::Value> args) {
|
||||
auto* isolate = context->GetIsolate();
|
||||
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
v8::Context::Scope context_scope(context);
|
||||
v8::MicrotasksScope script_scope(isolate, context->GetMicrotaskQueue(),
|
||||
v8::MicrotasksScope::kRunMicrotasks);
|
||||
|
||||
std::vector<v8::Local<v8::Value>> argv = {
|
||||
gin::ConvertToV8(isolate, internal), gin::ConvertToV8(isolate, channel),
|
||||
gin::ConvertToV8(isolate, ports), args};
|
||||
|
||||
InvokeIpcCallback(context, "onMessage", argv);
|
||||
}
|
||||
|
||||
} // namespace electron::ipc_native
|
|
@ -0,0 +1,22 @@
|
|||
// Copyright (c) 2019 Slack Technologies, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ELECTRON_SHELL_RENDERER_ELECTRON_IPC_NATIVE_H_
|
||||
#define ELECTRON_SHELL_RENDERER_ELECTRON_IPC_NATIVE_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "v8/include/v8-forward.h"
|
||||
|
||||
namespace electron::ipc_native {
|
||||
|
||||
void EmitIPCEvent(const v8::Local<v8::Context>& context,
|
||||
bool internal,
|
||||
const std::string& channel,
|
||||
std::vector<v8::Local<v8::Value>> ports,
|
||||
v8::Local<v8::Value> args);
|
||||
|
||||
} // namespace electron::ipc_native
|
||||
|
||||
#endif // ELECTRON_SHELL_RENDERER_ELECTRON_IPC_NATIVE_H_
|
|
@ -23,6 +23,7 @@
|
|||
#include "shell/renderer/electron_render_frame_observer.h"
|
||||
#include "shell/renderer/preload_realm_context.h"
|
||||
#include "shell/renderer/preload_utils.h"
|
||||
#include "shell/renderer/service_worker_data.h"
|
||||
#include "third_party/blink/public/common/web_preferences/web_preferences.h"
|
||||
#include "third_party/blink/public/platform/scheduler/web_agent_group_scheduler.h"
|
||||
#include "third_party/blink/public/web/blink.h"
|
||||
|
@ -33,6 +34,9 @@ namespace electron {
|
|||
|
||||
namespace {
|
||||
|
||||
// Data which only lives on the service worker's thread
|
||||
constinit thread_local ServiceWorkerData* service_worker_data = nullptr;
|
||||
|
||||
const char kEmitProcessEventKey[] = "emit-process-event";
|
||||
|
||||
void InvokeEmitProcessEvent(v8::Local<v8::Context> context,
|
||||
|
@ -184,6 +188,11 @@ void ElectronSandboxedRendererClient::WillEvaluateServiceWorkerOnWorkerThread(
|
|||
|
||||
auto* command_line = base::CommandLine::ForCurrentProcess();
|
||||
if (command_line->HasSwitch(switches::kServiceWorkerPreload)) {
|
||||
if (!service_worker_data) {
|
||||
service_worker_data = new ServiceWorkerData(
|
||||
context_proxy, service_worker_version_id, v8_context);
|
||||
}
|
||||
|
||||
preload_realm::OnCreatePreloadableV8Context(v8_context,
|
||||
service_worker_data);
|
||||
}
|
||||
|
@ -195,6 +204,13 @@ void ElectronSandboxedRendererClient::
|
|||
int64_t service_worker_version_id,
|
||||
const GURL& service_worker_scope,
|
||||
const GURL& script_url) {
|
||||
if (service_worker_data) {
|
||||
DCHECK_EQ(service_worker_version_id,
|
||||
service_worker_data->service_worker_version_id());
|
||||
delete service_worker_data;
|
||||
service_worker_data = nullptr;
|
||||
}
|
||||
|
||||
RendererClientBase::WillDestroyServiceWorkerContextOnWorkerThread(
|
||||
context, service_worker_version_id, service_worker_scope, script_url);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
// Copyright (c) 2024 Samuel Maddock <sam@samuelmaddock.com>.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "electron/shell/renderer/service_worker_data.h"
|
||||
|
||||
#include "shell/common/gin_converters/blink_converter.h"
|
||||
#include "shell/common/gin_converters/value_converter.h"
|
||||
#include "shell/common/heap_snapshot.h"
|
||||
#include "shell/renderer/electron_ipc_native.h"
|
||||
#include "shell/renderer/preload_realm_context.h"
|
||||
#include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
ServiceWorkerData::~ServiceWorkerData() = default;
|
||||
|
||||
ServiceWorkerData::ServiceWorkerData(blink::WebServiceWorkerContextProxy* proxy,
|
||||
int64_t service_worker_version_id,
|
||||
const v8::Local<v8::Context>& v8_context)
|
||||
: proxy_(proxy),
|
||||
service_worker_version_id_(service_worker_version_id),
|
||||
isolate_(v8_context->GetIsolate()),
|
||||
v8_context_(v8_context->GetIsolate(), v8_context) {
|
||||
proxy_->GetAssociatedInterfaceRegistry()
|
||||
.AddInterface<mojom::ElectronRenderer>(
|
||||
base::BindRepeating(&ServiceWorkerData::OnElectronRendererRequest,
|
||||
weak_ptr_factory_.GetWeakPtr()));
|
||||
}
|
||||
|
||||
void ServiceWorkerData::OnElectronRendererRequest(
|
||||
mojo::PendingAssociatedReceiver<mojom::ElectronRenderer> receiver) {
|
||||
receiver_.reset();
|
||||
receiver_.Bind(std::move(receiver));
|
||||
}
|
||||
|
||||
void ServiceWorkerData::Message(bool internal,
|
||||
const std::string& channel,
|
||||
blink::CloneableMessage arguments) {
|
||||
v8::Isolate* isolate = isolate_.get();
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
|
||||
v8::Local<v8::Context> context = v8_context_.Get(isolate_);
|
||||
|
||||
v8::MaybeLocal<v8::Context> maybe_preload_context =
|
||||
preload_realm::GetPreloadRealmContext(context);
|
||||
|
||||
if (maybe_preload_context.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
v8::Local<v8::Context> preload_context =
|
||||
maybe_preload_context.ToLocalChecked();
|
||||
v8::Context::Scope context_scope(preload_context);
|
||||
|
||||
v8::Local<v8::Value> args = gin::ConvertToV8(isolate, arguments);
|
||||
|
||||
ipc_native::EmitIPCEvent(preload_context, internal, channel, {}, args);
|
||||
}
|
||||
|
||||
void ServiceWorkerData::ReceivePostMessage(const std::string& channel,
|
||||
blink::TransferableMessage message) {
|
||||
NOTIMPLEMENTED();
|
||||
}
|
||||
|
||||
void ServiceWorkerData::TakeHeapSnapshot(mojo::ScopedHandle file,
|
||||
TakeHeapSnapshotCallback callback) {
|
||||
NOTIMPLEMENTED();
|
||||
std::move(callback).Run(false);
|
||||
}
|
||||
|
||||
} // namespace electron
|
|
@ -0,0 +1,68 @@
|
|||
// Copyright (c) 2024 Samuel Maddock <sam@samuelmaddock.com>.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ELECTRON_SHELL_RENDERER_SERVICE_WORKER_DATA_H_
|
||||
#define ELECTRON_SHELL_RENDERER_SERVICE_WORKER_DATA_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "electron/shell/common/api/api.mojom.h"
|
||||
#include "mojo/public/cpp/bindings/associated_receiver.h"
|
||||
#include "mojo/public/cpp/bindings/associated_remote.h"
|
||||
#include "mojo/public/cpp/bindings/pending_receiver.h"
|
||||
#include "mojo/public/cpp/bindings/receiver.h"
|
||||
#include "third_party/blink/public/web/modules/service_worker/web_service_worker_context_proxy.h"
|
||||
#include "v8/include/v8-context.h"
|
||||
#include "v8/include/v8-forward.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
// Per ServiceWorker data in worker thread.
|
||||
class ServiceWorkerData : public mojom::ElectronRenderer {
|
||||
public:
|
||||
ServiceWorkerData(blink::WebServiceWorkerContextProxy* proxy,
|
||||
int64_t service_worker_version_id,
|
||||
const v8::Local<v8::Context>& v8_context);
|
||||
~ServiceWorkerData() override;
|
||||
|
||||
// disable copy
|
||||
ServiceWorkerData(const ServiceWorkerData&) = delete;
|
||||
ServiceWorkerData& operator=(const ServiceWorkerData&) = delete;
|
||||
|
||||
int64_t service_worker_version_id() const {
|
||||
return service_worker_version_id_;
|
||||
}
|
||||
|
||||
blink::WebServiceWorkerContextProxy* proxy() const { return proxy_; }
|
||||
|
||||
// mojom::ElectronRenderer
|
||||
void Message(bool internal,
|
||||
const std::string& channel,
|
||||
blink::CloneableMessage arguments) override;
|
||||
void ReceivePostMessage(const std::string& channel,
|
||||
blink::TransferableMessage message) override;
|
||||
void TakeHeapSnapshot(mojo::ScopedHandle file,
|
||||
TakeHeapSnapshotCallback callback) override;
|
||||
|
||||
private:
|
||||
void OnElectronRendererRequest(
|
||||
mojo::PendingAssociatedReceiver<mojom::ElectronRenderer> receiver);
|
||||
|
||||
raw_ptr<blink::WebServiceWorkerContextProxy> proxy_;
|
||||
const int64_t service_worker_version_id_;
|
||||
|
||||
// The v8 context the bindings are accessible to.
|
||||
raw_ptr<v8::Isolate> isolate_;
|
||||
v8::Global<v8::Context> v8_context_;
|
||||
|
||||
mojo::AssociatedReceiver<mojom::ElectronRenderer> receiver_{this};
|
||||
|
||||
base::WeakPtrFactory<ServiceWorkerData> weak_ptr_factory_{this};
|
||||
};
|
||||
|
||||
} // namespace electron
|
||||
|
||||
#endif // ELECTRON_SHELL_RENDERER_SERVICE_WORKER_DATA_H_
|
|
@ -79,6 +79,9 @@ declare namespace Electron {
|
|||
_stopWorker(): void;
|
||||
}
|
||||
|
||||
interface Session {
|
||||
_init(): void;
|
||||
}
|
||||
|
||||
interface TouchBar {
|
||||
_removeFromWindow: (win: BaseWindow) => void;
|
||||
|
@ -198,6 +201,14 @@ declare namespace Electron {
|
|||
frameTreeNodeId?: number;
|
||||
}
|
||||
|
||||
interface IpcMainServiceWorkerEvent {
|
||||
_replyChannel: ReplyChannel;
|
||||
}
|
||||
|
||||
interface IpcMainServiceWorkerInvokeEvent {
|
||||
_replyChannel: ReplyChannel;
|
||||
}
|
||||
|
||||
// Deprecated / undocumented BrowserWindow methods
|
||||
interface BrowserWindow {
|
||||
getURL(): string;
|
||||
|
@ -273,11 +284,11 @@ declare namespace ElectronInternal {
|
|||
invoke<T>(channel: string, ...args: any[]): Promise<T>;
|
||||
}
|
||||
|
||||
interface IpcMainInternalEvent extends Omit<Electron.IpcMainEvent, 'reply'> {
|
||||
}
|
||||
type IpcMainInternalEvent = Omit<Electron.IpcMainEvent, 'reply'> | Omit<Electron.IpcMainServiceWorkerEvent, 'reply'>;
|
||||
type IpcMainInternalInvokeEvent = Electron.IpcMainInvokeEvent | Electron.IpcMainServiceWorkerInvokeEvent;
|
||||
|
||||
interface IpcMainInternal extends NodeJS.EventEmitter {
|
||||
handle(channel: string, listener: (event: Electron.IpcMainInvokeEvent, ...args: any[]) => Promise<any> | any): void;
|
||||
handle(channel: string, listener: (event: IpcMainInternalInvokeEvent, ...args: any[]) => Promise<any> | any): void;
|
||||
on(channel: string, listener: (event: IpcMainInternalEvent, ...args: any[]) => void): this;
|
||||
once(channel: string, listener: (event: IpcMainInternalEvent, ...args: any[]) => void): this;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче