| Component name | vendor_flavors |
|---|---|
| In the visitorId | yes |
| structuredId group | browser (engine and build) |
| Hashing | plain value; a digest only when longer than 128 characters or not printable ASCII |
| Excluded under | no browser class |
| Inner timeout | 1000 ms |
| Added in | signalsVersion 1 |
Some browsers leave their own named objects on every web page, such as chrome in Chrome or the helper objects that Chrome and Firefox add on iPhone. WhiskerJS checks which of ten such names exist. It is a simple way to tell apart browsers that otherwise look alike, including different apps on iOS that all use Apple’s engine, and in-app browsers.
What it reads
Whether each of these globals exists on the window and is not null or undefined: __crWeb, __firefox__, __gCrWeb, chrome, opr, safari, samsungAr, ucweb, webkit, yandex.
How the technique works
For each name, in the fixed order above, WhiskerJS checks name in window and that the value is not null or undefined. A throwing lookup counts as absent. The found names are joined with commas; if none is found the value is none. Only presence is checked: the objects are not called or inspected.
Why it helps
The globals come from the browser build, not the device: chrome in Chromium browsers, opr in Opera, yandex in Yandex Browser, samsungAr in some Samsung Internet builds, safari in Safari, webkit in some WebKit-based in-app browser views, and __crWeb, __gCrWeb and __firefox__ injected by Chrome and Firefox on iOS. That separates browser apps that share an engine and would otherwise give very similar results.
Stability
Stable for a given browser build. It changes when a browser adds or removes one of these globals in an update. Pages that define a global with one of these names themselves (rare) would change it too.
Because the value lists names rather than reading the objects, a browser that changes what is inside window.chrome between versions does not change it; only a global appearing or disappearing does.
Browser support and what blocks it
Every browser can be checked; the value just differs. Desktop Chrome, Edge and Brave usually show chrome; Safari usually shows safari; Firefox usually shows none; iOS browsers can show their injected helpers. No browser class excludes it.
Privacy
The names describe the browser app, not the user. No global is read beyond checking that it exists.
How WhiskerJS handles noise and failures
| Status | ruleId | When |
|---|---|---|
ok |
a comma-separated list, or none |
|
excluded |
caller_excluded |
you excluded it |
timed_out |
timeout, aborted |
budget or abort |
Errors during a lookup are caught per name. Never hashed in practice. The related headless_markers flag fires when the user agent is desktop Chrome but window.chrome is missing.
Example value
In the container test, headless Chromium and Firefox reported none and headless WebKit reported safari.
Questions and answers
Can vendor_flavors tell Chrome on iPhone from Safari?
Often yes. Chrome and Firefox on iOS inject their own globals (for example __gCrWeb or __firefox__) into pages, which Safari does not have, even though all of them run on WebKit.
What does none mean?
None of the ten globals exists. It is an ok value; headless Chromium without window.chrome reports it, for example.