81bdba67ec
* feat: Implement password delegate for NSS (#41188) Introduce an app event client-certificate-request-password. It allows the user to display a UI to prompt for the password. An alternative would have been to implement a class similar to CryptoModulePasswordDialogView, to provide the UI. This might have been simpler for the user, comparing to letting them implement the UI. But it seems like electron does not have an i18n framework, so it's not possible to provide a locale aware UI. * fix lint:markdown error * address review comments * use a trampoline handler in JS. The api exposed is now app.setClientCertRequestPasswordHandler * use properties on the Event object instead of positional parameters * remove ChromeNSSCryptoModuleDelegate::OnPassword in favor of args->GetNext(&password_) * address review comments second round - backslash escape parametrized TypeScript - rename hostName param to hostname - use base::ScopedAllowBaseSyncPrimitivesForTesting - and then, rename ChromeNSSCryptoModuleDelegate to ElectronNSSCryptoModuleDelegate * Update docs/api/app.md Co-authored-by: Sam Maddock <samuel.maddock@gmail.com> * Update docs/api/app.md Co-authored-by: Erick Zhao <erick@hotmail.ca> --------- Co-authored-by: Arno Renevier <arnaud@switchboard.app> Co-authored-by: Sam Maddock <samuel.maddock@gmail.com> Co-authored-by: Erick Zhao <erick@hotmail.ca>
129 lines
3.9 KiB
TypeScript
129 lines
3.9 KiB
TypeScript
import * as fs from 'fs';
|
|
|
|
import { Menu } from 'electron/main';
|
|
|
|
const bindings = process._linkedBinding('electron_browser_app');
|
|
const commandLine = process._linkedBinding('electron_common_command_line');
|
|
const { app } = bindings;
|
|
|
|
// Only one app object permitted.
|
|
export default app;
|
|
|
|
let dockMenu: Electron.Menu | null = null;
|
|
|
|
// Properties.
|
|
|
|
const nativeASGetter = app.isAccessibilitySupportEnabled;
|
|
const nativeASSetter = app.setAccessibilitySupportEnabled;
|
|
Object.defineProperty(app, 'accessibilitySupportEnabled', {
|
|
get: () => nativeASGetter.call(app),
|
|
set: (enabled) => nativeASSetter.call(app, enabled)
|
|
});
|
|
|
|
const nativeBCGetter = app.getBadgeCount;
|
|
const nativeBCSetter = app.setBadgeCount;
|
|
Object.defineProperty(app, 'badgeCount', {
|
|
get: () => nativeBCGetter.call(app),
|
|
set: (count) => nativeBCSetter.call(app, count)
|
|
});
|
|
|
|
const nativeNGetter = app.getName;
|
|
const nativeNSetter = app.setName;
|
|
Object.defineProperty(app, 'name', {
|
|
get: () => nativeNGetter.call(app),
|
|
set: (name) => nativeNSetter.call(app, name)
|
|
});
|
|
|
|
Object.assign(app, {
|
|
commandLine: {
|
|
hasSwitch: (theSwitch: string) => commandLine.hasSwitch(String(theSwitch)),
|
|
getSwitchValue: (theSwitch: string) => commandLine.getSwitchValue(String(theSwitch)),
|
|
appendSwitch: (theSwitch: string, value?: string) => commandLine.appendSwitch(String(theSwitch), typeof value === 'undefined' ? value : String(value)),
|
|
appendArgument: (arg: string) => commandLine.appendArgument(String(arg)),
|
|
removeSwitch: (theSwitch: string) => commandLine.removeSwitch(String(theSwitch))
|
|
} as Electron.CommandLine
|
|
});
|
|
|
|
// we define this here because it'd be overly complicated to
|
|
// do in native land
|
|
Object.defineProperty(app, 'applicationMenu', {
|
|
get () {
|
|
return Menu.getApplicationMenu();
|
|
},
|
|
set (menu: Electron.Menu | null) {
|
|
return Menu.setApplicationMenu(menu);
|
|
}
|
|
});
|
|
|
|
// The native implementation is not provided on non-windows platforms
|
|
app.setAppUserModelId = app.setAppUserModelId || (() => {});
|
|
|
|
if (process.platform === 'darwin') {
|
|
const setDockMenu = app.dock!.setMenu;
|
|
app.dock!.setMenu = (menu) => {
|
|
dockMenu = menu;
|
|
setDockMenu(menu);
|
|
};
|
|
app.dock!.getMenu = () => dockMenu;
|
|
}
|
|
|
|
if (process.platform === 'linux') {
|
|
const patternVmRSS = /^VmRSS:\s*(\d+) kB$/m;
|
|
const patternVmHWM = /^VmHWM:\s*(\d+) kB$/m;
|
|
|
|
const getStatus = (pid: number) => {
|
|
try {
|
|
return fs.readFileSync(`/proc/${pid}/status`, 'utf8');
|
|
} catch {
|
|
return '';
|
|
}
|
|
};
|
|
|
|
const getEntry = (file: string, pattern: RegExp) => {
|
|
const match = file.match(pattern);
|
|
return match ? parseInt(match[1], 10) : 0;
|
|
};
|
|
|
|
const getProcessMemoryInfo = (pid: number) => {
|
|
const file = getStatus(pid);
|
|
|
|
return {
|
|
workingSetSize: getEntry(file, patternVmRSS),
|
|
peakWorkingSetSize: getEntry(file, patternVmHWM)
|
|
};
|
|
};
|
|
|
|
const nativeFn = app.getAppMetrics;
|
|
app.getAppMetrics = () => {
|
|
const metrics = nativeFn.call(app);
|
|
for (const metric of metrics) {
|
|
metric.memory = getProcessMemoryInfo(metric.pid);
|
|
}
|
|
|
|
return metrics;
|
|
};
|
|
}
|
|
|
|
// Routes the events to webContents.
|
|
const events = ['certificate-error', 'select-client-certificate'];
|
|
for (const name of events) {
|
|
app.on(name as 'certificate-error', (event, webContents, ...args: any[]) => {
|
|
webContents.emit(name, event, ...args);
|
|
});
|
|
}
|
|
|
|
app._clientCertRequestPasswordHandler = null;
|
|
app.setClientCertRequestPasswordHandler = function (handler: (params: Electron.ClientCertRequestParams) => Promise<string>) {
|
|
app._clientCertRequestPasswordHandler = handler;
|
|
};
|
|
|
|
app.on('-client-certificate-request-password', async (event: Electron.Event<Electron.ClientCertRequestParams>, callback: (password: string) => void) => {
|
|
event.preventDefault();
|
|
const { hostname, tokenName, isRetry } = event;
|
|
if (!app._clientCertRequestPasswordHandler) {
|
|
callback('');
|
|
return;
|
|
}
|
|
const password = await app._clientCertRequestPasswordHandler({ hostname, tokenName, isRetry });
|
|
callback(password);
|
|
});
|