Two service-worker leaks. Running jobs were only bounded by their watchdog, so a command flood could hold many timers and results for up to JOB_TIMEOUT_MS; cap concurrent jobs at 32 and reject beyond that, since a clear error beats an unresponsive worker. Window aliases were never removed, so storage kept an entry for every window the user had ever renamed. Prune aliases against the live window set on list, rename, and close rather than only on close, because windows also disappear without going through windows.close. Also extract the repeated job summary in perf.status into a helper.
75 lines
2.6 KiB
TypeScript
75 lines
2.6 KiB
TypeScript
import { webExtApi as api } from '../browser-api';
|
|
import type { WindowCreateData } from '../types';
|
|
import { getAliases } from '../core';
|
|
import { CommandGroup } from '../classes/CommandGroup';
|
|
import type { CommandEntry } from '../classes/CommandGroup';
|
|
import type { WindowsRenameArgs, WindowsCloseArgs, WindowsOpenArgs } from '../types';
|
|
|
|
export class WindowsCommands extends CommandGroup {
|
|
readonly namespace = "windows";
|
|
readonly commands: Record<string, CommandEntry> = {
|
|
"windows.list": () => this.windowsList(),
|
|
"windows.rename": (a: WindowsRenameArgs) => this.windowsRename(a),
|
|
"windows.close": (a: WindowsCloseArgs) => this.windowsClose(a),
|
|
"windows.open": (a: WindowsOpenArgs) => this.windowsOpen(a),
|
|
};
|
|
|
|
private async activeWindowIds(): Promise<Set<number>> {
|
|
const windows = await api.windows.getAll({});
|
|
return new Set(windows.map(w => w.id).filter(id => typeof id === "number"));
|
|
}
|
|
|
|
private async pruneAliases(activeIds?: Set<number>): Promise<Record<string, string>> {
|
|
const aliases = await getAliases();
|
|
const liveIds = activeIds || await this.activeWindowIds();
|
|
const pruned: Record<string, string> = {};
|
|
let changed = false;
|
|
for (const [id, alias] of Object.entries(aliases)) {
|
|
if (liveIds.has(Number(id))) {
|
|
pruned[id] = alias;
|
|
} else {
|
|
changed = true;
|
|
}
|
|
}
|
|
if (changed) await api.storage.local.set({ windowAliases: pruned });
|
|
return pruned;
|
|
}
|
|
|
|
private async windowsList() {
|
|
const windows = await api.windows.getAll({ populate: true });
|
|
const activeIds = new Set(windows.map(w => w.id).filter(id => typeof id === "number"));
|
|
const aliases = await this.pruneAliases(activeIds);
|
|
return windows.map(w => ({
|
|
id: w.id,
|
|
alias: aliases[w.id] || null,
|
|
focused: w.focused,
|
|
state: w.state,
|
|
tabCount: (w.tabs || []).length,
|
|
}));
|
|
}
|
|
|
|
private async windowsRename({ windowId, name }: WindowsRenameArgs) {
|
|
const aliases = await this.pruneAliases();
|
|
aliases[windowId] = name;
|
|
await api.storage.local.set({ windowAliases: aliases });
|
|
return { windowId, name };
|
|
}
|
|
|
|
private async windowsClose({ windowId }: WindowsCloseArgs) {
|
|
await api.windows.remove(windowId);
|
|
const aliases = await this.pruneAliases();
|
|
if (windowId in aliases) {
|
|
delete aliases[windowId];
|
|
await api.storage.local.set({ windowAliases: aliases });
|
|
}
|
|
return { windowId };
|
|
}
|
|
|
|
private async windowsOpen({ url }: WindowsOpenArgs) {
|
|
const createData: WindowCreateData = { focused: true };
|
|
if (url) createData.url = url;
|
|
const w = await api.windows.create(createData);
|
|
return { id: w.id };
|
|
}
|
|
}
|