TZP: chameleon "spoof media devices" breaks devices.js

when chameleon is set to “spoof media devices”, the function never returns (and the section never finishes, so the overall FP never happens), and I can’t seem to catch it

any ideas? @sereneblue @kkapsner @abrahamjuliot @ beers @ coffee @ sleep

grrr

function get_media_devices() {
	return new Promise(resolve => {

		function finish(result) {
			dom.eMDList.style.color = zshow
			return resolve("media_devices:"+result)
		}
		if ("mediaDevices" in navigator) {
			// enumerate
			let str="", pad=13, strPad=""
			try {
				navigator.mediaDevices.enumerateDevices().then(function(devices) {
					let arr = []
					// enumerate
					try {
						devices.forEach(function(d) {
							arr.push(d.kind)
							str += (d.kind+": ").padStart(pad)+d.deviceId
							if (d.groupId.length > 0) {
								strPad = ("group: ").padStart(pad)
								str += "<br>"+strPad+d.groupId
							}
							if (d.label.length > 0) {
								strPad = ("label: ").padStart(pad)
								str += "<br>"+strPad+d.label
							}
							str += "<br>"
						})
					} catch(e) {
						console.debug(e.name, e.message)
					}
					// output list
					if (str.length == 0) {str = "none"}
					dom.eMDList.innerHTML = str

					// count each kind
					let pretty = [], plain = [], rfphash = ""
					if (arr.length > 0) {
						arr.sort()
						let map = arr.reduce((acc, e) => acc.set(e, (acc.get(e) || 0) + 1), new Map());
						arr = [...map.entries()]
						// build pretty/plain
						for (let i=0; i < arr.length; i++) {
							let data = arr[i],
								item = data[0],
								itemcount = data[1]
							pretty.push(item + s7 +"["+ itemcount +"]" + sc)
							plain.push(item +","+ itemcount)
						}
						pretty = pretty.join(" ")
						str = plain.join(";")
						rfphash = sha1(str)
					} else {
						pretty = "none"
					}
					// RFP
					if (rfphash == "6812ba88a8eb69ed8fd02cfaecf7431b9c3d9229") {
						dom.eMD.innerHTML = pretty + (isTB ? tb_red : rfp_green)
					} else {
						dom.eMD.innerHTML = pretty + (isTB ? tb_red : rfp_red)
					}
					finish(str)
				})
				.catch(function(e) {
					dom.eMDList.innerHTML = e.name +": "+ e.message
					dom.eMD.innerHTML = e.name
					finish(e.name)
				})
			} catch(e) {
				dom.eMDList.innerHTML = zB0
				dom.eMD.innerHTML = zB0 + (isTB ? tb_red : rfp_red)
				finish(zB0)
			}
		}	else {
			dom.eMDList = zD
			dom.eMD.innerHTML = zD + (isTB ? tb_green : rfp_red)
			finish(zD)
		}
	})
}

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 1
  • Comments: 16 (6 by maintainers)

Commits related to this issue

Most upvoted comments

@sereneblue

This might fix it at line 64.

value: () => new Promise(async resolve => {
	let devices = await _enumerateDevices()
	
        //....

	return resolve(devices)
}

Note, enumerateDevices should return a Function instead of an AsyncFunction. The above should handle this too.

// Chameleon
navigator.mediaDevices.enumerateDevices.constructor.name // returns "AsyncFunction"

// native
navigator.mediaDevices.enumerateDevices.constructor.name // returns "Function"

That works! Thanks, I’ll push out an update ASAP.

@sereneblue

This might fix it at line 64.

value: () => new Promise(async resolve => {
	let devices = await _enumerateDevices()
	
        //....

	return resolve(devices)
}) // <--- oops forgot the ) 

Note, enumerateDevices should return a Function instead of an AsyncFunction. The above should handle this too.

// Chameleon
navigator.mediaDevices.enumerateDevices.constructor.name // returns "AsyncFunction"

// native
navigator.mediaDevices.enumerateDevices.constructor.name // returns "Function"

@sereneblue It appears the issue is applied when the document contains or injects an iframe.

With spoof on, in the console, enter the code below on these pages:

No iFrames

iFrames

console.log('pending')
d = await navigator.mediaDevices.enumerateDevices()
console.log('resolved')
console.log(d)

hmmm … just tested - it throws an error and there is no spoofing: it also causes other functions to either not lie (or my code sucks), such as timezone + language spoofing was never done (or picked up) <- most likely in the order in which I ask for them

I also tried with various combos of settings: disabling almost all (except user agent cuz I don’t want to set that up again, i’m lazy) of them except spoof devices

yes, the UUID is chameleon

error

line 6 is minified

Fixed in v0.21.15.

@Thorin-Oakenpants enumerateDevices is called from generic.js and devices.js.

Ahh yes. Instead of running each modular js file as it downloads, I now wait for everything to be ready. So as soon as the first js file (generic.js) lands, I use the time to set a few things up

function run_once() {
	// lets get started while we wait for all the JS modules to arrive

	// immutable
	if ((location.protocol) == "file:") {isFile = true; note_file = " [file:/]"}
	if ((location.protocol) == "https:") {isSecure = true}
	if ("brave" in navigator) {isBrave = true}
	if ("undefined" != typeof InstallTrigger) {isFF = true}
	if (!isFF) {runS = false} // sim = FF only

	//prime up some JS functions
	try {
		navigator.mediaDevices.enumerateDevices().then(function(devices) {})
	} catch(e) {}
	try {
		let v = speechSynthesis.getVoices()
	} catch(e) {}
}

What I found was that the devices section perf was always high (of course other functions all trying to run at the same time don’t help), and I found that “priming” or “warming” up enumerate devices sped it up immensely (i.e the reported speed)

And on a whim I decided to do the same to speech engines, which solved why I got three returns on page load (see earlier post)