TZP: trap resource:// blocking

I make sure several global vars are set before we start fingerprinting: such as isFF, isOS, isTB, isEngine, prototype lies. Unfortunately, isTB never resolves with cydec (at least in total mode), and thus the promise never happens and we have no output: section re-runs work sweet šŸ˜ƒ

note: isTB is initially an empty string = ā€œā€, and I set it once to a boolean. I do not have any instances of (!isFF). TB shouldnā€™t have any extensions that cause the script to break, so Iā€™m happy to trap this and change isTB into false based on assumptions

note: this is the only resource:// that is set via code. The only other one is an image set via css: and that function works on the same assumption: if itā€™s there = TB, if it isnā€™t = not TB.

here is the code

function get_isTB() {
	return new Promise(resolve => {
		if (isFF && isTB === "") {
			try {
				let t0 = performance.now()
				console.debug("a")
				let css = document.createElement("link")
				console.debug("b")
				css.href = "resource://torbutton-assets/aboutTor.css"
				css.type = "text/css"
				css.rel = "stylesheet"
				console.debug("c")
				document.head.appendChild(css)
				console.debug("d")
				css.onload = function() {
					isTB = true
					debug_page("TB","     resource:// = aboutTor.css")
					if (logPerf) {debug_perf("[yes] isTB [immutables]",t0)}
					return resolve("done")
				}
				css.onerror = function() {
					isTB = false
					if (logPerf) {debug_perf("[no] isTB [immutables]",t0)}
					return resolve("done")
				}
				document.head.removeChild(css)
			} catch(e) {
				console.error("get_isTB", e.name, e.message)
				return resolve("error")
			}
		} else {
			return resolve("done")
		}
	})
}

everything debugs - a,b,c,d, no error is trapped. So I need a timeout

These three run a single Promise.all -> prototypeLies promise -> kick it all off

Firefox

perf detail: global
             isOS [immutables]:    0 ms |    0 ms
         isEngine [immutables]:    0 ms |    1 ms
        [no] isTB [immutables]:    1 ms |    2 ms

TB with RFP disabled so I can get some perf

perf detail: global
             isOS [immutables]:    1 ms |    1 ms
         isEngine [immutables]:    1 ms |    2 ms
       [yes] isTB [immutables]:   17 ms |   19 ms

So Iā€™m happy to add a timeout, but not sure of the syntax to add that. That is e.g. after 100ms set isTB true and resolve as rejected or something - help appreciated

About this issue

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

Most upvoted comments

OK, Iā€™ll rerun prototype for section reruns as well šŸ˜ƒ

I notice the delay (I use F5) on file:// ā€¦ have to close and restart nightly after a dozen or of them. Not noticed it on http ā€¦ so will be interesting when I upload all my changes: so much stuff here

I changed up line-height and scrollbar info by only reporting a narrower range of known zoom values: since FF recently added 400 and 500% - that and I think a lot of values on Linux will change with native theming disabled. In order to test it I made it update-able in real time: that was fun.

big-n-small

closing: have sorted out timing out in promises, and overall hardened all the isThings used throughout my code: promising them once on each pageload

@sereneblue FYI: on Cydec

  • cydec also tries to mess with error strings (which is not a real solution IMO, since what errors are generated and how they are, can be different depending on the script: i.e itā€™s a game of whack a mole). The errorā€™s generated in my test are from passing a string to a new function (thanks to abraham)
const newFn = x => typeof x != 'string' ? x : new Function(x)()

//and here are the three error strings used to check if you are firefox
try {newFn("alert('A)")} catch(e) {res.push(e.name +": "+ e.message)}
try {newFn(`null.value = 1`)} catch(e) {res.push(e.name +": "+ e.message)}
try {let test = newFn("let a = 1_00_;")} catch(e) {res.push(e.name +": "+ e.message)}
  • cydec also mangles math function results: which is the fourth isFF test (the one which also sets the isEngine)

That cydec really is a piece of work: right now Iā€™m returning [object Object] in navigator.connection because of itā€™s hijinks

Correct. Hereā€™s an example of Privacy Possom starting with none on page load and then 10 lies on re-run.

possom2

Yes, thatā€™s it. The timeout resolve only resolves the promise if the promise is still pending.


If you like, you can reduce some of the nested bracket layers by checking if !(isFF && isTB === ""), resolve early if true, and then the else block can be omitted since a false condition will execute everything else. It reads fine either way though.

function get_isTB() {
    return new Promise(resolve => {
        // resolve early if condition is true
        if (!(isFF && isTB === "")) {
            return resolve("done")
        }
        // else
        try {

        } catch (e) {

        }
    })
}

ā€¦And, an arrow function can remove the nested function brackets (minimal nesting).

const get_isTB = () => new Promise(resolve => {
    // resolve early if condition is true
    if (!(isFF && isTB === "")) {
        return resolve("done")
    }
    // else
    try {

    } catch (e) {

    }
})

you mean like this? I think I get it now, the resolve can only be resolved once, then the promise is over, including the timeout?

function get_isTB() {
	return new Promise(resolve => {
		if (isFF && isTB === "") {
			try {
				// extensions can cause this to never promise
					// FF is ~1ms, TB is ~20ms
				var timeout = window.setTimeout(function(){
					// assume false: if actually TB, you're a CRAZY edge case
					isTB = false
					console.debug("get_isTB timed out")
					resolve("get_isTB timed out")
				}, 100)
				//setTimeout(() => resolve("promise failed"), 100)

				let t0 = performance.now()
				// etc