Conversation
|
@R-Delfino95 is attempting to deploy a commit to the Mux Team on Vercel. A member of the Team first needs to authorize it. |
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
|
|
||
| nativeTracks.addEventListener('change', onChange); | ||
| nativeTracks.addEventListener('addtrack', onAddTrack); | ||
| nativeTracks.addEventListener('removetrack', onRemoveTrack); |
There was a problem hiding this comment.
Native track listeners stored locally, never removable
Medium Severity
The onChange, onAddTrack, and onRemoveTrack listeners are extracted into named local variables but never stored anywhere accessible for later removal. These references are lost when getVideoTracks/getAudioTracks returns, so the listeners on nativeTracks can never be cleaned up. Since these closures capture media (a strong reference to the custom element), they keep the media element alive as long as the native track list exists — undermining the WeakRef work done elsewhere in this PR.
Additional Locations (1)
There was a problem hiding this comment.
This case is for Safari only:
These listeners (onChange, onAddTrack, onRemoveTrack) are anonymous and are attached to nativeTracks, which is the track list of the native
Therefore, this isn't a leak because the listener target (nativeTracks) shares the same lifecycle as the element itself. This differs from listeners on globalThis or the custom element itself, which do require explicit cleanup


Description
This PR focuses on lifecycle management and breaking reference cycles that prevent garbage collection in
mux-player-react.Changes by Package:
castable-videodisconnectedCallbackand adestroy()method to properly unmount the element and its properties during casting.!this.isConnectedcheck to theremotegetter. This prevents the accidental creation of a newRemotePlaybackinstance during the disconnection process (e.g., when other mixins callsuper.disconnectedCallback()and access properties that hit this getter).custom-media-elementremoveEventListenercalls were added.disconnectedCallbackand re-created inconnectedCallback(if already initialized), skipping a fullinit().#isInitremainstrueduring disconnect. This prevents other mixins in the class hierarchy from inadvertently re-triggering#init()via property access (likethis.nativeElorthis.currentTime) while the component is being torn down.esbuildcompiles private fields intoWeakMapentries. If a closure capturesthis, it can create an ephemeron cycle that V8 cannot resolve. These fields are now nulled indisconnectedCallbackto break the cycle.media-tracksmediareferences withWeakRef(media)in track lists, rendition lists, and mixin init functions. Access sites now usemedia?.deref()with optional chaining.change,addtrack,removetrack) into named references to ensure they are properly removed during cleanup.Related Issues
Relates to Issue #1235
Testing Instructions
To verify these changes and the overall fix for memory leaks, please refer to the manual testing tool and the integration example provided in:
👉 PR #1285 - Manual Testing Tool & Examples
The tool allows for local testing using Yalc to link
media-elementsandmedia-chromewithout relying on NPM, and includes a script to restore the environment once finished.Note
Medium Risk
Touches custom element lifecycle and Cast remote playback teardown; mistakes could cause missed events or regress casting/track sync. Changes are mostly cleanup/guard logic but span multiple core media abstractions.
Overview
Fixes several lifecycle-related memory leaks across media components by ensuring teardown happens on disconnect and by breaking strong reference cycles.
In
castable-video, theremotegetter now avoids instantiatingRemotePlaybackwhile disconnected, anddisconnectedCallback()explicitly destroys the remote instance and clears storedprivateProps.RemotePlaybackgains adestroy()method to remove text track and cast controller listeners and clear cast element refs when the media element is removed mid-cast.In
custom-media-element, listener setup is refactored into#setupListeners()with a trackedslotchangehandler;disconnectedCallback()now removes all shadowRoot listeners, disconnects observers, removes cloned children, and nulls#nativeElto allow GC, whileconnectedCallback()re-attaches listeners on remount.In
media-tracks, track/rendition objects now storemediaasWeakRefand all access sites usemedia?.deref()with null-guards, preventing track lists from keeping media elements alive after removal.Written by Cursor Bugbot for commit e7a26e7. This will update automatically on new commits. Configure here.