WICG / ua-client-hints

Wouldn't it be nice if `User-Agent` was a (set of) client hints?
https://wicg.github.io/ua-client-hints/
Other
591 stars 77 forks source link

Define how to order browser versions. #209

Open jyasskin opened 3 years ago

jyasskin commented 3 years ago

Servers and web pages often want to serve different content to "browser A older than version N" vs "browser A at version N or newer". Some do this by comparing strings, which leads version 100 to be treated as older than version 99. The current specification defines version as a string (and propagates that type to the header), which increases the chance of making this mistake.

If we keep the string type, we should define how servers and scripts are expected to order these, so that UAs can assign their own version numbers in a way that servers and scripts get the right results.

If we change the type to a number (probably a decimal), we should probably also grease that by sometimes sending leading 0s.

miketaylr commented 3 years ago

This feels related to #196 as well.

amtunlimited commented 3 years ago

A couple of possible options:

UA-CH-Full-Version = major-version "." minor-version
major-version = integer
minor-version = *chr
domenic commented 3 years ago

My personal proposal:

Browser version should keep its current meaning, as an opaque string that incorporates a variety of concerns like marketing and displaying to users and sending diagnostics and so on.

However, for the specific case of working around known bugs or doing polyfill.io-style feature checks, the spec should also define a "comparable significant version" and maybe "comparable full version", and define them as integers. These integers are required to be comparable so that "newer" versions (in an implementation-defined sense) are larger integers than older ones.

I'd envision in practice we'd see comparable significant versions like 88, 89, 90, etc. for Chrome and Firefox, and 121, 122, 130, 134 for Safari. And comparable full versions would have more digits, e.g. 91000044460003 for Chrome full version 91.0.4446.3.

You'd slot this into the current API as comparableVersion in NavigatorUABrandVersion, comparableUAFullVersion in UADataValues, maybe vn= parameters in the header UA brand lists, and maybe a vn= parameter on top of the sh-string that Sec-CH-UA-Full-Version has.

erik-anderson commented 3 years ago

This feels related to #196 as well.

It's a bit different. Windows apps hit this problematic pattern all the time as well of writing version checks that don't fully consider all of the permutations they need to handle correctly between version components (e.g. 6.1 < 7.0 though many developers may do something like "if version.minor >= 1", see it working, and assuming it's right. What Windows eventually moved to was APIs that allow checking if a release is a specific version or higher.

If we think Windows' solution makes sense, I suppose we could extend that model to UA Client Hints, e.g. by defining a response header that could include a brand and full version list as an input (e.g. Accept-CH-UA-Compatible-Input: "Chromium"; v="90.0.100.5", "Awesome Browser"; v="5.1.2.4530") and cause the browser to send a Sec-CH-UA-Compatible: ?1 request header on subsequent requests depending on if it matched any of the list of conditions.

You could also have a dedicated JS API that takes a similar list and also returns a boolean value.

This is somewhat similar to issue #54 where I wanted to explore more significant ways of discouraging sites from "rusting shut" on browser checks, but this doesn't carry the ongoing cost on site devs.

Browser version should keep its current meaning, as an opaque string that incorporates a variety of concerns like marketing and displaying to users and sending diagnostics and so on.

Unfortunately the version info isn't particularly opaque with sites deriving meaning from it in the variety of ways you call out, including things like analytics and marketing checks. Ideally we'd have a more clearly lit path than two similar surfaces that may not be clear to a site developer about which to choose or why, but I understand the desire to easily pump a similar looking version string into existing systems.

A decimal representation as Jeffrey calls out may be a reasonable way (if my more opaque query approach I just outlined doesn't seem promising), though it will carry all of the same challenges I outlined in issue #196.

miketaylr commented 3 years ago

What's the risk of changing the type from DOMString / sf-string to decimal, instead of adding new version properties? I wonder if adding new stuff just increases the risk of making more mistakes (but I could be wrong).

Existing code (which there shouldn't be much of using UA-CH APIs) should just work*, and not suffer from the problematic string comparison down below... my understanding of https://tc39.es/ecma262/#sec-abstract-relational-comparison is that when comparing a number and a string, the string is converted to a number (or undefined).

Some imaginary code assuming brand is 89:


> brand.version < "9" // here it's a number
false

> brand.version < "9"  // here it's a string
true

(*famous last words)
domenic commented 3 years ago

On the JavaScript side, changing to a number is relatively safe as you point out. A potential strangeness is that floating-point precision is not the best match for some browser versions, e.g. Chrome full version 91.0.4446.3 might be tranlsated to 91.044463 which will really be represented as 91.0444629999999932579157757572829723358154296875 to JavaScript (so, e.g., fullVersion === 91.04446299999999 will also be true, since 91.04446299999999 === 91.044463).

On the headers side, it likely depends on what server-side processing is doing. E.g. the equivalent of

parse_as_sh_string(headers->get("sec-ch-ua-full-version"));

might fail in some way (e.g. returning a null value or throwing an exception) if the format switches from string to decimal.