mixone
Version:
A cross-platform desktop application framework that enables development using JavaScript, HTML5, Electron API and Node.js. Build Windows, macOS and Linux apps with Vue/React and system APIs - that's why we call it mixone.A powerful Electron development in
487 lines (470 loc) • 20.6 kB
JavaScript
// preload.js
const { contextBridge, ipcRenderer } = require('electron');
const windowController = require('./window-controller');
// 监听主进程日志
ipcRenderer.on('main-process-log', (event, { type, args }) => {
try {
// 确保 type 是有效的控制台方法
const validTypes = ['log', 'error', 'warn', 'info', 'debug'];
type = validTypes.includes(type) ? type : 'log';
// 确保 args 是数组且可以安全序列化
args = Array.isArray(args) ? args.map(arg => {
if (typeof arg === 'object' && arg !== null) {
try {
return JSON.parse(JSON.stringify(arg));
} catch (e) {
return String(arg);
}
}
return arg;
}) : [String(args)];
console[type](...args);
} catch (error) {
console.error('处理主进程日志失败:', error);
}
});
const _winId = Number(process.argv.find(arg => arg.startsWith('--window-id=')).split('=')[1]);
windowController.setWinId(_winId);
// 监听指定窗口的事件
ipcRenderer.on(`windowEvent:${_winId}`, (event, { eventName, data }) => {
const listeners = windowEventListeners.get(eventName);
if (listeners) {
listeners.forEach(listener => listener(data));
}
});
// 监听广播事件
ipcRenderer.on('broadcast', (event, { channel, data }) => {
const listeners = windowEventListeners.get(channel);
if (listeners) {
listeners.forEach(listener => listener(data));
}
});
contextBridge.exposeInMainWorld('getWinId', () => {
return _winId;
});
contextBridge.exposeInMainWorld('winId', _winId);
// 存储事件监听器
const windowEventListeners = new Map();
const windowObjEventListeners = new Map();
// 生成事件键
function generateEventKey(eventName, winId) {
return `${eventName}___${winId}`;
}
// 监听从主进程转发来的窗口事件
ipcRenderer.on('broadcast-window-event', (event, { eventName, args, winId }) => {
// 只处理属于当前窗口的事件
const eventKey = generateEventKey(eventName, winId);
const listeners = windowEventListeners.get(eventKey);
if (listeners) {
listeners.forEach(listener => listener(...args));
}
});
// 监听从主进程转发来的窗口事件
ipcRenderer.on('broadcast-window-obj-event', (event, { eventName, args, winId }) => {
// 只处理属于当前窗口的事件
const eventKey = generateEventKey(eventName, winId);
const listeners = windowObjEventListeners.get(eventKey);
if (listeners) {
listeners.forEach(listener => listener(...args));
}
});
// 跟踪已注册的事件监听器
const registeredCallbacks = new Map();
// 暴露安全的 API 给渲染进程
contextBridge.exposeInMainWorld('electron', {
// 调用主进程函数的方法
callMainFunction: async (id, params) => {
try {
const response = await ipcRenderer.invoke('call-main-fn', { id, args: params });
if (!response.success) {
throw new Error(response.error);
}
return response.result;
} catch (error) {
throw error;
}
},
// 注册回调函数的方法
registerCallback: (callbackId, callback) => {
// 检查是否已经注册过这个回调
if (registeredCallbacks.has(callbackId)) {
// 如果已注册,先移除旧的监听器
const oldListener = registeredCallbacks.get(callbackId);
ipcRenderer.removeListener('callback:' + callbackId, oldListener);
}
// 创建新的监听器函数
const listener = (event, ...args) => {
callback(...args);
};
// 注册新的监听器
ipcRenderer.on('callback:' + callbackId, listener);
// 保存监听器引用
registeredCallbacks.set(callbackId, listener);
}
});
contextBridge.exposeInMainWorld('windowManager', {
openWindow: async (...args) => {
let response = await ipcRenderer.invoke('window-manager-action', {
method: '_openWindow',
args
});
const windowProperties = [
// 'webContents',
'id', 'tabbingIdentifier', 'autoHideMenuBar', 'simpleFullScreen', 'fullScreen',
'focusable',
'visibleOnAllWorkspaces', 'shadow',
'menuBarVisible', 'kiosk',
'documentEdited', 'representedFilename', 'title', 'minimizable',
'maximizable', 'fullScreenable', 'resizable', 'closable', 'movable',
'excludedFromShownWindowsMenu', 'accessibleTitle'
];
const windowMethods = ["destroy", "close", "focus", "blur", "isFocused", "isDestroyed", "show", "showInactive", "hide", "isVisible", "isModal", "maximize", "unmaximize",
"isMaximized", "minimize", "restore", "isMinimized", "setFullScreen", "isFullScreen", "setSimpleFullScreen", "isSimpleFullScreen", "isNormal", "setAspectRatio",
"setBackgroundColor", "previewFile", "closeFilePreview", "setBounds", "getBounds", "getBackgroundColor", "setContentBounds", "getContentBounds", "getNormalBounds",
"setEnabled", "isEnabled", "setSize", "getSize", "setContentSize", "getContentSize", "setMinimumSize", "getMinimumSize", "setMaximumSize", "getMaximumSize",
"setResizable", "isResizable", "setMovable", "isMovable", "setMinimizable", "isMinimizable", "setMaximizable", "isMaximizable", "setFullScreenable",
"isFullScreenable", "setClosable", "isClosable", "setHiddenInMissionControl", "isHiddenInMissionControl", "setAlwaysOnTop", "isAlwaysOnTop", "moveAbove", "moveTop",
"center", "setPosition", "getPosition", "setTitle", "getTitle", "setSheetOffset", "flashFrame", "setSkipTaskbar", "setKiosk", "isKiosk", "isTabletMode",
"getMediaSourceId", "getNativeWindowHandle", "hookWindowMessage", "isWindowMessageHooked", "unhookWindowMessage", "unhookAllWindowMessages",
"setRepresentedFilename", "getRepresentedFilename", "setDocumentEdited", "isDocumentEdited", "focusOnWebView", "blurWebView", "capturePage", "loadURL", "loadFile",
"reload", "setMenu", "removeMenu", "setProgressBar", "setOverlayIcon", "invalidateShadow", "setHasShadow", "hasShadow", "setOpacity", "getOpacity", "setShape",
"setThumbarButtons", "setThumbnailClip", "setThumbnailToolTip", "setAppDetails", "showDefinitionForSelection", "setIcon", "setWindowButtonVisibility",
"setAutoHideMenuBar", "isMenuBarAutoHide", "setMenuBarVisibility", "isMenuBarVisible", "setVisibleOnAllWorkspaces", "isVisibleOnAllWorkspaces",
"setIgnoreMouseEvents", "setContentProtection", "setFocusable", "isFocusable", "setParentWindow", "getParentWindow", "getChildWindows", "setAutoHideCursor",
"selectPreviousTab", "selectNextTab", "showAllTabs", "mergeAllWindows", "moveTabToNewWindow", "toggleTabBar", "addTabbedWindow", "setVibrancy",
"setBackgroundMaterial", "setWindowButtonPosition", "getWindowButtonPosition", "setTouchBar", "setBrowserView", "getBrowserView", "addBrowserView",
"removeBrowserView", "setTopBrowserView", "getBrowserViews", "setTitleBarOverlay"]
if (response.success) {
let winId = response.result.winId;
const obj = {
...response.result,
webContents: {
// 添加窗口事件监听方法
on: (eventName, callback) => {
const eventKey = generateEventKey(eventName, winId);
console.log('set eventKey', eventKey)
if (!windowEventListeners.has(eventKey)) {
windowEventListeners.set(eventKey, new Set());
}
windowEventListeners.get(eventKey).add(callback);
},
// 移除窗口事件监听方法
off: (eventName, callback) => {
const eventKey = generateEventKey(eventName, winId);
const listeners = windowEventListeners.get(eventKey);
if (listeners) {
listeners.delete(callback);
if (listeners.size === 0) {
windowEventListeners.delete(eventKey);
}
}
},
// 一次性事件监听
once: (eventName, callback) => {
const eventKey = generateEventKey(eventName, winId);
const onceCallback = (...args) => {
callback(...args);
windowEventListeners.get(eventKey)?.delete(onceCallback);
};
if (!windowEventListeners.has(eventKey)) {
windowEventListeners.set(eventKey, new Set());
}
windowEventListeners.get(eventKey).add(onceCallback);
},
},
// 添加窗口对象的事件处理方法
on: (eventName, callback) => {
const eventKey = generateEventKey(eventName, winId);
if (!windowObjEventListeners.has(eventKey)) {
windowObjEventListeners.set(eventKey, new Set());
}
windowObjEventListeners.get(eventKey).add(callback);
},
off: (eventName, callback) => {
const eventKey = generateEventKey(eventName, winId);
const listeners = windowObjEventListeners.get(eventKey);
if (listeners) {
listeners.delete(callback);
if (listeners.size === 0) {
windowObjEventListeners.delete(eventKey);
}
}
},
once: (eventName, callback) => {
const eventKey = generateEventKey(eventName, winId);
const onceCallback = (...args) => {
callback(...args);
windowObjEventListeners.get(eventKey)?.delete(onceCallback);
};
if (!windowObjEventListeners.has(eventKey)) {
windowObjEventListeners.set(eventKey, new Set());
}
windowObjEventListeners.get(eventKey).add(onceCallback);
}
};
// 为每个属性定义 getter
windowProperties.forEach(prop => {
Object.defineProperty(obj, prop, {
enumerable: true,
configurable: true,
get: async () => {
const response = await ipcRenderer.invoke('window-obj-action', {
method: prop,
winId: winId,
type: 'property'
});
if (response.success) {
return response.result;
} else {
throw new Error(response.error);
}
}
});
});
windowMethods.forEach(method => {
Object.defineProperty(obj, method, {
enumerable: true,
configurable: true,
value: async (...args) => {
const response = await ipcRenderer.invoke('window-obj-action', {
method,
winId: winId,
type: 'method',
args
});
if (response.success) {
return response.result;
} else {
throw new Error(response.error);
}
}
});
});
return obj;
} else {
throw new Error(response.error);
}
},
// 添加模态窗口方法
openModalWindow: async (parentWinId, windowPath, options = {}) => {
let response = await ipcRenderer.invoke('window-manager-action', {
method: '_openModalWindow',
args: [parentWinId, windowPath, options]
});
// 复用现有的窗口属性和方法处理逻辑
let windowProperties = [
// 'webContents',
'id', 'tabbingIdentifier', 'autoHideMenuBar', 'simpleFullScreen', 'fullScreen',
'focusable',
'visibleOnAllWorkspaces', 'shadow',
'menuBarVisible', 'kiosk',
'documentEdited', 'representedFilename', 'title', 'minimizable',
'maximizable', 'fullScreenable', 'resizable', 'closable', 'movable',
'excludedFromShownWindowsMenu', 'accessibleTitle'
];
let windowMethods = ["destroy", "close", "focus", "blur", "isFocused", "isDestroyed", "show", "showInactive", "hide", "isVisible", "isModal", "maximize", "unmaximize",
"isMaximized", "minimize", "restore", "isMinimized", "setFullScreen", "isFullScreen", "setSimpleFullScreen", "isSimpleFullScreen", "isNormal", "setAspectRatio",
"setBackgroundColor", "previewFile", "closeFilePreview", "setBounds", "getBounds", "getBackgroundColor", "setContentBounds", "getContentBounds", "getNormalBounds",
"setEnabled", "isEnabled", "setSize", "getSize", "setContentSize", "getContentSize", "setMinimumSize", "getMinimumSize", "setMaximumSize", "getMaximumSize",
"setResizable", "isResizable", "setMovable", "isMovable", "setMinimizable", "isMinimizable", "setMaximizable", "isMaximizable", "setFullScreenable",
"isFullScreenable", "setClosable", "isClosable", "setHiddenInMissionControl", "isHiddenInMissionControl", "setAlwaysOnTop", "isAlwaysOnTop", "moveAbove", "moveTop",
"center", "setPosition", "getPosition", "setTitle", "getTitle", "setSheetOffset", "flashFrame", "setSkipTaskbar", "setKiosk", "isKiosk", "isTabletMode",
"getMediaSourceId", "getNativeWindowHandle", "hookWindowMessage", "isWindowMessageHooked", "unhookWindowMessage", "unhookAllWindowMessages",
"setRepresentedFilename", "getRepresentedFilename", "setDocumentEdited", "isDocumentEdited", "focusOnWebView", "blurWebView", "capturePage", "loadURL", "loadFile",
"reload", "setMenu", "removeMenu", "setProgressBar", "setOverlayIcon", "invalidateShadow", "setHasShadow", "hasShadow", "setOpacity", "getOpacity", "setShape",
"setThumbarButtons", "setThumbnailClip", "setThumbnailToolTip", "setAppDetails", "showDefinitionForSelection", "setIcon", "setWindowButtonVisibility",
"setAutoHideMenuBar", "isMenuBarAutoHide", "setMenuBarVisibility", "isMenuBarVisible", "setVisibleOnAllWorkspaces", "isVisibleOnAllWorkspaces",
"setIgnoreMouseEvents", "setContentProtection", "setFocusable", "isFocusable", "setParentWindow", "getParentWindow", "getChildWindows", "setAutoHideCursor",
"selectPreviousTab", "selectNextTab", "showAllTabs", "mergeAllWindows", "moveTabToNewWindow", "toggleTabBar", "addTabbedWindow", "setVibrancy",
"setBackgroundMaterial", "setWindowButtonPosition", "getWindowButtonPosition", "setTouchBar", "setBrowserView", "getBrowserView", "addBrowserView",
"removeBrowserView", "setTopBrowserView", "getBrowserViews", "setTitleBarOverlay"
];
if (response.success) {
let winId = response.result.winId;
const obj = {
...response.result,
webContents: {
// 复用现有的事件处理方法
on: (eventName, callback) => {
const eventKey = generateEventKey(eventName, winId);
if (!windowEventListeners.has(eventKey)) {
windowEventListeners.set(eventKey, new Set());
}
windowEventListeners.get(eventKey).add(callback);
},
off: (eventName, callback) => {
const eventKey = generateEventKey(eventName, winId);
const listeners = windowEventListeners.get(eventKey);
if (listeners) {
listeners.delete(callback);
if (listeners.size === 0) {
windowEventListeners.delete(eventKey);
}
}
},
once: (eventName, callback) => {
const eventKey = generateEventKey(eventName, winId);
const onceCallback = (...args) => {
callback(...args);
windowEventListeners.get(eventKey)?.delete(onceCallback);
};
if (!windowEventListeners.has(eventKey)) {
windowEventListeners.set(eventKey, new Set());
}
windowEventListeners.get(eventKey).add(onceCallback);
},
},
on: (eventName, callback) => {
const eventKey = generateEventKey(eventName, winId);
if (!windowObjEventListeners.has(eventKey)) {
windowObjEventListeners.set(eventKey, new Set());
}
windowObjEventListeners.get(eventKey).add(callback);
},
off: (eventName, callback) => {
const eventKey = generateEventKey(eventName, winId);
const listeners = windowObjEventListeners.get(eventKey);
if (listeners) {
listeners.delete(callback);
if (listeners.size === 0) {
windowObjEventListeners.delete(eventKey);
}
}
},
once: (eventName, callback) => {
const eventKey = generateEventKey(eventName, winId);
const onceCallback = (...args) => {
callback(...args);
windowObjEventListeners.get(eventKey)?.delete(onceCallback);
};
if (!windowObjEventListeners.has(eventKey)) {
windowObjEventListeners.set(eventKey, new Set());
}
windowObjEventListeners.get(eventKey).add(onceCallback);
}
};
// 复用现有的属性和方法定义逻辑
windowProperties.forEach(prop => {
Object.defineProperty(obj, prop, {
enumerable: true,
configurable: true,
get: async () => {
const response = await ipcRenderer.invoke('window-obj-action', {
method: prop,
winId: winId,
type: 'property'
});
if (response.success) {
return response.result;
} else {
throw new Error(response.error);
}
}
});
});
windowMethods.forEach(method => {
Object.defineProperty(obj, method, {
enumerable: true,
configurable: true,
value: async (...args) => {
const response = await ipcRenderer.invoke('window-obj-action', {
method,
winId: winId,
type: 'method',
args
});
if (response.success) {
return response.result;
} else {
throw new Error(response.error);
}
}
});
});
return obj;
} else {
throw new Error(response.error);
}
},
getAllWindow: async (...args) => {
let response = await ipcRenderer.invoke('window-manager-action', {
method: '_getAllWindow',
args
});
if (response.success) {
return response.result;
} else {
throw new Error(response.error);
}
},
getWindowInfo: async (...args) => {
let response = await ipcRenderer.invoke('window-manager-action', {
method: '_getWindowInfo',
args
});
if (response.success) {
return response.result;
} else {
throw new Error(response.error);
}
},
// 发送消息到指定窗口
sendToWindow: async (winId, eventName, data) => {
let response = await ipcRenderer.invoke('window-manager-action', {
method: 'sendToWindow',
args: [winId, eventName, data]
});
return response.success;
},
// 广播消息
broadcast: async (channel, data, excludeWinIds = []) => {
let response = await ipcRenderer.invoke('window-manager-action', {
method: 'broadcast',
args: [channel, data, excludeWinIds]
});
return response.success;
},
// 监听事件
on: (eventName, callback) => {
if (!windowEventListeners.has(eventName)) {
windowEventListeners.set(eventName, new Set());
}
windowEventListeners.get(eventName).add(callback);
},
// 移除事件监听
off: (eventName, callback) => {
const listeners = windowEventListeners.get(eventName);
if (listeners) {
listeners.delete(callback);
if (listeners.size === 0) {
windowEventListeners.delete(eventName);
}
}
},
// 一次性事件监听
once: (eventName, callback) => {
const onceCallback = (...args) => {
callback(...args);
windowEventListeners.get(eventName)?.delete(onceCallback);
};
if (!windowEventListeners.has(eventName)) {
windowEventListeners.set(eventName, new Set());
}
windowEventListeners.get(eventName).add(onceCallback);
}
});
contextBridge.exposeInMainWorld('openWindow', async (windowId, options = {}) => {
try {
const response = await ipcRenderer.invoke('window-manager-action', {
method: '_openWindow',
args: [windowId, options]
});
if (response.success) {
return response.result;
} else {
throw new Error(response.error);
}
} catch (error) {
throw error;
}
});