WebUI: Fix memory leak in context menus

This PR fixes a memory leak in context menus. Previously, for some reason, each menu retained references to its target elements without utilizing them further. Since the targets property was accessible/reachable from the root (window object), these references persisted even after the elements were removed from the DOM, preventing them from being garbage collected.
It's easily reproducible - just add a decent amount of torrents, switch between categories multiple times, then capture heap/detached elements snapshot in the Memory tab (Chrome dev tools). The number of detached elements will continue to increase after each category switch and they won't be cleaned up.
[More context](https://github.com/qbittorrent/qBittorrent/pull/22220/files#r1941137796)

PR #22234.
This commit is contained in:
skomerko 2025-02-08 10:51:21 +01:00 committed by GitHub
commit 93925042dd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -67,9 +67,6 @@ window.qBittorrent.ContextMenu ??= (() => {
// option diffs menu
this.menu = $(this.options.menu);
this.targets = (this.options.targets.length > 0)
? Array.from(document.querySelectorAll(this.options.targets))
: [];
// fx
this.fx = new Fx.Tween(this.menu, {
@ -169,15 +166,18 @@ window.qBittorrent.ContextMenu ??= (() => {
}
addTarget(t) {
if (t.hasEventListeners)
return;
// prevent long press from selecting this text
t.style.userSelect = "none";
this.targets[this.targets.length] = t;
t.hasEventListeners = true;
this.setupEventListeners(t);
}
searchAndAddTargets() {
document.querySelectorAll(this.options.targets).forEach((target) => { this.addTarget(target); });
if (this.options.targets.length > 0)
document.querySelectorAll(this.options.targets).forEach((target) => { this.addTarget(target); });
}
triggerMenu(e, el) {
@ -199,8 +199,7 @@ window.qBittorrent.ContextMenu ??= (() => {
// get things started
startListener() {
/* all elements */
for (const el of this.targets)
this.setupEventListeners(el);
this.searchAndAddTargets();
/* menu items */
this.menu.addEventListener("click", (e) => {
@ -222,6 +221,7 @@ window.qBittorrent.ContextMenu ??= (() => {
// hide on body click
$(document.body).addEventListener("click", () => {
this.hide();
this.options.element = null;
});
}