# vendor_flavors

> Which of ten browser-specific global objects exist on the page, such as window.chrome or the globals iOS browsers inject.

Source: https://whiskerjs.com/signals/vendor-flavors/ (last updated 2026-10-26)

**vendor_flavors at a glance (from the library code, signalsVersion 9)**

| Field | Value |
| --- | --- |
| Component name | `vendor_flavors` |
| In the visitorId | yes |
| structuredId group | [browser (engine and build)](https://whiskerjs.com/docs/identifiers/#structuredid) |
| 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](https://whiskerjs.com/whats-new/) |

> **In plain words:**
>
> 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](https://whiskerjs.com/browsers/chrome-desktop/), [Edge](https://whiskerjs.com/browsers/edge/) and [Brave](https://whiskerjs.com/browsers/brave/) usually show `chrome`; [Safari](https://whiskerjs.com/browsers/safari-macos/) usually shows `safari`; [Firefox](https://whiskerjs.com/browsers/firefox/) usually shows `none`; [iOS browsers](https://whiskerjs.com/browsers/safari-ios/) 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`](https://whiskerjs.com/docs/integrity/#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.

## Related pages

- [vendor](https://whiskerjs.com/signals/vendor/): The vendor string.
- [browser_class](https://whiskerjs.com/signals/browser-class/): Uses window.chrome as one input.
- [headless_markers flag](https://whiskerjs.com/docs/integrity/#headless_markers): Fires for a desktop Chrome user agent without window.chrome.
- [Safari and every iOS browser](https://whiskerjs.com/browsers/safari-ios/): Where injected globals matter most.
