Improve script error dialogs #304
- Include the script's directory path #304. - Exclude Windows-specific instructions on non-Windows OS. - Standardize language across dialogs for consistency. Other supporting changes: - Add script diagnostics data collection from main process. - Document script file storage and execution tamper protection in SECURITY.md. - Remove redundant comment in `NodeReadbackFileWriter`. - Centralize error display for uniformity and simplicity. - Simpify `WindowVariablesValidator` to omit checks when not on the renderer process. - Improve and centralize Electron environment detection. - Use more emphatic language (don't worry) in error messages.
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
import { ElectronEnvironmentDetector, ElectronProcessType } from './ElectronEnvironmentDetector';
|
||||
|
||||
export class ContextIsolatedElectronDetector implements ElectronEnvironmentDetector {
|
||||
constructor(
|
||||
private readonly nodeProcessAccessor: NodeProcessAccessor = () => globalThis?.process,
|
||||
private readonly userAgentAccessor: UserAgentAccessor = () => globalThis?.navigator?.userAgent,
|
||||
) { }
|
||||
|
||||
public isRunningInsideElectron(): boolean {
|
||||
return isNodeProcessElectronBased(this.nodeProcessAccessor)
|
||||
|| isUserAgentElectronBased(this.userAgentAccessor);
|
||||
}
|
||||
|
||||
public determineElectronProcessType(): ElectronProcessType {
|
||||
const isNodeAccessible = isNodeProcessElectronBased(this.nodeProcessAccessor);
|
||||
const isBrowserAccessible = isUserAgentElectronBased(this.userAgentAccessor);
|
||||
if (!isNodeAccessible && !isBrowserAccessible) {
|
||||
throw new Error('Unable to determine the Electron process type. Neither Node.js nor browser-based Electron contexts were detected.');
|
||||
}
|
||||
if (isNodeAccessible && isBrowserAccessible) {
|
||||
return 'preloader'; // Only preloader can access both Node.js and browser contexts in Electron with context isolation.
|
||||
}
|
||||
if (isNodeAccessible) {
|
||||
return 'main';
|
||||
}
|
||||
return 'renderer';
|
||||
}
|
||||
}
|
||||
|
||||
export type NodeProcessAccessor = () => NodeJS.Process | undefined;
|
||||
|
||||
function isNodeProcessElectronBased(nodeProcessAccessor: NodeProcessAccessor): boolean {
|
||||
const nodeProcess = nodeProcessAccessor();
|
||||
if (!nodeProcess) {
|
||||
return false;
|
||||
}
|
||||
if (nodeProcess.versions.electron) {
|
||||
// Electron populates `nodeProcess.versions.electron` with its version, see https://web.archive.org/web/20240113162837/https://www.electronjs.org/docs/latest/api/process#processversionselectron-readonly.
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export type UserAgentAccessor = () => string | undefined;
|
||||
|
||||
function isUserAgentElectronBased(
|
||||
userAgentAccessor: UserAgentAccessor,
|
||||
): boolean {
|
||||
const userAgent = userAgentAccessor();
|
||||
if (userAgent?.includes('Electron')) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export interface ElectronEnvironmentDetector {
|
||||
isRunningInsideElectron(): boolean;
|
||||
determineElectronProcessType(): ElectronProcessType;
|
||||
}
|
||||
|
||||
export type ElectronProcessType = 'main' | 'preloader' | 'renderer';
|
||||
@@ -1,49 +1,32 @@
|
||||
import { ElectronEnvironmentDetector } from './Electron/ElectronEnvironmentDetector';
|
||||
import { BrowserRuntimeEnvironment } from './Browser/BrowserRuntimeEnvironment';
|
||||
import { NodeRuntimeEnvironment } from './Node/NodeRuntimeEnvironment';
|
||||
import { RuntimeEnvironment } from './RuntimeEnvironment';
|
||||
import { ContextIsolatedElectronDetector } from './Electron/ContextIsolatedElectronDetector';
|
||||
|
||||
export const CurrentEnvironment = determineAndCreateRuntimeEnvironment({
|
||||
window: globalThis.window,
|
||||
process: globalThis.process,
|
||||
});
|
||||
export const CurrentEnvironment = determineAndCreateRuntimeEnvironment(globalThis.window);
|
||||
|
||||
export function determineAndCreateRuntimeEnvironment(
|
||||
globalAccessor: GlobalPropertiesAccessor,
|
||||
globalWindow: Window | undefined | null = globalThis.window,
|
||||
electronDetector: ElectronEnvironmentDetector = new ContextIsolatedElectronDetector(),
|
||||
browserEnvironmentFactory: BrowserRuntimeEnvironmentFactory = (
|
||||
window,
|
||||
) => new BrowserRuntimeEnvironment(window),
|
||||
nodeEnvironmentFactory: NodeRuntimeEnvironmentFactory = (
|
||||
process: NodeJS.Process,
|
||||
) => new NodeRuntimeEnvironment(process),
|
||||
nodeEnvironmentFactory: NodeRuntimeEnvironmentFactory = () => new NodeRuntimeEnvironment(),
|
||||
): RuntimeEnvironment {
|
||||
if (isElectronMainProcess(globalAccessor.process)) {
|
||||
return nodeEnvironmentFactory(globalAccessor.process);
|
||||
if (
|
||||
electronDetector.isRunningInsideElectron()
|
||||
&& electronDetector.determineElectronProcessType() === 'main') {
|
||||
return nodeEnvironmentFactory();
|
||||
}
|
||||
const { window } = globalAccessor;
|
||||
if (!window) {
|
||||
if (!globalWindow) {
|
||||
throw new Error('Unsupported runtime environment: The current context is neither a recognized browser nor a desktop environment.');
|
||||
}
|
||||
return browserEnvironmentFactory(window);
|
||||
}
|
||||
|
||||
function isElectronMainProcess(
|
||||
nodeProcess: NodeJS.Process | undefined,
|
||||
): nodeProcess is NodeJS.Process {
|
||||
// Electron populates `nodeProcess.versions.electron` with its version, see https://web.archive.org/web/20240113162837/https://www.electronjs.org/docs/latest/api/process#processversionselectron-readonly.
|
||||
if (!nodeProcess) {
|
||||
return false;
|
||||
}
|
||||
if (nodeProcess.versions.electron) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return browserEnvironmentFactory(globalWindow);
|
||||
}
|
||||
|
||||
export type BrowserRuntimeEnvironmentFactory = (window: Window) => RuntimeEnvironment;
|
||||
|
||||
export type NodeRuntimeEnvironmentFactory = (process: NodeJS.Process) => NodeRuntimeEnvironment;
|
||||
export type NodeRuntimeEnvironmentFactory = () => NodeRuntimeEnvironment;
|
||||
|
||||
export interface GlobalPropertiesAccessor {
|
||||
readonly window: Window | undefined;
|
||||
readonly process: NodeJS.Process | undefined;
|
||||
}
|
||||
export type GlobalWindowAccessor = Window | undefined;
|
||||
|
||||
Reference in New Issue
Block a user