-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
fix: useDevicesList
in firefox
#4674
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
fix: useDevicesList
in firefox
#4674
Conversation
Do we need to |
Sure, but by doing this we prevent |
If we have browser-specific stuff here, we should cover that with browser tests. |
I agree that that would be nice. However, you can't grant permissions to camera and microphone in Playwright (microsoft/playwright#5445), which also ties into their lack of support for testing WebRTC in browsers other than Chromium (microsoft/playwright#2973). It's possible to mock the involved API's. However, this kind of circumvents the point of a test, since that test would just be testing our mocks. In my opinion, adding tests here would be too much work for what it's worth (if even possible). @OrbisK let me know what direction you want to take this (closing vs. merging). If you still want to merge, let me know what needs to be done before this can be considered ready to merge. |
Agreed, let's test it manually as best we can 😄 |
@OrbisK What do you think? Do we need to refactor? |
We should definitely not call I wonder if we can detect if we need the stream from something other than the useragent. Like we did with the fallback part of |
What I can do is add a check in async function update() {
if (!isSupported.value)
return
devices.value = await navigator!.mediaDevices.enumerateDevices()
const { state, query } = usePermission(deviceName, { controls: true })
await query()
if (!stream && state.value === "granted" && devices.value.every(device => device.deviceId === "")) {
await startStream();
devices.value = await navigator!.mediaDevices.enumerateDevices()
}
onUpdated?.(devices.value)
stopStream()
} where the EDIT: this way the |
I am not against relying on the useragent if it solves the problem. Is FF the only browser with this behaviour? And why? A general thing with your PR and proposed changes: You should not call composables within functions other than composables. Composables should always be called at setup level. Example // useFoo
function useFoo(s: MaybeRef<string>){
const lowerCase = computed(()=>{
return toValue(s).toLowerCase()
})
watch(()=>s,()=>{
console.log("s changed")
}
return {
lowerCase
}
} // bad usage
const test = ref("abc")
function doSomeThing(){
const { lowerCase } = useFoo(test)
/* ... */
return /* ... */
} doSomeThing() // this will call useFoo the first time -> creating a computed and a watcher
test.value = "123" // this will call console.log once -> expected doSomeThing() // this will call useFoo the first time -> creating a computed and a watcher
doSomeThing() // this will call useFoo the second time -> creating a computed and a watcher again, but does not dispose the first
test.value = "123" // this will call console.log twice Alex has a great video on this: https://www.youtube.com/watch?v=njsGVmcWviY (its about useFetch, but can be applied to any composable) |
From what I can gather, this only happens in FF. And while the issue is described on SO, the only other documentation I could find is the spec which states that you need a live Another solution is to always create a MediaStream before trying to enumerate the devices. This does adhere to the spec, and will work in each browser, but has two important downsides:
My preference goes out the solution I proposed earlier, first trying to enumerate the devices, and if that fails retry with an active MediaStream. This has the benefit of not impacting the majority of users (on Chrome and Safari) with the downsides mentioned above, no explicit useragent checking, still functioning if behavior of any of the browsers change, with the only downside being having to call
I'll make sure to update the PR accordingly. |
I have found a discussion on w3c mediacapture w3c/mediacapture-main#1019 web-platform-tests/interop#849 (comment) > https://bugzilla.mozilla.org/show_bug.cgi?id=1928216 |
Thank you for your commitment. But I think we need to investigate the intended solution for this. From the w3c thread it looks like there should be a proper way to solve this (it also looks like FF is doing it the correct way). I have not had time to look into it yet. Maybe I can do it tomorrow. TLDR: Let me check the Threads. I will get back to you! |
Hey everyone! Just want to propose a solution with adding some flag that we pass to async function ensurePermissions() {
const deviceName = constraints.video ? 'camera' : 'microphone'
if (!isSupported.value)
return false
if (permissionGranted.value && !forceGUMToEnumerate)
return true
const { state, query } = usePermission(deviceName, { controls: true })
await query()
if (state.value !== 'granted' || forceGUMToEnumerate) {
let granted = true
try {
stream = await navigator!.mediaDevices.getUserMedia(constraints)
}
catch {
stream = null
granted = false
}
update()
permissionGranted.value = granted
} |
Before submitting the PR, please make sure you do the following
fixes #123
).Description
Fixes #4672. In Firefox it's not enough to only check if permission has already been granted before devices can be listed. Listing the devices always requires an active stream. This PR makes sure to always create that new stream in Firefox, even when permissions have already been granted. By doing this, listing devices continues to work in FF after a reload.
Additional context
I have refactored some logic into the
getCurrentPermission
function, so that it can be ran only when the browser is not Firefox. This does require anawait
call inside an if condition, which does feel a bit dirty.