I will refer to screen + available screen as SCREEN. These are always the same to hide docker/taskbar measurements
I will refer to inner + outer as WINDOW. These are always the same to hide chrome measurements
This proposal is to handle SCREEN differently from WINDOW, but still base it on WINDOW. This allows us to reduce SCREEN buckets
current:
all four (inner, outer, screen, available screen) = tied to inner
this means the possible resolutions for SCREEN are affected by the user and newin (+LB), and even worse if the user has disabled LBing (accidental resizes are not hard to do with a windowed object)
the possible buckets are either our LBing stepping or almost every combo without LBing
even in a perfect world, we still have entropy in SCREEN because newwin sizes vary (8 or 9 buckets?)
new:
inner is not exactly a stable metric - users resize, snap to edges in tile managers, toggle chrome bits, zoom, etc (I am excluding full screen). So at worst at least some scripts are highly likely IMO to discard this as a linkability factor since the IP is different. It's just not a good metric. Screen is a much more stable metric
WINDOW = newwin + LB sizes
SCREEN = step it based on WINDOW
by still being tied to inner, this means entropy mathematically cannot be increased, but it will be decreased in scripts that rely only screen
we step on width AND height, i.e try to make the inner res fit the screen res, but if we can't, no big deal (see below)
we can use 4 or 5 common screen sizes, e.g. 2k, 3k, 4k (maybe 8k)
the common sizes could really be anything we want, but we can choose common ones if it makes it seem more natural/real
it doesn't matter if SCREEN ends up making the inner look weird, e.g partially offscreen due to physical rotation to portrait, or if the screen is actually bigger than our max values because we're lying, who cares - did anyone seriously believe our figures before? Did anyone really believe inner has always equaled outer? We lie, bug deal :)
full screen is an issue with or without this change and is a different problem
note/bonus: it will upset scripts that use screen = inner logic as a point of difference to detect TB in JS (and making that harder is always fun)
orientation (matchmedia/css)
since screen width + height always match our array of 3 or 4 common landscape resolutions (this is desktop), then we need to also make sure that orientation is hooked into this (or just always report landscape to save some perf)
I think there's currently an RFP orientation patch (really old) which kinda doesn't make sense?. By always being landscape the new RFP orientation patch would make sense
As per earlier discussion to "scream" about this louder @tom @richard
Designs
Child items 0
Show closed items
No child items are currently assigned. Use child items to break down this issue into smaller parts.
when a user zooms in, the measurements are lower, so we will always fit within our set screen sizes
e.g. (RFP on using max 1000)
at 100% 1000 = 1000
at 200% 1000 = 500
System scaling has the same effect, but we can safely assume users' setups are within human eyeball comfort levels
When a user zooms out though, the measurements can quickly escalate to very large numbers
at 30% 1000 = 3333
at 30% 2560 = 8533 (2560 being my full screen real
at 30% 2400 = 8000 (2400 being maximized letterboxed viewport on my machine)
Does this matter? My best guess is no. As long as we have an upper limit of say 8k, then any rare'ish anomalies are just lies and we lie all the time (see OP re if the inner is outside the bounds of the screen)
@donuts Technically, this is not a screensize - this is purely a fingerprint spoof and doesn't have any UX or inner-window real estate or usability concerns. The rest of the issues labelled screensize look good
When maximized, scripts currently can extrapolate with a high degree of success IMO, your real screen - i.e it's going to be inner + some fuzzy number allowing for docker/taskbar and some chrome elements with some combos being much more prevalent - this is easier to determine the higher the resolution. The problem is the width is almost a dead giveaway to provide the height. Those with LBing have another parameter to make it harder.
And in full-screen we report it truthfully.
So we could in fact handle SCREEN in those two cases differently, at the very least in FS (remembering that the benefit comes from less advanced scripts that only use screen for linking/tracking) - since we're no longer making it match WINDOW - ideas spring to mind (ultimately I think we could randomize it with or w/o protecting the seed just to make life so much harder). Food for thought.
Also, with my foresight, I asked kkapsner who develops CanvasBlocker, to build this (a sliding screen return based on inner) into that extension, some 3+ years ago - and no one AFAIK, has ever reported a single problem - so that augurs well.
By their own admission (because, science + math), FPJSpro say (scroll half way down)
Window size is the opposite. The dimensions of a randomly resized browser window are much more unique than those of the screen size. However, stability is low and depends on user behavior. Some users don’t resize their browsers, while others do it frequently.
Well, they're unique not unique for us, because newwin + LBing. The point is that scripts will relie a lot more on screen than window, and we can reduce the buckets for that
I shall check back once a year for progress .. don't forget to vote
now upstream with FPP has a random seed ... see ideas in #42235, if feasible (@tjr, is this possible?), rather than reduce the screen/available screen lies, it would be nice to randomize them individually
so take inner, add an allowance for some chrome
then add between 1-100px for screen width
then add between 1-100px for screen height
then for available screen width or height remove between 30-60px and the other axis return same as screen (i.e randomize the left/right vs top/bottom and height of the taskbar/docker)
^ 100 x 100 x 30 = 300,000 combinations for noise
obviously also protect other methods (css/@media etc)
// actual resolutions reported (i.e not necessarily native)// https://firefoxgraphics.github.io/telemetry/#view=monitors// https://data.firefox.com/dashboard/hardware// https://gs.statcounter.com/screen-resolution-stats/desktop/worldwideconstresCommon=[// anything smaller than these would have no change with expanding max width from 1000// statc, FF, dashboard[3840,2160],// 1.4%[2880,1800],// 2.1%[2560,1440],// 2.5%, 2.2%[1920,1200],// 0.8%, 1.7%[1920,1080],// 23.0%, 49.6%[1680,1050],// 1.4%, 2.2%[1600,900],// 3.4%, 6.3%[1536,864],// 10.8%, [1440,900],// 6.0%, 2.6%[1366,768],// 16.9%, 21.5%[1360,768],// 1.0%, 1.3%[1280,1024],// 1.8%, 3.2%[1280,800],// 1.6%, 1.7%[1280,720],// 6.2%[1024,768],// 1.6% 2.0%/* ignore // none of these change if we up max width from 1000 to 1400 // and none are likely to system scale bigger [ 768, 1024], // 1.8% // portrait 1.33 [ 810, 1080], // 1.7% // portrait 1.33 [1024, 1366], // 0.7% // portrait 1.33 [ 820, 1180], // 0.6% // portrait 1.43 [ 834, 1194], // 0.5% // portrait 1.43 //*/// 82.3%, 94.0%/* ignored [ 360, 800], // 1.0% [ 800, 600], // 0.9% //*/// other // 15.9%, 6.1%]
native screen res
// these ones we apply scaling to generate more possible// screen resolutions which we merge with common// https://en.wikipedia.org/wiki/List_of_common_resolutionsconstresNative=[[1024,768],[1024,800],[1080,1200],[1120,832],[1152,720],[1152,768],[1152,864],[1280,720],[1280,854],[1280,960],[1280,1024],[1366,768],[1440,900],[1440,960],[1400,1050],[1440,1080],[1600,768],[1600,1024],[1600,1200],[1600,1280],[1680,1050],[1776,1000],[1792,1344],[1800,1440],[1856,1392],[1920,1080],[1920,1200],[1920,1280],[1920,1440],[2048,1080],[2048,1152],[2048,1280],[2160,1440],[2304,1440],[2256,1504],[2560,1440],[2576,1450],[2560,1080],[2560,1600],[2560,1700],[2560,1920],[2560,2048],[2736,1824],[2880,900],[2880,1620],[2880,1800],[2880,1920],[2800,2100],[3000,2000],[3072,1920],[3200,1800],[3200,2048],[3200,2400],[3240,2160],[3440,1440],[3840,1600],[3840,2160],[3840,2400],[4096,2160],[4096,2304],[4096,3072],[4480,2520],[4500,3000],[5120,1440],[5120,2880],[5120,3200],[5120,4096],[6000,4500],[6016,3384],[6400,4096],[6400,4800],[6480,3240],[7680,4320],[7680,4800],[8192,4320],[8192,4608],[8192,8192],[10240,4320],[15360,8640],[16384,8640],]
we return the inner value (viewport) for all when in FS
otherwise screen we return the stepped size at zoom 100% that outer would fit in, modified by zoom (I think we divide it)
I think I need to articulate all that more clearly. If you zoom in and out on TZP, gecko changes the screen+outer+inner measurements - chrome it only changes inner. Might be nice if gecko did the same, sure makes life easier, maybe we just do the same since we expect users to be at 100% zoom
buckets: 1920x1080 is the minimum. If you open smaller than our max 1400 x 1000, e.g. at 1400 x 800 then you already stick out - the buckets don't really matter, they are not entropy except that scripts do like to grab screen only rather than inner since inner is not stable
so I would just go for 1920 x 1080+ 2k + 4k + 8k - and always landscape since this is desktop only
This is not urgent, the only reasons I want to do it is
reduced buckets (to 1 I hope, if users do not resize) if only screen is collected (and not inner)
compat, not that we break anything but inner == outer == available == screen == red flags with anti-bit scripts
The more I think about it, the less I like the idea of randomizing (using a protected seed etc) the screen size metrics returned. It's adding a layer of complexity I can do without (e.g, zooming), and I am very weary of using it too soon (IANAE but I think I see issues with workers etc upstream). And it makes it hard to verify that it's working (e.g. on TZP). We can still do it in the future where we randomize using a set of values - see below, e.g. 8 heights for chrome, taskbar positon, taskbar thickness, etc - and TZP would only need to check the values were ones we support, so we could still check for health.
anyway...
so the logic would be to use inner to decide what to set the others to. There is no entropy gain. This would reduce the buckets if scripts do not use inner+outer as they are not stable. And by adding at least some chrome dimensions (except if in full screen) then we look normal: inner == outer (and in fact inner == screen, etc) would no longer be the case and can't be used against us - yes I have seen scripts using this
in future we could make these variables random: e.g.
windows taskbar auto-hiding or not (these are the only options in windows 11)
windows taskbar could be windows 10 height (yes it's different)
windows taskbar could be vertical and use windows 10 widths
windows chrome_height could be 1 of 8 values
based on real world values: menubar on/off, toolbar on/off, normal/touch density
^ i.e 2 x 2 x 2 = 8
if (windows){// values are windows 11, system scaling 100%, zoom 100%, dPR = 1// we can choose what we like but using common real world values is// never a bad idea - mozilla should have some telemetrychrome_width=isfullscreen?0:16chrome_height=isfullscreen?0:93// FYI 121 without toolbargap_width=100// optional gaps?gap_height=50docker_width=0// taskbar/docker, call it what you willdocker_height=48// taskbar/docker, call it what you will
So the logic for windows, for example, would be
read INNER width/height e.g. 1400w x 900h
if android (for now)
status quo: everything (screen, available, outer) == INNER
then if fullscreen
screen, outer == INNER
available screen = INNER - docker
then if windowed
set OUTER width/height
INNER width + chrome_width
INNER height + chrome_height
so e.g. 1416w x 993h
set MIN_SCREEN width/height (to determine screen)
this is used to set SCREEN
width must be at least INNER width + chrome_width (1416) + docker_width (0)
height must be at least INNER height + chrome_height (993) + docker_height (48)
which is 1041 which fits inside 1080 [1] FYI, tight but doable
so we have 1416 x 1041
set SCREEN
use MIN_SCREEN to determine this
so 1416 x 1041 fits into 1920 x 1080
set AVAIL_SCREEN
SCREEN - docker
so in this case 1080 - 48 = 1032
so windowed we would end up as
inner 1400 x 900
outer 1416 x 993
screen 1920 x 1080
available 1920 x 1032
[1] we should or could add an artificial gap that one would expect on a windowed app
so our final screen sizes seem kosher
in the example above if we added a height gap of 50 our min screen height would be 1091 which means 1080 is too small
in the above example if the toolbar was always visible then that adds 28 (my machine + density etc) which then brings us very close to being the actual available height
i don't think many people will have windows apps that use almost all the height (or width) - they would just tile them, or be maximized
it's not going to add any entropy and would only split the screen buckets a tiny bit
those at 900 height which is out max, would differ to everyone else: big deal
or we could start with 2560 x 1440 as our minimum size bucket
just pondering these things as I type, it's not super important, we're lying but at least it's plausible and allows us to add in some randomizing down the track
So I would to add this behind a pref privacy.resistFingerprinting.experimental_screen so we can test and play
ToDo:
decide on values per desktop platform
@fkilic@tjr might be able to lookup some common results in their FPP telemetry
FYI: when i built the newwin simulation a few years ago to determine our new max sizes, I did measure everything possible, but this may have changed due to UI changes/design
how does it handle zoom (numbers can blow in/out very quickly) ...
gecko changes all of them
yup, gecko changes everything and it's not linear for some variable such as chrome/taskbar (yes, 110% chrome at 7 x 102 is correct - when maximized it reports -7 wtf)
click me for details
so as you can see my monitor is 2560 x 1440 - not super huge, there are higher resolutions. So if I was maximized or full screen I'm going to be 2400 x 1400 if letterboxed. Not that people would zoom to 30%, but we'll need to handle sizes larger than our biggest screensize bucket, and how do we handle large portrait which could easily fall into "not fitting" - assuming we want to ensure both width + height fit?
Inner is 2560 x 1323/2560 x 1355 depending on the display I measure this, but in any case 85px height on Linux (without bookmarks bar).
The bookmarks bar is something we could randomize, as well as having the activity bar (or maybe I could measure some other values on KDE, for example). We could use some distribution with probabilities that aren't uniform .
As for macOS, I think we'll need some information from an actual user.
I've used macOS so few times I still haven't understood how maximizing windows on macOS works (sometimes the bottom bar disappears, some other times it doesn't?).
And it seems to me that on macOS you don't really maximize windows.
Anyway, the hardware is very standard for macOS, so we could use the values from the built-in displays of laptops and iMacs, rather than the more standard full HD and 4k (even though they're going to be standard-ish with mac minis).
for down the track. There are a bunch of variables: density x 3, menubar, toolbar, zoom, system scaling. In my newwin simulation (which i only used based on windows), I had max and min values (to test the newwin max/min results) - its a large range
I think your values for Windows are okay, we have some values I provided for Linux (I think we can use GNOME values for all: Debian's popcon seems to confirm, even though there are other more used distro, I don't know if they have something similar).
So, now we need values for macOS, and to decide the final strategy, esp. for scaling (we need somebody to test) and zoom (use the same for zoom 100%, and then adjust the values?).
To sum up, if I understood correctly, your base suggestion is:
Inner: remains as it is
Outer: same width as inner, add the values we found for height (93px on Windows, 85px on Linux, TBD on macOS)
Screen: add taskbar size to outer to find a minimum screen resolution, and take the first one it fits from a small set (1920x1080, twice and 4 times as that for 4k and 8k respectively)
Available? We need to fit something from outer and screen. Should we hardcode, or randomize within certain intervals (GNOME has the activity bar on the top, Windows the taskbar usually on the bottom, macOS has the top bar and the dock, etc...)?
we still use spoofed chrome values to decide the minimum screen, regardless of being in fullscreen - we just do not add them to create the outer if in fullscreen
Yep. It isn't the most realistic value for a non-maximized window, but I guess it's fine to keep it in this way and re-evaluate if we start randomizing.
I am not sure if I'm going to be able to keep detailed track of the work here, but I always want to help if I can. If you need a seed for the randomness see if you can get a reference to the CookieJarSettings for the document you are randomizing - that object will hold a key that has all the same properties as CanvasRandomization that you would want. (Different in sessions, changed on New Identity, cleared with Site Data is cleared, different per top level domains...)
It's possible you'll want to hack something here - but since Canvas Randomization is always going to be on in Tor Browser by default - maybe you won't need to.
screen (and make available screen the same) is a really good metric to randomize - see #42235 - it's impossible (when not full screen) to detect it's random - so this will really mess with scripts. But since those values change with zoom, we need to figure that part out
ok, just saw the issue title has the word randomize in it. This mostly isn't about randomizing (although that was mentioned when upstream got a per origin seed) but rather this is about stepping the screen sizes to reduce buckets (and look more normal) - we even built into into an extension 4 years ago as an experiment
Thorinchanged title from desktop: randomize screen+available screen based on inner to desktop: step screen spoofs and return realistic outer based on inner window
changed title from desktop: randomize screen+available screen based on inner to desktop: step screen spoofs and return realistic outer based on inner window
So, to sum up, we have these values we already spoof:
Value
Relevant RFPTargets
Formula
Example
Notes
inner
-
1400 x 900
Nothing to do. LB will protect this, and users who don't want LB will be protected by newwin rounding (at most of its capabilities, see #43205 (closed))
outer
WindowOuterSize, WindowScreenXY
inner + constantOffset1
1416 x 993
Spoof the existence of the chrome and window decoration (platform specific: Windows includes decorations on outer, Linux doesn't)
screen
ScreenRect, CSSDeviceSize
pick the first one that is bigger than outer
1920 x 1080
See below for resolutions to use
available
ScreenAvailRect
screen - constantOffset2
1920 x 1032
Size of docks and other elements of the shell. This offset is also platform-dependent.
Resolutions we should use:
full HD: 1920x1080
4k (2x full HD on each direction, so 3840x2160)
8k (2x 4k on each direction, so 7680x4320).
Corner case: should we double each size in an exponential fashion, or shall we just stop at 8k for now?
TODO: define the platform-specific constants for chrome and shells.