Skip to content

Commit 5e3f4ff

Browse files
committed
chore: fix ai-found memory leaks
1 parent 4abc0b2 commit 5e3f4ff

File tree

2 files changed

+50
-13
lines changed

2 files changed

+50
-13
lines changed

src/blocks/CameraSource/CameraSource.ts

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,17 @@ export class CameraSource extends LitUploaderBlock {
7171
private _cameraDevices: CameraDeviceOption[] = [];
7272
private _audioDevices: AudioDeviceOption[] = [];
7373
private _permissionResponses: Partial<Record<(typeof DEFAULT_PERMISSIONS)[number], PermissionStatus>> = {};
74+
private _permissionCleanupFns: Array<() => void> = [];
75+
76+
private readonly _handlePreviewPlay = (): void => {
77+
this._startTimeline();
78+
this.currentTimelineIcon = 'pause';
79+
};
80+
81+
private readonly _handlePreviewPause = (): void => {
82+
this.currentTimelineIcon = 'play';
83+
this._stopTimeline();
84+
};
7485

7586
private timerRef = createRef<HTMLElement>();
7687
private lineRef = createRef<HTMLElement>();
@@ -416,21 +427,24 @@ export class CameraSource extends LitUploaderBlock {
416427
this.video = null;
417428
videoElement.src = videoURL;
418429

419-
videoElement.addEventListener('play', () => {
420-
this._startTimeline();
421-
this.currentTimelineIcon = 'pause';
422-
});
423-
424-
videoElement.addEventListener('pause', () => {
425-
this.currentTimelineIcon = 'play';
426-
this._stopTimeline();
427-
});
430+
this._attachPreviewListeners(videoElement);
428431
} catch (error) {
429432
console.error('Failed to preview video', error);
430433
this.telemetryManager.sendEventError(error, 'camera previewing. Failed to preview video');
431434
}
432435
};
433436

437+
private _attachPreviewListeners(videoElement: HTMLVideoElement): void {
438+
this._detachPreviewListeners(videoElement);
439+
videoElement.addEventListener('play', this._handlePreviewPlay);
440+
videoElement.addEventListener('pause', this._handlePreviewPause);
441+
}
442+
443+
private _detachPreviewListeners(videoElement?: HTMLVideoElement | null): void {
444+
videoElement?.removeEventListener('play', this._handlePreviewPlay);
445+
videoElement?.removeEventListener('pause', this._handlePreviewPause);
446+
}
447+
434448
_retake = (): void => {
435449
this._setCameraState(CameraSourceEvents.RETAKE);
436450

@@ -723,6 +737,7 @@ export class CameraSource extends LitUploaderBlock {
723737
}
724738
const tracks = this.video?.getTracks?.();
725739
tracks?.[0]?.stop();
740+
this._detachPreviewListeners(this.videoRef.value);
726741
this.video = null;
727742

728743
this._makeStreamInactive();
@@ -783,21 +798,40 @@ export class CameraSource extends LitUploaderBlock {
783798

784799
_permissionAccess = async (): Promise<void> => {
785800
try {
801+
this._teardownPermissionListeners();
786802
for (const permission of DEFAULT_PERMISSIONS) {
787803
const response = await navigator.permissions.query({
788804
name: permission as PermissionName,
789805
});
790806
this._permissionResponses[permission] = response;
791807

792808
response.addEventListener('change', this._handlePermissionsChange);
809+
this._permissionCleanupFns.push(() => {
810+
response.removeEventListener('change', this._handlePermissionsChange);
811+
});
793812
}
813+
this._unsubPermissions = () => {
814+
this._teardownPermissionListeners();
815+
};
794816
} catch (error) {
817+
this._teardownPermissionListeners();
795818
console.log('Failed to use permissions API. Fallback to manual request mode.', error);
796819
this.telemetryManager.sendEventError(error, 'camera permissions. Failed to use permissions API');
797820
this._capture();
798821
}
799822
};
800823

824+
private _teardownPermissionListeners(): void {
825+
if (this._permissionCleanupFns.length === 0) {
826+
return;
827+
}
828+
for (const cleanup of this._permissionCleanupFns) {
829+
cleanup();
830+
}
831+
this._permissionCleanupFns = [];
832+
this._unsubPermissions = null;
833+
}
834+
801835
_getPermission = (): void => {};
802836

803837
_requestDeviceAccess = async (): Promise<void> => {
@@ -909,11 +943,9 @@ export class CameraSource extends LitUploaderBlock {
909943
}
910944

911945
_destroy(): void {
912-
for (const permission of DEFAULT_PERMISSIONS) {
913-
this._permissionResponses[permission]?.removeEventListener('change', this._handlePermissionsChange);
914-
}
915-
946+
this._teardownPermissionListeners();
916947
navigator.mediaDevices?.removeEventListener('devicechange', this._getDevices);
948+
this._detachPreviewListeners(this.videoRef.value);
917949
}
918950

919951
override disconnectedCallback(): void {

src/blocks/ExternalSource/ExternalSource.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,11 @@ export class ExternalSource extends LitUploaderBlock {
284284
this._latestSelectionSummary = null;
285285
}
286286

287+
override disconnectedCallback(): void {
288+
super.disconnectedCallback();
289+
this.unmountIframe();
290+
}
291+
287292
override render() {
288293
return html`
289294
<uc-activity-header>

0 commit comments

Comments
 (0)