Skip to content
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

code help #11

Open
1 task
Thorin-Oakenpants opened this issue Sep 25, 2020 · 52 comments
Open
1 task

code help #11

Thorin-Oakenpants opened this issue Sep 25, 2020 · 52 comments

Comments

@Thorin-Oakenpants
Copy link
Contributor

Thorin-Oakenpants commented Sep 25, 2020

rather than pollute other issues: i'll just use this generic one for help with making code cleaner, or making things work

  • item 10: promisifying 5 lots of workers with error handling
@abrahamjuliot
Copy link
Collaborator

item help code: 2

In the above case, you could use collection instead of collection.map(function(fn) { return fn() }) or a 2d array containing the function reference and function arguments.

let collection = [
    [get_A, []],
    [get_B, ["s"]],
    [get_B, ["n"]],
    // etc
]

Promise.all([
    collection.map(function(functionParts) {
        const functionReference = functionParts[0]
        const functionArguments = functionParts[1]
        return functionReference(...functionArguments)
    })
]).then(function(result) {
    collection.forEach(function(currentValue, index) {
        section.push(result[index])
    })
    // do stuff
})

@abrahamjuliot
Copy link
Collaborator

abrahamjuliot commented Sep 26, 2020

item help code: 1

get_computed_styles currently returns void. It just needs to return a promise that resolves a response. You could return a global promise that resolves in then or return promise.all and return a result in then.

return a promise

function get_computed_styles() {
    // wrap everything in this promise
    return new Promise(resolve => {
	
        let styleVersion = type => {
            return new Promise(resolve => {
                //...
            })
        }
		
        Promise.all([
            styleVersion(0),
            styleVersion(1),
            styleVersion(2)
        ]).then(res => {
            // update the dom, etc.

            return resolve(res) // resolve the promise here with res or anything
        }).catch(error => {
            console.error(error)
        })
    })
}

return promise.all

function get_computed_styles() {
    let styleVersion = type => {
        return new Promise(resolve => {
        	// ...
        })
    }
    return Promise.all([
        styleVersion(0),
        styleVersion(1),
        styleVersion(2)
    ]).then(res => {
        // update the dom, etc.
		
        return res // return res or anything here
    }).catch(error => {
        console.error(error)
    })
}

@abrahamjuliot
Copy link
Collaborator

item 3

I might be misunderstanding the question, but to dynamically access the array chk3 where n in chk+n is 3, you can place the property in an object (obj.chk3) and then obj['chk'+n] will return the same value as obj.chk3. Here are 2 examples.

object with numbered properties

This line should be removed: let chk0 = [], chk1 = [], chk2 = [], chk3 = []

let obj = {
	chk0: [ ], // obj.chk0 replaces chk0
	chk1: [ ], // obj.chk1 replaces chk1
	chk2: [ ], // obj.chk2 replaces chk2
	chk3: [ ]  // obj.chk3 replaces chk3
}

// when i == 3, obj[`chk${i}`] or obj['chk'+i] will return the array obj.chk3
let compare = obj[`chk${i}`] // no if else check needed

object with numeric properties

let chk = {
	0: [ ], // chk[0] replaces chk0
	1: [ ], // chk[1] replaces chk1
	2: [ ], // chk[2] replaces chk2
	3: [ ]  // chk[3] replaces chk3
}

// when i == 3, chk[i] will return the array chk[3]
let compare = chk[i] // no if else check needed

@abrahamjuliot
Copy link
Collaborator

abrahamjuliot commented Sep 27, 2020

item 2

Since result is a list containing all the responses, you can iterate over it.

Promise.all([
    get_A(),
    get_B("s"),
    get_B("n"),
    // etc
]).then(function(results) {
    results.forEach(function(currentResult) {
        section.push(currentResult)
    })
    // do stuff
})

@abrahamjuliot
Copy link
Collaborator

abrahamjuliot commented Sep 27, 2020

item 4

https://stackoverflow.com/a/3764557 - this is an interesiting post touching on the subject of how sort is not language-sensitive.

@abrahamjuliot
Copy link
Collaborator

6b10608 :)

@kkapsner
Copy link
Collaborator

Sorry for coming back so late (spare time was rare in the last weeks) - @Thorin-Oakenpants: on what do you still need help?

@kkapsner
Copy link
Collaborator

kkapsner commented Nov 1, 2020

So the idea is

Sounds like a good idea.

