From 65b4c556f2fee8ba95c4ca35c5e0a4aee91b0c72 Mon Sep 17 00:00:00 2001 From: yilin ren Date: Tue, 25 Nov 2025 16:35:30 -0800 Subject: [PATCH 1/6] fix: add secure context checks for navigator.mediaDevices Prevents crashes in non-HTTPS environments by checking window.isSecureContext before accessing navigator.mediaDevices API. --- src/room/Room.ts | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/room/Room.ts b/src/room/Room.ts index e62d407d95..5ff3142a7e 100644 --- a/src/room/Room.ts +++ b/src/room/Room.ts @@ -276,9 +276,11 @@ class Room extends (EventEmitter as new () => TypedEmitter) const abortController = new AbortController(); // in order to catch device changes prior to room connection we need to register the event in the constructor - navigator.mediaDevices?.addEventListener('devicechange', this.handleDeviceChange, { - signal: abortController.signal, - }); + if (window.isSecureContext && navigator.mediaDevices?.addEventListener) { + navigator.mediaDevices.addEventListener('devicechange', this.handleDeviceChange, { + signal: abortController.signal, + }); + } if (Room.cleanupRegistry) { Room.cleanupRegistry.register(this, () => { @@ -1615,7 +1617,9 @@ class Room extends (EventEmitter as new () => TypedEmitter) window.removeEventListener('beforeunload', this.onPageLeave); window.removeEventListener('pagehide', this.onPageLeave); window.removeEventListener('freeze', this.onPageLeave); - navigator.mediaDevices?.removeEventListener('devicechange', this.handleDeviceChange); + if (window.isSecureContext && navigator.mediaDevices?.removeEventListener) { + navigator.mediaDevices.removeEventListener('devicechange', this.handleDeviceChange); + } } } finally { this.setAndEmitConnectionState(ConnectionState.Disconnected); @@ -2500,7 +2504,9 @@ class Room extends (EventEmitter as new () => TypedEmitter) new LocalVideoTrack( publishOptions.useRealTracks ? ( - await window.navigator.mediaDevices.getUserMedia({ video: true }) + window.isSecureContext && window.navigator.mediaDevices + ? await window.navigator.mediaDevices.getUserMedia({ video: true }) + : new MediaStream() ).getVideoTracks()[0] : createDummyVideoStreamTrack( 160 * (participantOptions.aspectRatios[0] ?? 1), @@ -2528,7 +2534,11 @@ class Room extends (EventEmitter as new () => TypedEmitter) }), new LocalAudioTrack( publishOptions.useRealTracks - ? (await navigator.mediaDevices.getUserMedia({ audio: true })).getAudioTracks()[0] + ? ( + window.isSecureContext && navigator.mediaDevices + ? await navigator.mediaDevices.getUserMedia({ audio: true }) + : new MediaStream() + ).getAudioTracks()[0] : getEmptyAudioStreamTrack(), undefined, false, From e225b255234085ba2124a55bf3b76651642a5761 Mon Sep 17 00:00:00 2001 From: yilin ren Date: Tue, 25 Nov 2025 16:49:07 -0800 Subject: [PATCH 2/6] fix: add secure context checks for navigator.mediaDevices - Add window.isSecureContext checks before accessing navigator.mediaDevices - Prevent crashes in non-HTTPS environments when mediaDevices is undefined - Add checks for devicechange event listeners (add/remove) - Add checks for getUserMedia calls (video/audio) - Gracefully fallback to empty MediaStream in non-secure contexts --- src/room/Room.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/room/Room.ts b/src/room/Room.ts index 5ff3142a7e..3c88a2732a 100644 --- a/src/room/Room.ts +++ b/src/room/Room.ts @@ -2503,10 +2503,9 @@ class Room extends (EventEmitter as new () => TypedEmitter) }), new LocalVideoTrack( publishOptions.useRealTracks - ? ( - window.isSecureContext && window.navigator.mediaDevices - ? await window.navigator.mediaDevices.getUserMedia({ video: true }) - : new MediaStream() + ? (window.isSecureContext && window.navigator.mediaDevices + ? await window.navigator.mediaDevices.getUserMedia({ video: true }) + : new MediaStream() ).getVideoTracks()[0] : createDummyVideoStreamTrack( 160 * (participantOptions.aspectRatios[0] ?? 1), @@ -2534,10 +2533,9 @@ class Room extends (EventEmitter as new () => TypedEmitter) }), new LocalAudioTrack( publishOptions.useRealTracks - ? ( - window.isSecureContext && navigator.mediaDevices - ? await navigator.mediaDevices.getUserMedia({ audio: true }) - : new MediaStream() + ? (window.isSecureContext && navigator.mediaDevices + ? await navigator.mediaDevices.getUserMedia({ audio: true }) + : new MediaStream() ).getAudioTracks()[0] : getEmptyAudioStreamTrack(), undefined, From ee8ce91fb70276d448c1dcf9f3443ff8487971b0 Mon Sep 17 00:00:00 2001 From: yilin ren Date: Thu, 27 Nov 2025 11:15:43 -0800 Subject: [PATCH 3/6] refactor: use optional chaining for mediaDevices calls instead of isSecureContext checks --- src/room/Room.ts | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/src/room/Room.ts b/src/room/Room.ts index 3c88a2732a..9fcfb35f71 100644 --- a/src/room/Room.ts +++ b/src/room/Room.ts @@ -276,11 +276,9 @@ class Room extends (EventEmitter as new () => TypedEmitter) const abortController = new AbortController(); // in order to catch device changes prior to room connection we need to register the event in the constructor - if (window.isSecureContext && navigator.mediaDevices?.addEventListener) { - navigator.mediaDevices.addEventListener('devicechange', this.handleDeviceChange, { - signal: abortController.signal, - }); - } + navigator.mediaDevices?.addEventListener?.('devicechange', this.handleDeviceChange, { + signal: abortController.signal, + }); if (Room.cleanupRegistry) { Room.cleanupRegistry.register(this, () => { @@ -1617,9 +1615,7 @@ class Room extends (EventEmitter as new () => TypedEmitter) window.removeEventListener('beforeunload', this.onPageLeave); window.removeEventListener('pagehide', this.onPageLeave); window.removeEventListener('freeze', this.onPageLeave); - if (window.isSecureContext && navigator.mediaDevices?.removeEventListener) { - navigator.mediaDevices.removeEventListener('devicechange', this.handleDeviceChange); - } + navigator.mediaDevices?.removeEventListener?.('devicechange', this.handleDeviceChange); } } finally { this.setAndEmitConnectionState(ConnectionState.Disconnected); @@ -2503,9 +2499,7 @@ class Room extends (EventEmitter as new () => TypedEmitter) }), new LocalVideoTrack( publishOptions.useRealTracks - ? (window.isSecureContext && window.navigator.mediaDevices - ? await window.navigator.mediaDevices.getUserMedia({ video: true }) - : new MediaStream() + ? (await window.navigator.mediaDevices?.getUserMedia?.({ video: true }) ?? new MediaStream() ).getVideoTracks()[0] : createDummyVideoStreamTrack( 160 * (participantOptions.aspectRatios[0] ?? 1), @@ -2533,9 +2527,7 @@ class Room extends (EventEmitter as new () => TypedEmitter) }), new LocalAudioTrack( publishOptions.useRealTracks - ? (window.isSecureContext && navigator.mediaDevices - ? await navigator.mediaDevices.getUserMedia({ audio: true }) - : new MediaStream() + ? (await navigator.mediaDevices?.getUserMedia?.({ audio: true }) ?? new MediaStream() ).getAudioTracks()[0] : getEmptyAudioStreamTrack(), undefined, From cc226fbdfb08bd48ac0c4c58a4e1773b77692d7d Mon Sep 17 00:00:00 2001 From: yilin ren Date: Thu, 27 Nov 2025 11:26:13 -0800 Subject: [PATCH 4/6] refactor: use optional chaining for mediaDevices calls instead of isSecureContext checks --- src/room/Room.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/room/Room.ts b/src/room/Room.ts index 9fcfb35f71..c42934e4e2 100644 --- a/src/room/Room.ts +++ b/src/room/Room.ts @@ -2499,7 +2499,8 @@ class Room extends (EventEmitter as new () => TypedEmitter) }), new LocalVideoTrack( publishOptions.useRealTracks - ? (await window.navigator.mediaDevices?.getUserMedia?.({ video: true }) ?? new MediaStream() + ? ((await window.navigator.mediaDevices?.getUserMedia?.({ video: true })) ?? + new MediaStream() ).getVideoTracks()[0] : createDummyVideoStreamTrack( 160 * (participantOptions.aspectRatios[0] ?? 1), @@ -2527,7 +2528,8 @@ class Room extends (EventEmitter as new () => TypedEmitter) }), new LocalAudioTrack( publishOptions.useRealTracks - ? (await navigator.mediaDevices?.getUserMedia?.({ audio: true }) ?? new MediaStream() + ? ((await navigator.mediaDevices?.getUserMedia?.({ audio: true })) ?? + new MediaStream() ).getAudioTracks()[0] : getEmptyAudioStreamTrack(), undefined, From abc9a7b05db17a562e40b5a3f8e13c7681c63ecc Mon Sep 17 00:00:00 2001 From: yilin ren Date: Thu, 27 Nov 2025 11:31:11 -0800 Subject: [PATCH 5/6] refactor: use optional chaining for mediaDevices calls instead of isSecureContext checks --- src/room/Room.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/room/Room.ts b/src/room/Room.ts index c42934e4e2..5c2489e426 100644 --- a/src/room/Room.ts +++ b/src/room/Room.ts @@ -2499,7 +2499,9 @@ class Room extends (EventEmitter as new () => TypedEmitter) }), new LocalVideoTrack( publishOptions.useRealTracks - ? ((await window.navigator.mediaDevices?.getUserMedia?.({ video: true })) ?? + ? + ( + (await window.navigator.mediaDevices?.getUserMedia?.({ video: true })) ?? new MediaStream() ).getVideoTracks()[0] : createDummyVideoStreamTrack( @@ -2528,7 +2530,9 @@ class Room extends (EventEmitter as new () => TypedEmitter) }), new LocalAudioTrack( publishOptions.useRealTracks - ? ((await navigator.mediaDevices?.getUserMedia?.({ audio: true })) ?? + ? + ( + (await navigator.mediaDevices?.getUserMedia?.({ audio: true })) ?? new MediaStream() ).getAudioTracks()[0] : getEmptyAudioStreamTrack(), From 4438fcbe047ced57b696a70b5a9c700fe54ab805 Mon Sep 17 00:00:00 2001 From: yilin ren Date: Thu, 27 Nov 2025 11:34:35 -0800 Subject: [PATCH 6/6] refactor: use optional chaining for mediaDevices calls instead of isSecureContext checks --- src/room/Room.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/room/Room.ts b/src/room/Room.ts index 5c2489e426..29e805f57b 100644 --- a/src/room/Room.ts +++ b/src/room/Room.ts @@ -2499,8 +2499,7 @@ class Room extends (EventEmitter as new () => TypedEmitter) }), new LocalVideoTrack( publishOptions.useRealTracks - ? - ( + ? ( (await window.navigator.mediaDevices?.getUserMedia?.({ video: true })) ?? new MediaStream() ).getVideoTracks()[0] @@ -2530,10 +2529,8 @@ class Room extends (EventEmitter as new () => TypedEmitter) }), new LocalAudioTrack( publishOptions.useRealTracks - ? - ( - (await navigator.mediaDevices?.getUserMedia?.({ audio: true })) ?? - new MediaStream() + ? ( + (await navigator.mediaDevices?.getUserMedia?.({ audio: true })) ?? new MediaStream() ).getAudioTracks()[0] : getEmptyAudioStreamTrack(), undefined,