Compare commits

..
Author SHA1 Message Date
Lotus CIandClaude Opus 5.5 46d6570df3 fix(calls): lift CallControl's listener limit so ended calls can be freed (#316)
CI / Build & Quality Checks (pull_request) Successful in 3m16s
CI / Trigger Desktop Build (pull_request) Skipped
CI / Docker image build & smoke test (pull_request) Skipped
CI / Secret scan (gitleaks) (pull_request) Successful in 9s
CI / Playwright smoke (e2e) (pull_request) Successful in 18m30s
More than 10 call features subscribe to CallControl's state_update at once
(call bar, controls, hotkeys, AFK, muted-talk, join/leave sounds,
announcements, rejoin, policy toast, glance, desktop thumbbar/SMTC). At
the default limit Node's events logged a MaxListenersExceededWarning, and
the console kept the warning's emitter (this CallControl, and through it
the whole Element Call document) alive: ~11 MB per call per the audit.
setMaxListeners(100); these are real, unsubscribed-on-cleanup listeners.

Not reproduced in this environment (headless web logs no warning: fewer
subscribers without the desktop hooks); this is the fix the issue
measured (swallowing the warning freed all CallControls).

Fixes #316

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
2026-10-17 18:57:22 -04:00
2 changed files with 9 additions and 8 deletions
+2 -8
View File
@@ -334,9 +334,7 @@ function InviteNotifications() {
if (quietActive) return;
// [Gitea #324] The in-app toast (focused window) needs no browser
// permission; only the OS notification does.
if (showNotifications && (document.hasFocus() || notificationPermission('granted'))) {
if (showNotifications && notificationPermission('granted')) {
notify(newCount);
}
if (notificationSound && inviteSoundId !== 'none') {
@@ -643,11 +641,7 @@ function MessageNotifications() {
if (quietActive) return;
// [Gitea #324] notify() shows an in-app toast while the window is focused,
// which needs no browser permission; only its OS-notification path does.
// Gating both on the permission left people who declined it (most web
// users) with a ping sound and nothing on screen.
if (showNotifications && (document.hasFocus() || notificationPermission('granted'))) {
if (showNotifications && notificationPermission('granted')) {
const avatarMxc =
room.getAvatarFallbackMember()?.getMxcAvatarUrl() ?? room.getMxcAvatarUrl();
notify({
+7
View File
@@ -91,6 +91,13 @@ export class CallControl extends EventEmitter implements CallControlState {
constructor(state: CallControlState, call: ClientWidgetApi, iframe: HTMLIFrameElement) {
super();
// [Gitea #316] More than 10 call features subscribe to state_update at once
// (call bar, controls, hotkeys, AFK, muted-talk, join/leave sounds,
// announcements, rejoin, policy toast, glance, desktop thumbbar/SMTC …).
// Hitting the default limit logged a MaxListenersExceededWarning, and the
// console then kept this CallControl — and through it the whole Element Call
// document — alive: ~11 MB leaked per call. These are not leaks, so lift it.
this.setMaxListeners(100);
this.state = state;
this.call = call;