I'm using three worker js files, three lots of code to check for them, etc. The CB test only uses one worker js file.

The three different worker types are managed by https://github.com/kkapsner/CanvasBlocker/blob/master/test/navigatorTestWorker.js#L17-L42

https://github.com/kkapsner/CanvasBlocker/blob/master/test/navigatorTestWorker.js#L44-53 deals with the nested workers.

he other thing I was thinking, was using a global worker to return all results: all the language stuff, all the navigator properties etc. These results would go into a global array which each section can then just look up what they need. This way I only need to run a single test on page load, or when I rerun a section or rerun all, I clear the global array and do it again

I would delay such performance optimizations. It would make the code harder to code/read/maintain. Starting with small independent sections is way easier to develop.

Maybe I'm better off with a single worker file per section

Yes - I think so.

@abrahamjuliot
Copy link
Collaborator

abrahamjuliot commented Nov 6, 2020

Enumerating elements or window objects has some gains in singling out (and guessing) versions and/or user triggered changes. I also target certain window objects and perform tampering tests on near every property per prototype.

If a native preference or form of tampering triggers a change to HTMLAnchorElement or HTMLLinkElement, then it might be worth enumerating and checking.

Object.keys(HTMLAnchorElement.prototype)
Object.getOwnPropertyNames(HTMLAnchorElement.prototype) // includes the constructor

@abrahamjuliot
Copy link
Collaborator

ping, charset, hreflang, referrerPolicy

I'm not sure on this. I have not researched or tested it. There might be some unique behavior if we test a link with these attributes.

@abrahamjuliot
Copy link
Collaborator

item 7

8551b01

@abrahamjuliot
Copy link
Collaborator

Yes, that's it.

@abrahamjuliot
Copy link
Collaborator

This will get each property/value

const anchorElement = document.createElement('a') // or a live tag: document.getElementById('test-anchor')
const propertyNames = Object.getOwnPropertyNames(anchorElement.__proto__)
const obj = {}
propertyNames.forEach(propName => {
    const value = anchorElement[propName]
    return (
        obj[propName] = value
    )
})

console.log(obj)

@abrahamjuliot
Copy link
Collaborator

abrahamjuliot commented Nov 26, 2020

Here's how I would style the modal overlay and content.

/* parent overlay */
.modal-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  overflow: auto; /* scroll when content overflows */
  visibility: hidden; /* toggle to show the modal (optionally use display instead)*/
}

/* child cotent */
.modal-content {
  max-width: calc(900px * 0.9); /* 90% of 900px */
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
}

I use a modified pure CSS approach inspired by http://youmightnotneedjs.com/#modal from https://youmightnotneed.com.

<div>
  <!-- Modal Open -->
  <input id="open-modal-1" name="modal-1" type="radio">
  <label for="open-modal-1" onclick="">[open modal-1]</label>
  <!-- Modal Overlay/Modal Close -->
  <label for="close-modal-1" class="modal-overlay" onclick="">
    <!-- Modal Content -->
    <label for="open-modal-1" class="modal-content" onclick="">
      <!-- Close -->
      <input id="close-modal-1" name="modal-1" type="radio">
      <label for="close-modal-1" class="close-btn" onclick="">×</label>
      <!-- Content -->
      <div>modal-1 Content</div>
    </label>
  </label>
</div>

<!--
- modal-1 can be something unique like modal-canvas 
- add more modals by changing all occurances of "modal-1" to modal-a-unique-name in the html and css
-->
input[type="radio"][name^="modal"] {
  display: none;
}
.modal-overlay,
[for^="open-modal"],
[for^="close-modal"] {
  cursor: pointer;
}
.modal-overlay {
  background: rgba(0, 0, 0, 0.9);
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  visibility: hidden;
  overflow: auto;
  z-index: 1000;
}
.modal-content {
  background: #fff;
  border: 1px solid;
  margin: 100px auto;
  padding: 20px 30px;
  max-width: calc(400px * 0.9);
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  cursor: text;
}
.close-btn {
  position: absolute;
  top: 0;
  right: 8px;
  font-size: 30px;
}

/* modal-1 component */
[name="modal-1"]:checked ~ .modal-overlay {
  visibility: visible;
}
[name="modal-1"]:not(:checked) ~ .modal-overlay {
  visibility: hidden;
}

live example: https://jsfiddle.net/oap06rf1/

