1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

electron 14 & remove @electron/remote dependency

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
Jari Kolehmainen 2022-01-03 16:19:06 +02:00 committed by Sebastian Malton
parent 10e4416b07
commit 197dbe6cb0
16 changed files with 230 additions and 122 deletions

View File

@ -1,3 +1,3 @@
disturl "https://atom.io/download/electron"
target "13.6.1"
target "14.2.1"
runtime "electron"

View File

@ -191,7 +191,6 @@
}
},
"dependencies": {
"@electron/remote": "^1.2.2",
"@hapi/call": "^8.0.1",
"@hapi/subtext": "^7.0.3",
"@kubernetes/client-node": "^0.16.1",
@ -334,7 +333,7 @@
"css-loader": "^5.2.7",
"deepdash": "^5.3.9",
"dompurify": "^2.3.4",
"electron": "^13.6.1",
"electron": "^14.2.1",
"electron-builder": "^22.14.5",
"electron-notarize": "^0.3.0",
"esbuild": "^0.13.15",

24
src/common/ipc/dialog.ts Normal file
View File

@ -0,0 +1,24 @@
/**
* Copyright (c) 2021 OpenLens Authors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
export const enum IpcMainDialogEvents {
SHOW_OPEN = "dialog:show-open",
}

View File

@ -13,3 +13,4 @@ export * from "./cluster.ipc";
export * from "./type-enforced-ipc";
export * from "./hotbar";
export * from "./extension-loader.ipc";
export * from "./window";

View File

@ -12,21 +12,8 @@ import { toJS } from "../utils/toJS";
import logger from "../../main/logger";
import { ClusterFrameInfo, clusterFrameMap } from "../cluster-frames";
import type { Disposer } from "../utils";
import type remote from "@electron/remote";
const electronRemote = (() => {
if (ipcRenderer) {
try {
return require("@electron/remote");
} catch {
// ignore temp
}
}
return null;
})();
const subFramesChannel = "ipc:get-sub-frames";
export const broadcastMainChannel = "ipc:broadcast-main";
export async function requestMain(channel: string, ...args: any[]) {
return ipcRenderer.invoke(channel, ...args.map(sanitizePayload));
@ -42,51 +29,48 @@ function getSubFrames(): ClusterFrameInfo[] {
return Array.from(clusterFrameMap.values());
}
export function broadcastMessage(channel: string, ...args: any[]) {
const subFramesP = ipcRenderer
? requestMain(subFramesChannel)
: Promise.resolve(getSubFrames());
export async function broadcastMessage(channel: string, ...args: any[]): Promise<void> {
if (ipcRenderer) {
return requestMain(broadcastMainChannel, ...args);
}
subFramesP
.then(subFrames => {
const views: undefined | ReturnType<typeof webContents.getAllWebContents> | ReturnType<typeof remote.webContents.getAllWebContents> = (webContents || electronRemote?.webContents)?.getAllWebContents();
const subFrames = getSubFrames();
const views = webContents.getAllWebContents();
if (!views || !Array.isArray(views) || views.length === 0) return;
args = args.map(sanitizePayload);
if (!views || !Array.isArray(views) || views.length === 0) return;
args = args.map(sanitizePayload);
ipcRenderer?.send(channel, ...args);
ipcMain?.emit(channel, ...args);
ipcMain?.emit(channel, ...args);
for (const view of views) {
let viewType = "unknown";
for (const view of views) {
let viewType = "unknown";
// There will be a uncaught exception if the view is destroyed.
try {
viewType = view.getType();
} catch {
// We can ignore the view destroyed exception as viewType is only used for logging.
}
// There will be a uncaught exception if the view is destroyed.
try {
viewType = view.getType();
} catch {
// We can ignore the view destroyed exception as viewType is only used for logging.
}
// Send message to views.
try {
logger.silly(`[IPC]: broadcasting "${channel}" to ${viewType}=${view.id}`, { args });
view.send(channel, ...args);
} catch (error) {
logger.error(`[IPC]: failed to send IPC message "${channel}" to view "${viewType}=${view.id}"`, { error: String(error) });
}
// Send message to views.
try {
logger.silly(`[IPC]: broadcasting "${channel}" to ${viewType}=${view.id}`, { args });
view.send(channel, ...args);
} catch (error) {
logger.error(`[IPC]: failed to send IPC message "${channel}" to view "${viewType}=${view.id}"`, { error: String(error) });
}
// Send message to subFrames of views.
for (const frameInfo of subFrames) {
logger.silly(`[IPC]: broadcasting "${channel}" to subframe "frameInfo.processId"=${frameInfo.processId} "frameInfo.frameId"=${frameInfo.frameId}`, { args });
// Send message to subFrames of views.
for (const frameInfo of subFrames) {
logger.silly(`[IPC]: broadcasting "${channel}" to subframe "frameInfo.processId"=${frameInfo.processId} "frameInfo.frameId"=${frameInfo.frameId}`, { args });
try {
view.sendToFrame([frameInfo.processId, frameInfo.frameId], channel, ...args);
} catch (error) {
logger.error(`[IPC]: failed to send IPC message "${channel}" to view "${viewType}=${view.id}"'s subframe "frameInfo.processId"=${frameInfo.processId} "frameInfo.frameId"=${frameInfo.frameId}`, { error: String(error) });
}
}
try {
view.sendToFrame([frameInfo.processId, frameInfo.frameId], channel, ...args);
} catch (error) {
logger.error(`[IPC]: failed to send IPC message "${channel}" to view "${viewType}=${view.id}"'s subframe "frameInfo.processId"=${frameInfo.processId} "frameInfo.frameId"=${frameInfo.frameId}`, { error: String(error) });
}
});
}
}
}
export function ipcMainOn(channel: string, listener: (event: Electron.IpcMainEvent, ...args: any[]) => any): Disposer {
@ -101,10 +85,6 @@ export function ipcRendererOn(channel: string, listener: (event: Electron.IpcRen
return () => ipcRenderer.off(channel, listener);
}
export function bindBroadcastHandlers() {
ipcMainHandle(subFramesChannel, () => getSubFrames());
}
/**
* Sanitizing data for IPC-messaging before send.
* Removes possible observable values to avoid exceptions like "can't clone object".

10
src/common/ipc/window.ts Normal file
View File

@ -0,0 +1,10 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
export const enum IpcMainWindowEvents {
OPEN_CONTEXT_MENU = "window:open-context-menu",
WINDOW_ACTION = "window:window-action",
LOCATION_CHANGED = "window:location-changed",
}

View File

@ -6,7 +6,6 @@
// Main process
import { injectSystemCAs } from "../common/system-ca";
import { initialize as initializeRemote } from "@electron/remote/main";
import * as Mobx from "mobx";
import * as LensExtensionsCommonApi from "../extensions/common-api";
import * as LensExtensionsMainApi from "../extensions/main-api";
@ -24,7 +23,7 @@ import type { InstalledExtension } from "../extensions/extension-discovery/exten
import type { LensExtensionId } from "../extensions/lens-extension";
import { installDeveloperTools } from "./developer-tools";
import { disposer, getAppVersion, getAppVersionFromProxyServer } from "../common/utils";
import { bindBroadcastHandlers, ipcMainOn } from "../common/ipc";
import { ipcMainOn } from "../common/ipc";
import { startUpdateChecking } from "./app-updater";
import { IpcRendererNavigationEvents } from "../renderer/navigation/events";
import { pushCatalogToRenderer } from "./catalog-pusher";
@ -81,9 +80,6 @@ di.runSetups().then(() => {
app.disableHardwareAcceleration();
}
logger.debug("[APP-MAIN] initializing remote");
initializeRemote();
logger.debug("[APP-MAIN] configuring packages");
configurePackages();
@ -131,8 +127,6 @@ di.runSetups().then(() => {
logger.info("🐚 Syncing shell environment");
await shellSync();
bindBroadcastHandlers();
powerMonitor.on("shutdown", () => app.exit());
registerFileProtocol("static", __static);

View File

@ -9,17 +9,20 @@ import { clusterActivateHandler, clusterSetFrameIdHandler, clusterVisibilityHand
import type { ClusterId } from "../../../common/cluster-types";
import { ClusterStore } from "../../../common/cluster-store/cluster-store";
import { appEventBus } from "../../../common/app-event-bus/event-bus";
import { dialogShowOpenDialogHandler, ipcMainHandle, ipcMainOn } from "../../../common/ipc";
import { broadcastMainChannel, broadcastMessage, dialogShowOpenDialogHandler, ipcMainHandle, ipcMainOn, IpcMainWindowEvents } from "../../../common/ipc";
import { catalogEntityRegistry } from "../../catalog";
import { pushCatalogToRenderer } from "../../catalog-pusher";
import { ClusterManager } from "../../cluster-manager";
import { ResourceApplier } from "../../resource-applier";
import { IpcMainWindowEvents, WindowManager } from "../../window-manager";
import { WindowManager } from "../../window-manager";
import path from "path";
import { remove } from "fs-extra";
import { getAppMenu } from "../../menu/menu";
import type { MenuRegistration } from "../../menu/menu-registration";
import type { IComputedValue } from "mobx";
import { onLocationChange, windowAction } from "../../ipc/window";
import { IpcMainDialogEvents } from "../../../common/ipc/dialog";
import { showOpenDialog } from "../../ipc/dialog";
interface Dependencies {
electronMenuItems: IComputedValue<MenuRegistration[]>,
@ -142,6 +145,14 @@ export const initIpcMainHandlers = ({ electronMenuItems, directoryForLensLocalSt
return dialog.showOpenDialog(BrowserWindow.getFocusedWindow(), dialogOpts);
});
ipcMainHandle(IpcMainWindowEvents.WINDOW_ACTION, (event, action) => windowAction(action));
ipcMainHandle(IpcMainWindowEvents.LOCATION_CHANGED, () => onLocationChange());
ipcMainHandle(IpcMainDialogEvents.SHOW_OPEN, (event, opts) => showOpenDialog(opts));
ipcMainHandle(broadcastMainChannel, (event, channel, ...args) => broadcastMessage(channel, ...args));
ipcMainOn(IpcMainWindowEvents.OPEN_CONTEXT_MENU, async (event) => {
const menu = Menu.buildFromTemplate(getAppMenu(WindowManager.getInstance(), electronMenuItems.get()));
const options = {

28
src/main/ipc/dialog.ts Normal file
View File

@ -0,0 +1,28 @@
/**
* Copyright (c) 2021 OpenLens Authors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { BrowserWindow, dialog, OpenDialogOptions } from "electron";
export async function showOpenDialog(dialogOptions: OpenDialogOptions) {
const { canceled, filePaths } = await dialog.showOpenDialog(BrowserWindow.getFocusedWindow(), dialogOptions);
return { canceled, filePaths };
}

97
src/main/ipc/window.ts Normal file
View File

@ -0,0 +1,97 @@
/**
* Copyright (c) 2021 OpenLens Authors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import { BrowserWindow, webContents } from "electron";
import { broadcastMessage } from "../../common/ipc";
type WindowAction = "goBack" | "goForward" | "minimize" | "toggleMaximize" | "close";
export function windowAction(action: WindowAction) {
const window = BrowserWindow.getFocusedWindow();
if (!window) return;
switch (action) {
case "goBack": {
window.webContents.goBack();
break;
}
case "goForward": {
window.webContents.goForward();
break;
}
case "minimize": {
window.minimize();
break;
}
case "toggleMaximize": {
if (window.isMaximized()) {
window.unmaximize();
} else {
window.maximize();
}
break;
}
case "close": {
window.close();
break;
}
}
if (action === "goBack") {
window.webContents.goBack();
} else if (action === "goForward") {
window.webContents.goForward();
} else if (action === "toggleMaximize") {
if (window.isMaximized()) {
window.unmaximize();
} else {
window.maximize();
}
}
}
export function onLocationChange() {
const getAllWebContents = webContents.getAllWebContents();
const canGoBack = getAllWebContents.some((webContent) => {
if (webContent.getType() === "window") {
return webContent.canGoBack();
}
return false;
});
const canGoForward = getAllWebContents.some((webContent) => {
if (webContent.getType() === "window") {
return webContent.canGoForward();
}
return false;
});
broadcastMessage("history:can-go-back", canGoBack);
broadcastMessage("history:can-go-forward", canGoForward);
}

View File

@ -16,10 +16,6 @@ import logger from "./logger";
import { isMac, productName } from "../common/vars";
import { LensProxy } from "./lens-proxy";
export const enum IpcMainWindowEvents {
OPEN_CONTEXT_MENU = "window:open-context-menu",
}
function isHideable(window: BrowserWindow | null): boolean {
return Boolean(window && !window.isDestroyed());
}
@ -75,7 +71,6 @@ export class WindowManager extends Singleton {
webPreferences: {
nodeIntegration: true,
nodeIntegrationInSubFrames: true,
enableRemoteModule: true,
webviewTag: true,
contextIsolation: false,
},
@ -249,7 +244,6 @@ export class WindowManager extends Singleton {
show: false,
webPreferences: {
nodeIntegration: true,
enableRemoteModule: true,
contextIsolation: false,
nodeIntegrationInSubFrames: true,
},

View File

@ -7,7 +7,7 @@ import React from "react";
import { fireEvent } from "@testing-library/react";
import "@testing-library/jest-dom/extend-expect";
import { TopBar } from "./top-bar";
import { IpcMainWindowEvents } from "../../../../main/window-manager";
import { IpcMainWindowEvents } from "../../../../common/ipc";
import { broadcastMessage } from "../../../../common/ipc";
import * as vars from "../../../../common/vars";
import { getDiForUnitTesting } from "../../../getDiForUnitTesting";

View File

@ -4,17 +4,15 @@
*/
import styles from "./top-bar.module.scss";
import React, { useEffect, useMemo, useRef } from "react";
import React, { useEffect, useRef } from "react";
import { observer } from "mobx-react";
import type { IComputedValue } from "mobx";
import { Icon } from "../../icon";
import { webContents, getCurrentWindow } from "@electron/remote";
import { observable } from "mobx";
import { broadcastMessage, ipcRendererOn } from "../../../../common/ipc";
import { broadcastMessage, IpcMainWindowEvents, ipcRendererOn, requestMain } from "../../../../common/ipc";
import { watchHistoryState } from "../../../remote-helpers/history-updater";
import { isActiveRoute, navigate } from "../../../navigation";
import { catalogRoute, catalogURL } from "../../../../common/routes";
import { IpcMainWindowEvents } from "../../../../main/window-manager";
import { isLinux, isWindows } from "../../../../common/vars";
import { cssNames } from "../../../utils";
import topBarItemsInjectable from "./top-bar-items/top-bar-items.injectable";
@ -40,7 +38,6 @@ ipcRendererOn("history:can-go-forward", (event, state: boolean) => {
const NonInjectedTopBar = (({ items, children, ...rest }: Props & Dependencies) => {
const elem = useRef<HTMLDivElement>();
const window = useMemo(() => getCurrentWindow(), []);
const openContextMenu = () => {
broadcastMessage(IpcMainWindowEvents.OPEN_CONTEXT_MENU);
@ -51,11 +48,11 @@ const NonInjectedTopBar = (({ items, children, ...rest }: Props & Dependencies)
};
const goBack = () => {
webContents.getAllWebContents().find((webContent) => webContent.getType() === "window")?.goBack();
requestMain(IpcMainWindowEvents.WINDOW_ACTION, "back");
};
const goForward = () => {
webContents.getAllWebContents().find((webContent) => webContent.getType() === "window")?.goForward();
requestMain(IpcMainWindowEvents.WINDOW_ACTION, "forward");
};
const windowSizeToggle = (evt: React.MouseEvent) => {
@ -68,19 +65,15 @@ const NonInjectedTopBar = (({ items, children, ...rest }: Props & Dependencies)
};
const minimizeWindow = () => {
window.minimize();
requestMain(IpcMainWindowEvents.WINDOW_ACTION, "minimize");
};
const toggleMaximize = () => {
if (window.isMaximized()) {
window.unmaximize();
} else {
window.maximize();
}
requestMain(IpcMainWindowEvents.WINDOW_ACTION, "toggleMaximize");
};
const closeWindow = () => {
window.close();
requestMain(IpcMainWindowEvents.WINDOW_ACTION, "close");
};
useEffect(() => {

View File

@ -3,11 +3,13 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { FileFilter, OpenDialogOptions, remote } from "electron";
import type { FileFilter, OpenDialogOptions } from "electron";
import { observer } from "mobx-react";
import React from "react";
import { cssNames } from "../../utils";
import { Button } from "../button";
import { requestMain } from "../../../common/ipc";
import { IpcMainDialogEvents } from "../../../common/ipc/dialog";
export interface PathPickOpts {
label: string;
@ -29,8 +31,8 @@ export interface PathPickerProps extends PathPickOpts {
export class PathPicker extends React.Component<PathPickerProps> {
static async pick(opts: PathPickOpts) {
const { onPick, onCancel, label, ...dialogOptions } = opts;
const { dialog, BrowserWindow } = remote;
const { canceled, filePaths } = await dialog.showOpenDialog(BrowserWindow.getFocusedWindow(), {
const { canceled, filePaths } = await requestMain(IpcMainDialogEvents.SHOW_OPEN, {
message: label,
...dialogOptions,
});

View File

@ -3,32 +3,12 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { webContents } from "@electron/remote";
import { reaction } from "mobx";
import { broadcastMessage } from "../../common/ipc";
import { IpcMainWindowEvents, requestMain } from "../../common/ipc";
import { navigation } from "../navigation";
export function watchHistoryState() {
return reaction(() => navigation.location, () => {
const getAllWebContents = webContents.getAllWebContents();
const canGoBack = getAllWebContents.some((webContent) => {
if (webContent.getType() === "window") {
return webContent.canGoBack();
}
return false;
});
const canGoForward = getAllWebContents.some((webContent) => {
if (webContent.getType() === "window") {
return webContent.canGoForward();
}
return false;
});
broadcastMessage("history:can-go-back", canGoBack);
broadcastMessage("history:can-go-forward", canGoForward);
return reaction(() => navigation.location, (location) => {
requestMain(IpcMainWindowEvents.LOCATION_CHANGED, location);
});
}

View File

@ -396,11 +396,6 @@
global-agent "^2.0.2"
global-tunnel-ng "^2.7.1"
"@electron/remote@^1.2.2":
version "1.2.2"
resolved "https://registry.yarnpkg.com/@electron/remote/-/remote-1.2.2.tgz#4c390a2e669df47af973c09eec106162a296c323"
integrity sha512-PfnXpQGWh4vpX866NNucJRnNOzDRZcsLcLaT32fUth9k0hccsohfxprqEDYLzRg+ZK2xRrtyUN5wYYoHimMCJg==
"@electron/universal@1.0.5":
version "1.0.5"
resolved "https://registry.yarnpkg.com/@electron/universal/-/universal-1.0.5.tgz#b812340e4ef21da2b3ee77b2b4d35c9b86defe37"
@ -5055,10 +5050,10 @@ electron-window-state@^5.0.3:
jsonfile "^4.0.0"
mkdirp "^0.5.1"
electron@^13.6.1:
version "13.6.1"
resolved "https://registry.yarnpkg.com/electron/-/electron-13.6.1.tgz#f61c4f269b57c7dc27e0d5476216a988caa9c752"
integrity sha512-rZ6Y7RberigruefQpWOiI4bA9ppyT88GQF8htY6N1MrAgal5RrBc+Mh92CcGU7zT9QO+XO3DarSgZafNTepffQ==
electron@^14.2.1:
version "14.2.3"
resolved "https://registry.yarnpkg.com/electron/-/electron-14.2.3.tgz#3facf572c57cefe8ce80154ad3e63f937784644b"
integrity sha512-7wBqvzUKhK1tw544w3+F8J7NajnqURGC4pH3VFTiBHU5ayiI/oaTTXJxyFLZ54zsR7xwon/3dYEVjIm2i68+Zg==
dependencies:
"@electron/get" "^1.0.1"
"@types/node" "^14.6.2"