From d7ac865b98253b6170f63d8b579ccb89a9f8a026 Mon Sep 17 00:00:00 2001 From: librelad Date: Fri, 29 May 2026 23:46:04 +0100 Subject: [PATCH] fix(webui): guard app-detail listener binds against per-navigation leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AppTabbedManager.initialize() re-runs on every /app navigation (its 'initialized' flag is never set true), and setupURLMonitoring() / setupTaskEventListeners() add window listeners (popstate, taskCreated/ Completed/Updated) unguarded — so each app-detail visit stacked another listener set. Bind them once via a _listenersWired flag (mirrors the existing _watchdogStarted guard). Pre-existing leak surfaced by the feature-migration review. Co-Authored-By: Claude Opus 4.8 Signed-off-by: librelad --- .../js/components/app/app-tabbed-manager.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/containers/libreportal/frontend/js/components/app/app-tabbed-manager.js b/containers/libreportal/frontend/js/components/app/app-tabbed-manager.js index f6fe575..8cae508 100755 --- a/containers/libreportal/frontend/js/components/app/app-tabbed-manager.js +++ b/containers/libreportal/frontend/js/components/app/app-tabbed-manager.js @@ -708,11 +708,16 @@ class AppTabbedManager { this.switchTab('tasks'); } - // Monitor URL changes for app navigation - this.setupURLMonitoring(); - - // Listen for task creation events - this.setupTaskEventListeners(); + // Monitor URL changes for app navigation + listen for task events. These + // add window-level listeners (popstate, taskCreated/Completed/Updated), so + // bind them ONCE for the lifetime of this singleton — initialize() re-runs + // on every /app navigation (the `initialized` flag is never set true), and + // without this guard each visit stacked another set of window listeners. + if (!this._listenersWired) { + this._listenersWired = true; + this.setupURLMonitoring(); + this.setupTaskEventListeners(); + } // Set initial active tab (only if no task parameter) if (!taskId) {