@Thorin-Oakenpants
Copy link
Contributor Author

now this is what I'm talking about

  • the middle screen overlay: it resizes as you change the browser window size, and the content contains a scrollbar if your height isn't sufficient
  • ^^ that and I quite like the how the modal example above it darkens the document underneath it, not super keen on the actual modality though

Not sure if I should go pure css, because I need the text to trigger js anyway (to populate the overlay content)

@abrahamjuliot
Copy link
Collaborator

modal content scrollbar

To include a scroll bar in the modal content when the max height is reached, you can set max-height: 90% and overflow-y: auto. Here's style similar to the middle screen overlay: https://jsfiddle.net/1bu2xy3a.

`

@abrahamjuliot
Copy link
Collaborator

abrahamjuliot commented Dec 2, 2020

Looking good.

  • Is webkit bs or should it be Gecko + webkit is bs (FxiOS will return bs)?
  • FF68 on Android has Gecko/68.0 (not sure about other versions)
  • I would check for 2 or more instead of double spaces: /\s{2,}/.test(" ")
  • Or, reduce the space checks to 1 line: /^\s{1}|\s{1}$|\s{2,}/.test(' user agent ')
  • Since each if block can trigger bs = true, these can be converted to if...else if blocks.
  • regexing the versions can be overkill (might be noteworthy)

Here's a regex short circuit version of the test, but if blocks read better in my opinion.

function check_basics(str, property) {
    const dynamicReturns = /(undefined (value|string)|blocked|empty string|(^\s{1}|\s{1}$|\s{2,}))/
    const firefox = /\srv:(\d|\.)+\) Gecko\/(20100101|(\d|\.)+) Firefox\//
    const webkit = /webkit/i  // or /gecko\/.+webkit|webkit.+gecko\//i
    const bs = (
        dynamicReturns.test(str) ||
        (property == 'userAgent' ? !firefox.test(str) || webkit.test(str) : false)
    )
    return bs
}

@Thorin-Oakenpants
Copy link
Contributor Author

Thorin-Oakenpants commented Dec 2, 2020

  • the BS detection only runs if isFF = true (and isVer > 77). FxiOS should fail the isFF test, right? isFF uses a Gecko only property, and AFAIK iOS apps have to use the webkit engine - right?
if ("undefined" != typeof InstallTrigger) {isFF = true}
  • I noticed the Fenix (not Fennec) issue and already coded around it - d67aec3
  • I thought about if else, but would it really save any time? I'll do it anyway 👍
  • why check for more than a double space? A double space is already present in longer spaces?
  • This is just a basic sanity check. Anyone not purporting to be FF should be weeded out by this - edit: anything not caught here gets a deeper dive later.

I think regex might be overkill, but I kinda like reducing the lines: however, regex is too confusing for me, and I have to work with the code, so probably better I don't use it

@Thorin-Oakenpants
Copy link
Contributor Author

Thorin-Oakenpants commented Dec 2, 2020

@abrahamjuliot one thing I noticed was that Brave (not chrome or opera) document/iframes sometimes add a double trailing space to userAgent and appVersion - I emailed @pes10k

weird

@abrahamjuliot
Copy link
Collaborator

FxiOS

Correct, it's on Webkit and should fail the Gecko test.

Brave

https://github.com/brave/brave-browser/issues/9190 - document/iframes (+web workers in nightly)

double space already present in longer spaces

That is short and sweet. My thinking is upside down on that one. 🤦‍♂️🙃

@Thorin-Oakenpants
Copy link
Contributor Author

Thorin-Oakenpants commented Dec 2, 2020

^^ ahh, right ... so Brave doesn't protect workers then .. sheesh

And in fact, you could use if (isBrave) {remove all leading, trailing and double spaces until no more double spaces exist} .. bam, randomizing stopped and real value exposed edit: correction: the randomizing is probably neutralized (depends on how far one wants to check what is being done and how to counter it)

@pes10k
Copy link

pes10k commented Dec 2, 2020

Just to clarify, the above is not entirely correct.

  1. There are fingerprinting protection changes that are applied everywhere (removing device info on android devices)
  2. There is a long list of things in blink that are tricky to do in workers. This is one of them (the canvas item you identified before is a second)

If it saves ya'll time to, here are the manual tests we use for checking farbling protections in a number of cases, if it helps

https://dev-pages.brave.software/farbling.html

@pes10k
Copy link

pes10k commented Dec 2, 2020

Will also note we're tracking the UA in workers issue here brave/brave-browser#12392

@Thorin-Oakenpants
Copy link
Contributor Author

Thorin-Oakenpants commented Dec 2, 2020

Thanks @pes10k .. patch those holes man :) I'm adding code here to show that extensions lack APIs to do the job, and that FPing generally needs to be applied internally - it keeps throwing me when I see results like this in Brave (I very occasionally flick open chromium browsers to see what happens)

edit: corrected my statement two posts up: I work on FF stuff, so I'm not particularly looking at Brave when I want to unmask randomizing (to return a static value for all users in that browser) or find a bug/other-method (to get entropy more than just a static value).

PPS: thanks for the brave dev test page

@pes10k
Copy link

pes10k commented Dec 4, 2020

@Thorin-Oakenpants i think thats a known issue; FF doesn't seem too support the API (https://caniuse.com/offscreencanvas). Do you know if they have some other way of doing worker-side canvas operations?

This snowballing test suite is something i maintain for the QA folks at Brave. Im happy to make a change or two if it'd be helpful for ya'll, but would just need specific asks and pointers :)

@Thorin-Oakenpants
Copy link
Contributor Author

No need to make changes just for me - just seems weird that it error'd: i mean even if offscreen canvas in FF had issues (I enabled it: the code is there behind a pref), you'd think that the other worker checks would still pass. But to be fair, it is a Brave test page :)

@Thorin-Oakenpants
Copy link
Contributor Author

@abrahamjuliot I'm trying to color up differences in two strings

example

Wednesday, January 30, 2019, 1:00:00 PM Coordinated Universal Time
Wednesday, 30 January 2019, 1:00:00 pm Coordinated Universal Time

result e.g. bold parts would be red

  • Wednesday, January 30, 2019, 1:00:00 PM Coordinated Universal Time
  • Wednesday, 30 January 2019, 1:00:00 pm Coordinated Universal Time

really struggling to find something - any ideas?

@Thorin-Oakenpants
Copy link
Contributor Author

Thorin-Oakenpants commented Dec 22, 2021

^ only if this is easy to drop in, otherwise it's not worth it. Need to handle RTL etc and not drop any chars but pick up any char diffs

PS: work in progress: https://arkenfox.github.io/TZP/tests/formatting.html

  • not perfect: I cannot simulate resolvedOptions
    • e.g. vi-VI should return vi
    • e.g. zh-TW s/be zh-Hant-TW
    • this even varies by browser: e.g. FF returns en-US, opera en for PluralRules

But the rest looks solid: i.e the preferred language should return the expected formatting. FF unfortunately uses Region, not "app"

@abrahamjuliot
Copy link
Collaborator

abrahamjuliot commented Dec 22, 2021

Here's my shot at this. It should handle line & word diffs fine.

dateString1 = 'Wednesday, January 30, 2019, 1:00:00 PM Coordinated Universal Time'
dateString2 = 'Wednesday, 30 January 2019, 1:00:00 pm Coordinated Universal Time'

getDiffs = ({ stringA, stringB, charDiff = false, decorate = diff => `[${diff}]` }) => {
    const splitter = charDiff ? '' : ' '
    const listA = (''+stringA).split(splitter)
    const listB = (''+stringB).split(splitter)
    const listBWithDiffs = listB.map((x, i) => {
        const matcher = listA[i]
        const match = x == matcher
        return !match ? decorate(x) : x
    })
    return listBWithDiffs.join(splitter)
}

// example with custom options

getDiffs({
    stringA: dateString1,
    stringB: dateString2,
    //charDiff: true // use this option for single words, hashes or number
    decorate: diff => `<span style="color: red">${diff}</span>` // custom function with css style
})

// default example

getDiffs({ stringA: dateString2, stringB: dateString1 })
//=> 'Wednesday, [January] [30,] 2019, 1:00:00 [PM] Coordinated Universal Time'

getDiffs({ stringA: dateString1, stringB: dateString2 })
//=> 'Wednesday, [30] [January] 2019, 1:00:00 [pm] Coordinated Universal Time'

@Thorin-Oakenpants
Copy link
Contributor Author

spiffy .. I'll add it later on, thanks Satan Santa

@Thorin-Oakenpants
Copy link
Contributor Author

Thorin-Oakenpants commented Dec 23, 2021

anyway to make this a friendly function for me?

function colorDiff ( stringA, stringB, decOpen, decClose = "sc", charDiff = false ) {
   // returns the second string with diffs colored compared to first string
   // ^ IDK about you but that should be the other way round

}

let coloredStringExpected = colorDiff(strGot, strExpected, "sb")

anyway this is a bit whack - the second one is fubar, did I do something wrong?

const color_diffs = ({ stringA, stringB, charDiff = false, decorate = diff => `[${diff}]` }) => {
	const splitter = charDiff ? '' : ' '
	const listA = (''+stringA).split(splitter)
	const listB = (''+stringB).split(splitter)
	const listBWithDiffs = listB.map((x, i) => {
		const matcher = listA[i]
		const match = x == matcher
		return !match ? decorate(x) : x
	})
	return listBWithDiffs.join(splitter)
}

function whatever() {
	for (let i=0; i < arrayS.length; i++) {
		// expected vs got
		let strE = arrayS[i], strG = arrayU[i]
		if (strE !== strG) {
			strE = color_diffs({
					stringA: strG,
					stringB: strE,
					//charDiff: true // use this option for single words, hashes or number
					decorate: diff => sb + `${diff}` + sc
			})
			strG = color_diffs({
					stringA: strE,
					stringB: strG,
					decorate: diff => sb + `${diff}` + sc
			})
			diffs.push(s12 + aIndex[i] + sc)
			let str = "<ul><li>"+ strE +"</li><li>"+ strG +"</li></ul>"
			diffs.push(str)
		}
	}
} 

whack

@Thorin-Oakenpants
Copy link
Contributor Author

oh snap, I modified strE and then reused it 🤦‍♀️ sorry Santa

@Thorin-Oakenpants
Copy link
Contributor Author

still something a little off - see the Int;.DateTimeFormat lines
whack

@abrahamjuliot
Copy link
Collaborator

This cleans it up a bit. I added a search in reverse order.

original = "1/30/2019, 1:00 PM Jan 30, 2019, 1:00:00 PM January 30, 2019 at 1:00:00 PM UTC"
changed = "30/01/2019, 1:00 pm 30/01/2019, 1:00:00 pm 30 January 2019 at 1:00:00 pm UTC"

getDiffs = ({ stringA, stringB, charDiff = false, decorate = diff => '['+diff+']' }) => {
    const splitter = charDiff ? '' : ' '
    const listA = (''+stringA).split(splitter)
    const listB = (''+stringB).split(splitter)
    const diffIndexList = []
    listB.forEach((x, i) => {
        const matcher = listA[i]
        const match = x == matcher
        if (!match) {
            diffIndexList.push(i)
        }
        return
    })
    // 🎅🏻
    const listAReversed = [...listA].reverse()
    const listBWithDiffs = [...listB].reverse().map((x, i) => {
        const matcher = listAReversed[i]
        const match = x == matcher
        const index = listB.length-(i+1)
        if (diffIndexList.includes(index)) {
            if (match) {
                diffIndexList.splice(diffIndexList.indexOf(index), 1)
                return x
            }
            return decorate(x)
        }
        return x
    })
    return listBWithDiffs.reverse().join(splitter)
}

getDiffs({ stringA: original, stringB: changed })
//=> "[30/01/2019,] 1:00 [pm] [30/01/2019,] 1:00:00 [pm] [30] [January] 2019 at 1:00:00 [pm] UTC"

getDiffs({ stringA: changed, stringB: original })
//=> "[1/30/2019,] 1:00 [PM] [Jan] [30,] [2019,] 1:00:00 [PM] [January] [30,] 2019 at 1:00:00 [PM] UTC"

@Thorin-Oakenpants
Copy link
Contributor Author

much spiffier and good enough .. will be interesting to see how it handled non-western and LTR

@Thorin-Oakenpants
Copy link
Contributor Author

@abrahamjuliot I got two things for you

ONE

  • enable JS Restrictor (or JS Shelter, IDK what the name is meant to be)
  • set to mode 3
  • run prototypeLies
    here's a pic
    meh

Where, how is are those two Type Errors being thrown? I'd like to trap it for entropy


TWO

  • You don;t need to do this, I'll just explain
  • JS Restrictor at level 3
  • load TZP - it all finishes
  • note the getFloatFrequencyData prototypeLie in the above pic
  • click the manual audio test and it will never finish (and CPU goes nuts)
  • the problem is here 6f62fac
    • the two functions oscillator and hybrid have the same issue
    • the error is caught in a try catch and I return, but this doesn't stop it: how do I break out of this?

@abrahamjuliot
Copy link
Collaborator

If I reload with the console open, the errors should log with function line detail. Looks like the error is trigger in these 2 lines when we access contentWindow.

TZP/js/misc.js

Line 73 in 7039ec7

let contentWindow = iframe.contentWindow

const iframeWindow = contentWindow ? element.contentWindow : context[length]

Looks like this is the tampering code line. 'get contentWindow' should not exist on the prototype.

HTMLIFrameElement.prototype['get contentWindow']

@abrahamjuliot
Copy link
Collaborator

I'll test the audio in a VM and see what I can find. Sounds like memory leak in the extension.

@Thorin-Oakenpants
Copy link
Contributor Author

Thorin-Oakenpants commented Jan 11, 2022

I can't replicate today (version hasn't changed since Nov last year)
hmmm

edit: @abrahamjuliot don't bother testing unless you really want to - I think it's intermittent

@Thorin-Oakenpants
Copy link
Contributor Author

Thorin-Oakenpants commented Feb 7, 2022

//from globals
let zU = "undefined",
	zUQ = "\"undefined\""

function cleanFn(item) {
	// catch strings as strings, tidy undefined, empty strings
	if (typeof item == "number" || typeof item == "bigint") { return item
	} else if (item == zU) {item = zUQ
	} else if (item == "true") {item = "\"true\""
	} else if (item == "false") {item = "\"false\""
	} else if (item == "null") {item = "\"null\""
	} else if (!skipArray && Array.isArray(item)) {
		item = !item.length ? "empty array" : "array"
	} else if (item === undefined || item === true || item === false || item === null) {item += ""
	} else if (item == "") {item = "empty string"
	} else if (typeof item == "string") {
		if (!isNaN(item*1)) {item = "\"" + item + "\""}
	}
	return item
}

@abrahamjuliot - how can I catch an empty array

@abrahamjuliot
Copy link
Collaborator

This should do the trick

a = []
isEmptyArray = a => Array.isArray(a) && !a.length
isEmptyArray(a) // true

@Thorin-Oakenpants
Copy link
Contributor Author

Thorin-Oakenpants commented Feb 7, 2022

I must be tired, I tried Array,isArray etc .. I had put it after checking for "" 🤦 - function edited above

@Thorin-Oakenpants
Copy link
Contributor Author

@Thorin-Oakenpants
Copy link
Contributor Author

@abrahamjuliot .. in looking for paper cuts, I've found (e.g. for larger arrays such as my font list building), using .push.apply is faster than concat - any downsides to this? IIUIC this saves recreating the first array - right?

e.g. a1 = a1.concat(a2) vs a1.push.apply(a1, a2)

@abrahamjuliot
Copy link
Collaborator

Good tip. I have not looked into it, but it makes sense. concat creates a new array per iteration, and push modifies the existing array. There are no downsides unless we want to preserve the original a1 array. We could always map a copy of it.

@Thorin-Oakenpants
Copy link
Contributor Author

I did a bunch of tests (using the font lists from TZP) where for the current OS it creates, one time, a full list and a base (kBaseFonts, whitelist - depending on if TB or not) list

do 12 tests on each, wait a second or two between tests, remove highest + lowest outliers, average

windows FF

  • ignore the parameters: the second one is simulation isTB, the last one is what method to use (concat = 1, push = 2)
testmerge("windows", false, 1)
let array = [0.5118186958134174, 0.3838640218600631, 0.41323485458269715, 0.35500398511067033, 0.36802931129932404, 0.5169266662560403, 0.3777344562113285, 0.5496176811866462, 0.41783202951774, 0.3787560509517789]
10 totalling 3.7609990569762886
testmerge("windows", false, 2)
[0.2252615219913423, 0.23215728206560016, 0.23522206535562873, 0.26229431154206395, 0.26689148554578424, 0.2845139857381582, 0.23343427572399378, 0.21249159425497055, 0.22807090589776635, 0.21938735526055098]
10 totalling 2.174463261384517

old .376 vs new .217 - save .16

mac

testmerge("mac", false, 1)
[0.6969826449640095, 0.5128402896225452, 0.509264709893614, 0.484235652256757, 0.5363369565457106, 0.48704503616318107, 0.4737643115222454, 0.4924084055237472, 0.5228008339181542, 0.48857742780819535]
10 totalling 4.50727362325415
testmerge("mac", false, 2)
[0.3350828983820975, 0.3767128624022007, 0.465336159337312, 0.3703278978355229, 0.4382639126852155, 0.3611335502937436, 0.34887441946193576, 0.3593457601964474, 0.39944333396852016, 0.41272405721247196]
10 totalling 3.53216195339337

old .451 vs new .353 = save .098

of course this is on an old PC (had it's 11th birthday two weeks ago), and macs should be pretty grunty. So who knows, maybe 0.05 ms on a page load (fntLists are only set once)

Anyway, I found a massive perf boost by not writing to the DOM until the end of all the FP data collection (except screen which is real-time on resize event) - ~20ms .. so take that

At the moment the live master (as a local copy) and my refactoring (sooooo many changes - AND additional stuff) both run as file:// and loaded several times to remove any cold starts/latency are approx 310ms master and 235ms (refactor)

master live on my mac as from memory 102ms .. not sure how much more perf I can wring out of this... only to then add new tests (webgl, font variants, etc)

anyway .. happy fat-red-fucker day ... going to go give mrs 🤶 a stuffing 👋

@Thorin-Oakenpants
Copy link
Contributor Author

@abrahamjuliot : is there any real benefit to using promises vs functions : i.e return vs return resolve? I thought I saw some PRs in creepy where you removed some for perf?

e.g. - except get_fonts (which uses get_font_sizes) and get_unicode - all are simple and super fast: e.g. out of say 120ms to run the entire fonts section, 117ms is get_fonts and get_unicode

promise-vs-function

@abrahamjuliot
Copy link
Collaborator

promises vs functions

If there are no event-based operations in the function like fetch, timeouts, listeners, or built in promise-based APIs, I don't see any major benefit to making functions promises, except maybe to try/catch errors with different syntax. In any case, we can place both promise and non-promise functions in a Promise.all or Promise.allSettled and then use Promise.catch.

However, there can be some performance advantages in making functions event-based by using setTimeout(fn, 0). Interesting thread on the subject. That's what I experimented with, but I might tone it down in some areas. It's tricky to find a sweet spot on performance when there's already a lot going on in the event loop. I'm trying to wrap my mind around some of these concepts here.

@Thorin-Oakenpants
Copy link
Contributor Author

there can be some performance advantages in making functions event-based

indeed, I found by doing this - but it was years ago, that it at least gives me more accurate perf results per section

You can also see it here .. and that was only six months ago

IDK if it improves overall perf. I could test. I do know that when RFP is on, I need to run devices very early or it holds everything up - I need to retest that with my refactored version

At the moment the only things that affect timing are IDK what the word, things like media devices (promised), fonts, canvas (promised). I need to learn more about async and stuff - that link looks like a good read

@Thorin-Oakenpants
Copy link
Contributor Author

hey @abrahamjuliot , how do you handle undefined in your JSONs etc. I kinda have two issues

display

  • by this I don't mean the JSON popups
  • I can display strings "null", "undefined", "true", "false" as strings: i.e "\"null"\", so they are visually different from their non-string counterparts, and of course strings in JSON are not an issue
  • so this would make the display of null vs "null" visually different
  • the other one is displaying an empty string (don't want a line to be blank: looks like the test failed)
  • and the other one is displaying a string that resembles a number

I wonder if the display side of things (an empty string aside) is even worth it - the underlying data/hash knows the difference. Everything is parsed thru a display function, so I could split the passed data into value and notation and modify as required

however, undefined will not show up in JSON

I was thinking of trying to catch undefined everywhere and recording it as typeof undefined, but this is problematic and somewhat messy (but not impossible)

is undefined really an issue? If it's not in the JSON, then that's why (or it wasn't a collected metric at the time) - e.g. import JSON into database etc. And a user clicks [10 metrics] and only sees 9 metrics then that's why.

Just wondering what you do?

@abrahamjuliot
Copy link
Collaborator

I see at as non-issue if property gets dropped in JSON. undefined in an array gets converted to null by JSON.stringify, so we would need to check type beforehand. I've just been consuming the values and console logging the raw object, but I've overlooked some surprises by not having the types displayed visually.

@Thorin-Oakenpants
Copy link
Contributor Author

undefined in an array gets converted to null by JSON.stringify

well, fuck

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

4 participants