Files
iptvnator/apps/electron-backend-e2e/src/performance/renderer-window-rss-session.ts
T

175 lines
5.9 KiB
TypeScript

import type {
RendererProcessRssCapture,
RendererProcessRssCaptureApi,
} from './renderer-process-rss-capture';
export interface RendererWindowIdentity {
readonly browserWindowId: number;
readonly webContentsId: number;
}
export interface RendererWindowRssSessionMetrics {
readonly responsiveEvents: number;
readonly rss: RendererProcessRssCapture;
readonly unresponsiveEvents: number;
readonly windowIdentity: RendererWindowIdentity;
}
export interface RendererWindowWebContents {
readonly id: number;
getOSProcessId(): number;
}
export interface RendererWindow {
readonly id: number;
readonly webContents: RendererWindowWebContents;
off(event: 'responsive' | 'unresponsive', listener: () => void): unknown;
on(event: 'responsive' | 'unresponsive', listener: () => void): unknown;
}
export interface RendererWindowRssSession {
detach(): void;
sample(): RendererWindowRssSessionMetrics;
snapshot(): RendererWindowRssSessionMetrics;
}
export interface RendererWindowRssSessionCreateInput {
readonly browserWindowFromId: (
browserWindowId: number
) => RendererWindow | null;
readonly browserWindowId: number;
readonly getAppMetrics: () => readonly unknown[];
readonly rendererRssApi: RendererProcessRssCaptureApi;
readonly webContentsId: number;
}
export interface RendererWindowRssSessionApi {
create(
input: RendererWindowRssSessionCreateInput
): RendererWindowRssSession;
}
export function createRendererWindowRssSessionApi(): RendererWindowRssSessionApi {
const helpers = {
isValidIdentityId(value: unknown): value is number {
return Number.isSafeInteger(value) && Number(value) > 0;
},
create(
input: RendererWindowRssSessionCreateInput
): RendererWindowRssSession {
if (!helpers.isValidIdentityId(input.browserWindowId)) {
throw new Error('renderer-browser-window-id-invalid');
}
if (!helpers.isValidIdentityId(input.webContentsId)) {
throw new Error('renderer-web-contents-id-invalid');
}
let browserWindow: RendererWindow | null = null;
try {
browserWindow = input.browserWindowFromId(
input.browserWindowId
);
} catch {
throw new Error('renderer-browser-window-missing');
}
if (browserWindow === null) {
throw new Error('renderer-browser-window-missing');
}
if (browserWindow.id !== input.browserWindowId) {
throw new Error('renderer-browser-window-identity-mismatch');
}
const webContents = browserWindow.webContents;
if (
webContents === null ||
typeof webContents !== 'object' ||
webContents.id !== input.webContentsId
) {
throw new Error('renderer-web-contents-identity-mismatch');
}
const windowIdentity = Object.freeze({
browserWindowId: input.browserWindowId,
webContentsId: input.webContentsId,
});
let rendererRss = input.rendererRssApi.create(
helpers.readOsProcessId(webContents),
helpers.readAppMetrics(input.getAppMetrics)
);
let responsiveEvents = 0;
let unresponsiveEvents = 0;
let detached = false;
const callbacks = {
onResponsive(): void {
if (!detached) {
responsiveEvents += 1;
}
},
onUnresponsive(): void {
if (!detached) {
unresponsiveEvents += 1;
}
},
snapshot(): RendererWindowRssSessionMetrics {
return Object.freeze({
responsiveEvents,
rss: rendererRss,
unresponsiveEvents,
windowIdentity,
});
},
};
browserWindow.on('unresponsive', callbacks.onUnresponsive);
try {
browserWindow.on('responsive', callbacks.onResponsive);
} catch (error) {
browserWindow.off('unresponsive', callbacks.onUnresponsive);
throw error;
}
return Object.freeze({
detach(): void {
if (detached) {
return;
}
detached = true;
browserWindow.off('unresponsive', callbacks.onUnresponsive);
browserWindow.off('responsive', callbacks.onResponsive);
},
sample(): RendererWindowRssSessionMetrics {
if (!detached) {
rendererRss = input.rendererRssApi.sample(
rendererRss,
helpers.readOsProcessId(webContents),
helpers.readAppMetrics(input.getAppMetrics)
);
}
return callbacks.snapshot();
},
snapshot: callbacks.snapshot,
});
},
readAppMetrics(
getAppMetrics: () => readonly unknown[]
): readonly unknown[] {
try {
const metrics = getAppMetrics();
return Array.isArray(metrics) ? metrics : [];
} catch {
return [];
}
},
readOsProcessId(webContents: RendererWindowWebContents): unknown {
try {
return webContents.getOSProcessId();
} catch {
return null;
}
},
};
return Object.freeze({ create: helpers.create });
}