mirror of
https://github.com/feder-cr/invisible_playwright.git
synced 2026-07-04 10:52:24 +02:00
Split pure-config into invisible-core; keep back-compat shims
The seed->fingerprint->prefs logic, binary download, and proxy/geo config now live in a standalone invisible-core package (zero Playwright), so a profile manager can reuse it without pulling Playwright in. The wrapper depends on invisible-core and replaces the moved modules with full-alias shims: existing imports (public API, submodules, private names) and isinstance checks keep working unchanged. tests/test_backcompat.py locks that contract (6 guards). Bumps to 0.3.0.
This commit is contained in:
parent
d434ee4059
commit
76e41e2789
36 changed files with 136 additions and 11109 deletions
|
|
@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|||
|
||||
[project]
|
||||
name = "invisible-playwright"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
description = "Playwright wrapper for a patched Firefox with deterministic stealth profile."
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.11"
|
||||
|
|
@ -20,13 +20,11 @@ classifiers = [
|
|||
"Programming Language :: Python :: 3.12",
|
||||
]
|
||||
dependencies = [
|
||||
# Pure config (seed -> fingerprint -> prefs, binary download, proxy, geo) lives
|
||||
# in invisible-core since the 2026-07-03 split; it pulls platformdirs/requests/
|
||||
# maxminddb/tzdata/tqdm/pywin32 transitively. The wrapper only adds Playwright.
|
||||
"invisible-core @ git+https://github.com/feder-cr/invisible_core.git",
|
||||
"playwright>=1.40,<1.61",
|
||||
"platformdirs>=4",
|
||||
"requests[socks]>=2.31",
|
||||
"maxminddb>=2.2",
|
||||
"tzdata>=2024.1",
|
||||
"tqdm>=4.66",
|
||||
"pywin32>=306; sys_platform == 'win32'",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
|
|
@ -57,6 +55,7 @@ Issues = "https://github.com/feder-cr/invisible_playwright/issues"
|
|||
|
||||
[tool.hatch.build.targets.wheel]
|
||||
packages = ["src/invisible_playwright"]
|
||||
exclude = ["*.bak", "*-bak"]
|
||||
|
||||
[tool.hatch.build.targets.sdist]
|
||||
include = ["src/invisible_playwright", "tests", "README.md", "LICENSE", "pyproject.toml"]
|
||||
|
|
|
|||
|
|
@ -1,26 +1,17 @@
|
|||
"""Internal Bayesian fingerprint generator used by invisible_playwright.
|
||||
"""Backward-compat shim — spostato in invisible_core._fpforge (alias completo).
|
||||
|
||||
Private module — do not import from user code. Use
|
||||
invisible_playwright.InvisiblePlaywright(seed=..., pin=...) instead.
|
||||
Aliasa il package E i suoi submodule agli stessi oggetti del core, cosi'
|
||||
``from invisible_playwright._fpforge.profile import Profile`` e
|
||||
``from invisible_core._fpforge.profile import Profile`` sono la STESSA classe
|
||||
(isinstance funziona) e i nomi privati restano accessibili.
|
||||
"""
|
||||
from .profile import (
|
||||
AudioProfile,
|
||||
CodecProfile,
|
||||
GPUProfile,
|
||||
HardwareProfile,
|
||||
Profile,
|
||||
ScreenProfile,
|
||||
WebGLProfile,
|
||||
generate_profile,
|
||||
)
|
||||
import sys as _sys
|
||||
import invisible_core._fpforge as _pkg
|
||||
from invisible_core._fpforge import profile as _profile
|
||||
from invisible_core._fpforge import _sampler as _sampler_mod
|
||||
from invisible_core._fpforge import _network as _network_mod
|
||||
|
||||
__all__ = [
|
||||
"generate_profile",
|
||||
"Profile",
|
||||
"GPUProfile",
|
||||
"ScreenProfile",
|
||||
"HardwareProfile",
|
||||
"AudioProfile",
|
||||
"CodecProfile",
|
||||
"WebGLProfile",
|
||||
]
|
||||
_sys.modules[__name__] = _pkg
|
||||
_sys.modules[__name__ + ".profile"] = _profile
|
||||
_sys.modules[__name__ + "._sampler"] = _sampler_mod
|
||||
_sys.modules[__name__ + "._network"] = _network_mod
|
||||
|
|
|
|||
|
|
@ -1,144 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Generic Bayesian network for fingerprint sampling.
|
||||
|
||||
A Node has:
|
||||
- name
|
||||
- parents (list of parent node names)
|
||||
- CPT: either
|
||||
* marginal (no parents): flat [{value, prob}, ...]
|
||||
* conditional: {parent_tuple: [{value, prob}, ...]}
|
||||
- OR deterministic: a classifier function `(context) -> value` (no CPT)
|
||||
|
||||
Sampling:
|
||||
- Nodes are topologically sorted
|
||||
- For each node, look up the conditional distribution given parent values
|
||||
already sampled in `context`, then weighted-pick
|
||||
- Deterministic nodes apply their classifier directly
|
||||
|
||||
Values can be ANY JSON-serializable type (int, str, dict, list, bool).
|
||||
Complex values (e.g. screen joint {w, h, dpr}) are stored as dicts in the
|
||||
CPT and returned as-is in the context.
|
||||
"""
|
||||
import json
|
||||
import random
|
||||
from typing import Any, Callable, Dict, List, Optional, Tuple
|
||||
|
||||
|
||||
class Node:
|
||||
"""Single Bayesian node."""
|
||||
|
||||
__slots__ = ("name", "parents", "cpt", "classifier", "_marginal")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
parents: Optional[List[str]] = None,
|
||||
cpt: Optional[Any] = None,
|
||||
classifier: Optional[Callable[[Dict[str, Any]], Any]] = None,
|
||||
):
|
||||
self.name = name
|
||||
self.parents = list(parents or [])
|
||||
self.cpt = cpt
|
||||
self.classifier = classifier
|
||||
# Precompute: for no-parent nodes, cpt is the marginal list
|
||||
self._marginal = cpt if not self.parents and classifier is None else None
|
||||
|
||||
def sample(self, context: Dict[str, Any], rng: random.Random) -> Any:
|
||||
# Deterministic nodes don't sample
|
||||
if self.classifier is not None:
|
||||
return self.classifier(context)
|
||||
|
||||
if not self.parents:
|
||||
# Marginal root
|
||||
return _weighted_pick(self._marginal, rng)
|
||||
|
||||
# Conditional node: build the key from parent values
|
||||
key = _parent_key(self.parents, context)
|
||||
if key not in self.cpt:
|
||||
# Fallback: concatenate all parents' tables (uniform over union)
|
||||
# Keeps sampler from crashing if data doesn't cover some combo.
|
||||
pool = []
|
||||
for v in self.cpt.values():
|
||||
pool.extend(v)
|
||||
if not pool:
|
||||
raise ValueError(
|
||||
f"Node {self.name!r}: no CPT entries for {self.parents}={key}"
|
||||
)
|
||||
return _weighted_pick(pool, rng)
|
||||
return _weighted_pick(self.cpt[key], rng)
|
||||
|
||||
|
||||
class Network:
|
||||
"""Collection of nodes with topological sampling."""
|
||||
|
||||
def __init__(self, nodes: List[Node]):
|
||||
self.nodes = _topsort(nodes)
|
||||
self.by_name = {n.name: n for n in self.nodes}
|
||||
|
||||
def sample(
|
||||
self,
|
||||
rng: random.Random,
|
||||
evidence: Optional[Dict[str, Any]] = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""Sample the network. ``evidence`` fixes named nodes BEFORE their children
|
||||
sample, so the children RE-CONDITION on the fixed value (not relabel after).
|
||||
Used to pin ``gpu_class`` to the validated WebGL persona's class so the whole
|
||||
bundle (cores/screen/fonts) stays coherent with the GPU we expose. Earlier
|
||||
nodes still sample (RNG stream preserved → per-seed determinism)."""
|
||||
evidence = evidence or {}
|
||||
context: Dict[str, Any] = {}
|
||||
for node in self.nodes:
|
||||
if node.name in evidence:
|
||||
context[node.name] = evidence[node.name]
|
||||
else:
|
||||
context[node.name] = node.sample(context, rng)
|
||||
return context
|
||||
|
||||
|
||||
# ── Helpers ─────────────────────────────────────────────────────────────
|
||||
|
||||
def _weighted_pick(table: List[Dict[str, Any]], rng: random.Random) -> Any:
|
||||
"""`table` is a list of {value, prob} dicts. Returns one value."""
|
||||
values = [e["value"] for e in table]
|
||||
probs = [float(e["prob"]) for e in table]
|
||||
if not values:
|
||||
raise ValueError("Empty CPT entry")
|
||||
total = sum(probs)
|
||||
if total <= 0:
|
||||
return rng.choice(values)
|
||||
# Normalize to be safe (CPTs can be unnormalized)
|
||||
probs = [p / total for p in probs]
|
||||
return rng.choices(values, weights=probs, k=1)[0]
|
||||
|
||||
|
||||
def _parent_key(parents: List[str], context: Dict[str, Any]) -> str:
|
||||
"""Build a JSON-stable key from parent values in declared order."""
|
||||
if len(parents) == 1:
|
||||
v = context[parents[0]]
|
||||
return v if isinstance(v, str) else json.dumps(v, sort_keys=True)
|
||||
return json.dumps([context[p] for p in parents], sort_keys=True)
|
||||
|
||||
|
||||
def _topsort(nodes: List[Node]) -> List[Node]:
|
||||
"""Topological sort by parent-before-child."""
|
||||
by_name = {n.name: n for n in nodes}
|
||||
visited: set = set()
|
||||
order: List[Node] = []
|
||||
|
||||
def visit(n: Node, path: set):
|
||||
if n.name in visited:
|
||||
return
|
||||
if n.name in path:
|
||||
raise ValueError(f"Cycle at {n.name}")
|
||||
path.add(n.name)
|
||||
for p in n.parents:
|
||||
if p not in by_name:
|
||||
raise ValueError(f"Node {n.name} has unknown parent {p}")
|
||||
visit(by_name[p], path)
|
||||
path.discard(n.name)
|
||||
visited.add(n.name)
|
||||
order.append(n)
|
||||
|
||||
for n in nodes:
|
||||
visit(n, set())
|
||||
return order
|
||||
|
|
@ -1,445 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""stealth_forge — Bayesian fingerprint generator for Firefox 150 Windows.
|
||||
|
||||
Everything the Firefox build exposes to JS (screen, hardwareConcurrency,
|
||||
WebGL, audio, MSAA, theme, media codecs) is sampled from a Bayesian network
|
||||
with coherent cross-field dependencies. Identity (userAgent, platform,
|
||||
oscpu, webdriver=false, maxTouchPoints=0) is locked by the compiled build.
|
||||
|
||||
Graph:
|
||||
|
||||
gpu (root, 474 real Windows ANGLE renderers)
|
||||
│
|
||||
└─> gpu_class (deterministic classifier, 6 classes)
|
||||
├─> hw_concurrency (CPT per class)
|
||||
├─> screen (w/h/dpr/av) (CPT per class)
|
||||
└─> msaa_samples (CPT per class)
|
||||
|
||||
audio (root, joint rate+latency+channels — marginal)
|
||||
dark_theme (marginal)
|
||||
av1_enabled (marginal)
|
||||
webm_encoder_enabled (marginal)
|
||||
|
||||
font_exclude ← deterministic hash of stealth_seed (seed-derived)
|
||||
|
||||
CPTs live in `data/*.json` (easy to tune without code changes).
|
||||
Sampling is deterministic per stealth_seed via a private random.Random.
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from ._network import Network, Node
|
||||
|
||||
_HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
def _load(filename: str) -> Any:
|
||||
with open(os.path.join(_HERE, "data", filename), "r", encoding="utf-8") as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
# LOCKED IDENTITY (compiled into our Firefox 150 build — never varies)
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
_LOCKED: Dict[str, Any] = {
|
||||
"user_agent": (
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0) "
|
||||
"Gecko/20100101 Firefox/150.0.1"
|
||||
),
|
||||
"platform": "Win32",
|
||||
"oscpu": "Windows NT 10.0; Win64; x64",
|
||||
"app_code_name": "Mozilla",
|
||||
"app_version": "5.0 (Windows)",
|
||||
"product_sub": "20100101",
|
||||
"webdriver": False,
|
||||
"max_touch_points": 0,
|
||||
}
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
# DATA
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
_GPU_POOL = _load("webgl_renderer_pool.json")["entries"]
|
||||
# hwc/screen/storage now keyed on (gpu_class, intra_tier) for triangulation
|
||||
_CPT_HWC = _load("cpt_hwc_given_class_tier.json")["table"]
|
||||
_CPT_SCREEN = _load("cpt_screen_given_class_tier.json")["table"]
|
||||
_CPT_STORAGE = _load("cpt_storage_given_class_tier.json")["table"]
|
||||
# Hidden tier variable that makes hwc/screen/storage jointly coherent
|
||||
_CPT_INTRA_TIER = _load("cpt_intra_tier_given_class.json")["table"]
|
||||
# MSAA depends on (gpu_class, screen_tier) — 4K gaming → MSAA=0, 1080p+GPU → MSAA=4
|
||||
_CPT_MSAA = _load("cpt_msaa_given_class_screen.json")["table"]
|
||||
# Codec unchanged
|
||||
_CPT_CODEC = _load("cpt_codec_given_class.json")["table"]
|
||||
# Audio now conditional on gpu_class (workstation → pro audio, old → 44.1kHz onboard)
|
||||
_CPT_AUDIO = _load("cpt_audio_given_class.json")["table"]
|
||||
_INDEP = _load("priors_independent.json")
|
||||
_FONT_POOL = _load("font_pool.json")
|
||||
# hardwareConcurrency: grounded in the REAL Windows marginal (browserforge Windows UAs).
|
||||
# cores are OS-level, ~independent of GPU given the OS (browserforge confirms), so this is a
|
||||
# root marginal — NOT conditioned on gpu_class/intra_tier. Fixes the old CPT over-representing
|
||||
# 6 cores (~28% vs real ~2%). NB: screen size + dpr are intentionally LEFT on their existing
|
||||
# nodes (user 2026-06-18: "non modificare dpr e le size degli screen, rompono sempre").
|
||||
_CORES_MARGINAL = [
|
||||
{"value": int(e["value"]), "prob": e["prob"]}
|
||||
for e in _load("win_hw_marginals.json")["cores"]
|
||||
if 2 <= int(e["value"]) <= 64 and e["prob"] >= 0.004
|
||||
]
|
||||
# Each entry is a dict {"name": "<lowercase family>", "factor": float}.
|
||||
# - name: the font family advertised to the page.
|
||||
# - factor: per-family width scale used by the consumer to make the family
|
||||
# detectable by width-diff probes.
|
||||
# Core = always-included; Optional = sampled with P(font | gpu_class).
|
||||
_FONT_CORE: list = _FONT_POOL["core"]
|
||||
_FONT_OPTIONAL: list = _FONT_POOL["optional"]
|
||||
_CPT_FONTS_OPT = _load("cpt_fonts_optional_given_class.json")["table"] # legacy (per-font sampling, superseded by profiles)
|
||||
# Realistic Windows font PROFILES (2026-06-18): each = a real machine's optional-font set
|
||||
# (validated to NOT over-claim on FP Pro). Profile-level variation (machines differ in
|
||||
# Office/extra fonts) instead of per-font random sampling, which produced unrealistic
|
||||
# combinations (exotic fonts -> FP Pro over-detection -> tampering_ml tell).
|
||||
_FONT_PROFILES: list = _FONT_POOL.get("profiles", [])
|
||||
_OPT_BY_NAME = {e["name"]: e for e in _FONT_OPTIONAL}
|
||||
# Browsing-history pool + CPT (per-class probabilities for visited sites).
|
||||
# Drives _recaptcha_seed's cookie pre-seed: each persona ends up with a
|
||||
# coherent list of ~15-30 visited sites whose categories correlate with
|
||||
# gpu_class (workstation → dev-heavy, integrated_old → shop+news-heavy).
|
||||
_BROWSING_POOL: list = _load("browsing_pool.json")["entries"]
|
||||
_CPT_BROWSING = _load("cpt_browsing_given_class.json")["table"]
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
# GPU CLASSIFIER (deterministic function of gpu → gpu_class)
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
_GPU_CLASSES = (
|
||||
"integrated_old", "integrated_modern", "low_end",
|
||||
"mid_range", "high_end", "workstation",
|
||||
)
|
||||
|
||||
|
||||
def classify_gpu(gpu_value: Dict[str, str]) -> str:
|
||||
"""Deterministic: maps (renderer, vendor) dict to one of 6 classes.
|
||||
|
||||
See data/cpt_*.json — each CPT table has an entry for every class.
|
||||
"""
|
||||
r = gpu_value.get("renderer", "")
|
||||
|
||||
if re.search(r"Intel.*HD Graphics (3000|4000|2500)", r):
|
||||
return "integrated_old"
|
||||
# Discrete Intel Arc DESKTOP/dGPU cards (A-series / B-series, e.g. A750,
|
||||
# A770, B580) are discrete GPUs (~RTX 3060 tier for A7xx), NOT the
|
||||
# integrated "Arc 130T/140T/Graphics" iGPUs in Core Ultra chips. Route the
|
||||
# discrete SKUs to a coherent discrete-GPU class so the conditioned bundle
|
||||
# (cores, screen, storage) matches a real discrete-GPU machine; A3xx are
|
||||
# entry discrete -> low_end, A5xx/A7xx/Bxxx -> mid_range. Bare "Arc 1x0(T/V)"
|
||||
# integrated names do NOT match and fall through to integrated_modern below.
|
||||
m = re.search(r"Intel.*\bArc(?:\(TM\))?\s+([AB])(\d)\d\d\b", r)
|
||||
if m:
|
||||
return "low_end" if m.group(2) == "3" else "mid_range"
|
||||
if re.search(
|
||||
r"Intel.*(HD Graphics (4[56]|5\d\d|6\d\d)|UHD Graphics|Graphics Family|Iris|Arc)",
|
||||
r,
|
||||
):
|
||||
return "integrated_modern"
|
||||
if re.search(
|
||||
r"AMD.*(Radeon(\(TM\))? (Graphics|6\d\dM|7\d\dM|8\d\dM)|Vega [0-9]|"
|
||||
r"Renoir|Rembrandt|TM Graphics)",
|
||||
r, re.IGNORECASE,
|
||||
):
|
||||
return "integrated_modern"
|
||||
|
||||
# NVIDIA: Firefox SanitizeRenderer.cpp collapses every GeForce into one of
|
||||
# 3 vintage buckets (8800 GTX / GTX 480 / GTX 980). The renderer string
|
||||
# exposed to JS is therefore vintage; pairing it with modern cores/screen
|
||||
# creates an internal mismatch that FP Pro's tampering_ml flags. We pick
|
||||
# `low_end` for all 3 buckets so cores stay 4-12 and screen 1080-1440p,
|
||||
# consistent with what a real user with each of those (vintage) cards
|
||||
# would have. Workstation overrides keep their high-tier classification.
|
||||
if re.search(
|
||||
r"(GeForce (8\d\d\d?|9\d\d\d?|GTX 980|GTX 480|GT 1030|GT 710|GT 730|"
|
||||
r"GT 220|GT 240|210|310)|Quadro K\d|Radeon HD [1234]\d\d\d)", r,
|
||||
):
|
||||
return "low_end"
|
||||
|
||||
# NVIDIA discrete (any other GeForce — should be rare after the pool was
|
||||
# collapsed to the 3 sanitize buckets, but kept as a safety net).
|
||||
m = re.search(r"GeForce\s+(?:GTX\s+|RTX\s+)?(\d{3,4})", r)
|
||||
if m:
|
||||
if "Quadro" in r or "Workstation" in r:
|
||||
return "workstation"
|
||||
# Anything that survives the sanitize collapse stays low_end to avoid
|
||||
# the modern-cores/vintage-renderer pairing.
|
||||
return "low_end"
|
||||
|
||||
# AMD discrete
|
||||
m = re.search(r"Radeon[^0-9]*(\d{3,4})", r)
|
||||
if m:
|
||||
n = int(m.group(1))
|
||||
if "FirePro" in r or "Radeon Pro" in r:
|
||||
return "workstation"
|
||||
if n >= 5700:
|
||||
return "high_end"
|
||||
if 5500 <= n <= 5600 or 580 <= n <= 590:
|
||||
return "mid_range"
|
||||
return "low_end"
|
||||
|
||||
# Fallback
|
||||
return "mid_range"
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
# NETWORK CONSTRUCTION
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
# Build once at import — the network is stateless, only the RNG varies.
|
||||
|
||||
def _gpu_marginal():
|
||||
"""Build marginal distribution over GPU pool (uniform for now)."""
|
||||
n = len(_GPU_POOL)
|
||||
p = 1.0 / n
|
||||
return [{"value": g, "prob": p} for g in _GPU_POOL]
|
||||
|
||||
|
||||
def _cpt_from_table(table: Dict[str, Any]) -> Dict[str, list]:
|
||||
"""CPT for conditional nodes: `{class_name: [{value, prob}, ...]}`."""
|
||||
return dict(table)
|
||||
|
||||
|
||||
def _screen_tier(ctx):
|
||||
"""Classify screen width into tier for (gpu_class, screen_tier) CPTs."""
|
||||
s = ctx.get("screen", {}) or {}
|
||||
w = int(s.get("w", 1920))
|
||||
h = int(s.get("h", 1080))
|
||||
# Ultrawide: aspect ratio > 2.1 (e.g. 3440x1440, 5120x1440)
|
||||
if h > 0 and (w / h) > 2.1:
|
||||
return "ultrawide"
|
||||
if w <= 1920:
|
||||
return "1080p"
|
||||
if w <= 2560:
|
||||
return "1440p"
|
||||
if w <= 3840:
|
||||
return "2160p"
|
||||
return "ultrawide"
|
||||
|
||||
|
||||
_NETWORK = Network([
|
||||
Node("gpu", parents=[], cpt=_gpu_marginal()),
|
||||
Node("gpu_class", parents=["gpu"], classifier=lambda ctx: classify_gpu(ctx["gpu"])),
|
||||
# Hidden variable: within a gpu_class, user's OTHER components (RAM, SSD,
|
||||
# cores, screen) correlate — a 'premium' mid_range user has more cores,
|
||||
# larger SSD, higher-res screen than a 'budget' mid_range user. Without
|
||||
# this, hwc/screen/storage would be independent given gpu_class (noisy).
|
||||
Node("intra_tier", parents=["gpu_class"], cpt=_cpt_from_table(_CPT_INTRA_TIER)),
|
||||
# hw_concurrency: REAL Windows marginal (root, OS-level, not GPU-conditioned). screen +
|
||||
# storage stay jointly coherent via (gpu_class, intra_tier) — screen size deliberately
|
||||
# unchanged (user: dpr + screen sizes break things; leave them).
|
||||
Node("hw_concurrency", parents=[], cpt=_CORES_MARGINAL),
|
||||
Node("screen", parents=["gpu_class", "intra_tier"],
|
||||
cpt=_cpt_from_table(_CPT_SCREEN)),
|
||||
# Derive screen_tier from screen for msaa parent lookup.
|
||||
Node("screen_tier", parents=["screen"], classifier=_screen_tier),
|
||||
# MSAA: realistic combo (4K + high_end GPU → MSAA=0 due to perf cost;
|
||||
# 1080p + high_end → MSAA=4 common; 1080p + integrated → MSAA=0).
|
||||
Node("msaa_samples", parents=["gpu_class", "screen_tier"],
|
||||
cpt=_cpt_from_table(_CPT_MSAA)),
|
||||
# Joint codec distribution (gpu_class only).
|
||||
Node("codec", parents=["gpu_class"], cpt=_cpt_from_table(_CPT_CODEC)),
|
||||
# Storage quota: coherent within gpu_class × intra_tier (premium workstation
|
||||
# user → 2-3TB SSD; budget workstation user → 512GB; budget integrated_old
|
||||
# → 128GB).
|
||||
Node("storage_quota_mb", parents=["gpu_class", "intra_tier"],
|
||||
cpt=_cpt_from_table(_CPT_STORAGE)),
|
||||
# Audio: pro users (workstation) → 48/96kHz 6-8ch; old onboard → 44.1kHz
|
||||
# 2ch high latency. Workstation GPU + 44.1kHz mono was previously
|
||||
# implausible; now blocked by the CPT.
|
||||
Node("audio", parents=["gpu_class"], cpt=_cpt_from_table(_CPT_AUDIO)),
|
||||
Node("dark_theme", parents=[], cpt=_INDEP["dark_theme"]["table"]),
|
||||
])
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
# FONT LIST (Bayesian: core ∪ sampled_optional | gpu_class)
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
# The browser sees ONLY these families (everything else hidden) and renders
|
||||
# them from the REAL Windows font files the binary bundles in <GRE>/fonts
|
||||
# (MOZ_BUNDLED_FONTS). No fabricated widths: per-session metric uniqueness
|
||||
# comes from the HarfBuzz per-glyph jitter (shared fpp.hw_seed), not here.
|
||||
# Core (~112): always included — fresh Win11 + Office 2021 English.
|
||||
# Optional (~40): one realistic Windows profile sampled per seed (weighted,
|
||||
# deterministic) → ~3-8 optional families differ per session while staying
|
||||
# centered on 'typical Windows user'.
|
||||
|
||||
|
||||
def derive_font_prefs(gpu_class: str, rng) -> Dict[str, str]:
|
||||
"""Build the session's font family list.
|
||||
|
||||
Profile-based (not per-font random):
|
||||
- Core families always included (OS defaults + CSS-generic backers).
|
||||
- Optional families come from ONE realistic Windows profile picked per
|
||||
seed (weighted, deterministic).
|
||||
|
||||
Returns ``{"whitelist": "arial,calibri,marlett,..."}`` — the comma-joined
|
||||
family list to advertise. The binary applies it to the native system font
|
||||
allow-list AT CONSTRUCTION and renders each family from the bundled real
|
||||
Windows file, so glyphs and widths are genuine. To add a family, just add
|
||||
an entry to font_pool.json:core/optional — no special-case code needed.
|
||||
"""
|
||||
# Profile-based (2026-06-18): pick ONE realistic Windows font profile (weighted,
|
||||
# deterministic per seed). Per-font random sampling is superseded — it produced
|
||||
# unrealistic optional combinations (exotic fonts) that FP Pro over-detected
|
||||
# (detected-set 26 vs real 20 -> tampering_ml ~0.72). Profiles are validated subsets
|
||||
# of a real machine's set, so the detected-set matches a genuine Windows install.
|
||||
included: list = list(_FONT_CORE) # core: always present (OS defaults + generic backers)
|
||||
profile = None
|
||||
if _FONT_PROFILES:
|
||||
total = sum(p.get("weight", 1) for p in _FONT_PROFILES)
|
||||
anchor = rng.random() * total
|
||||
cum = 0.0
|
||||
for p in _FONT_PROFILES:
|
||||
cum += p.get("weight", 1)
|
||||
if anchor < cum:
|
||||
profile = p
|
||||
break
|
||||
if profile is None:
|
||||
profile = _FONT_PROFILES[-1]
|
||||
if profile is not None:
|
||||
for name in profile.get("optional", []):
|
||||
entry = _OPT_BY_NAME.get(name)
|
||||
if entry is not None:
|
||||
included.append(entry)
|
||||
else:
|
||||
included.extend(_FONT_OPTIONAL) # fallback (no profiles defined): all optional
|
||||
# Dedup by name (a profile may list a font that is also in core, e.g. after a
|
||||
# standard font is promoted core→always-present) so the list never carries a
|
||||
# duplicate family.
|
||||
_seen: set = set()
|
||||
_uniq: list = []
|
||||
for e in included:
|
||||
if e["name"] not in _seen:
|
||||
_seen.add(e["name"])
|
||||
_uniq.append(e)
|
||||
included = _uniq
|
||||
# Deterministic ordering: sort by name
|
||||
included.sort(key=lambda e: e["name"])
|
||||
whitelist = ",".join(e["name"] for e in included)
|
||||
return {"whitelist": whitelist}
|
||||
|
||||
|
||||
# Back-compat shim: legacy callers still import derive_font_whitelist.
|
||||
def derive_font_whitelist(gpu_class: str, rng) -> str:
|
||||
return derive_font_prefs(gpu_class, rng)["whitelist"]
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
# BROWSING HISTORY (Bayesian: per-site P(visited|gpu_class))
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
def derive_browsing_history(gpu_class: str, rng) -> list:
|
||||
"""Sample which sites this persona has visited recently.
|
||||
|
||||
Each site in the pool has a per-class probability (CPT). We sample
|
||||
independently per-site, producing a list of dicts:
|
||||
[{"name": "github.com", "category": "dev", "cookie_profile": "ga_cf"}, ...]
|
||||
|
||||
Sum of CPT probabilities per class is tuned to land ~15-30 visited sites
|
||||
on average — an established-user signature. Sorted by name for stable
|
||||
output across runs of the same seed.
|
||||
"""
|
||||
cpt = _CPT_BROWSING.get(gpu_class)
|
||||
if cpt is None:
|
||||
cpt = _CPT_BROWSING["mid_range"]
|
||||
visited: list = []
|
||||
for entry in _BROWSING_POOL:
|
||||
name = entry["name"]
|
||||
p = cpt.get(name, 0.3) # default 0.3 for missing CPT row
|
||||
if rng.random() < p:
|
||||
visited.append(dict(entry)) # copy to avoid mutating pool
|
||||
visited.sort(key=lambda e: e["name"])
|
||||
return visited
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
# PUBLIC API: Forge
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
import random
|
||||
|
||||
|
||||
class Forge:
|
||||
"""Fingerprint forge — single seed → coherent bundle."""
|
||||
|
||||
def __init__(self, seed: int):
|
||||
self.seed = int(seed)
|
||||
self._rng = random.Random(self.seed)
|
||||
|
||||
def sample(self, fixed_gpu_class: Optional[str] = None) -> Dict[str, Any]:
|
||||
# fixed_gpu_class pins gpu_class so the WHOLE bundle (cores/screen/fonts) is
|
||||
# drawn coherently for the WebGL persona's class we expose on Windows/mac.
|
||||
# The default (no fix) path calls _NETWORK.sample(rng) with one arg so existing
|
||||
# monkeypatches/tests keep working.
|
||||
if fixed_gpu_class:
|
||||
bundle = _NETWORK.sample(self._rng, evidence={"gpu_class": fixed_gpu_class})
|
||||
else:
|
||||
bundle = _NETWORK.sample(self._rng)
|
||||
gpu = bundle["gpu"]
|
||||
screen = bundle["screen"]
|
||||
audio = bundle["audio"]
|
||||
codec = bundle["codec"]
|
||||
return {
|
||||
# Seed tracking
|
||||
"stealth_seed": self.seed,
|
||||
# Locked identity
|
||||
**_LOCKED,
|
||||
# GPU (coherent pair from 474 pool)
|
||||
"webgl_renderer": gpu["renderer"],
|
||||
"webgl_vendor": gpu["vendor"],
|
||||
"gpu_class": bundle["gpu_class"],
|
||||
# Hidden-variable debug metadata (not a Firefox pref, just for
|
||||
# analysis / test result correlation tracking)
|
||||
"intra_tier": bundle["intra_tier"],
|
||||
"screen_tier": bundle["screen_tier"],
|
||||
# Screen (coherent with GPU class)
|
||||
"screen_w": int(screen["w"]),
|
||||
"screen_h": int(screen["h"]),
|
||||
"screen_avail_w": int(screen.get("aw", screen["w"])),
|
||||
"screen_avail_h": int(screen.get("ah", screen["h"] - 40)),
|
||||
"dpr": float(screen["dpr"]),
|
||||
# Hardware (coherent with GPU class)
|
||||
"hw_concurrency": int(bundle["hw_concurrency"]),
|
||||
# WebGL MSAA (coherent with GPU class)
|
||||
"msaa_samples": int(bundle["msaa_samples"]),
|
||||
# Audio (independent joint)
|
||||
"audio_sample_rate": int(audio["rate"]),
|
||||
"audio_output_latency_ms": int(audio["latency"]),
|
||||
"audio_max_channel_count": int(audio["channels"]),
|
||||
# Codec prefs (joint, coherent with GPU class). All 5 are
|
||||
# JS-visible: av1/webm_encoder via canPlayType/MediaRecorder,
|
||||
# mediasource_* via MediaSource.isTypeSupported, webspeech_synth
|
||||
# via 'speechSynthesis' in window (CreepJS voices probe).
|
||||
"av1_enabled": bool(codec["av1_enabled"]),
|
||||
"webm_encoder_enabled": bool(codec["webm_encoder_enabled"]),
|
||||
"mediasource_webm": bool(codec["mediasource_webm"]),
|
||||
"mediasource_mp4": bool(codec["mediasource_mp4"]),
|
||||
"webspeech_synth": bool(codec["webspeech_synth"]),
|
||||
# Storage quota MB (coherent with GPU class — workstation larger SSDs).
|
||||
"storage_quota_mb": int(bundle["storage_quota_mb"]),
|
||||
# Independent marginals
|
||||
"dark_theme": int(bundle["dark_theme"]),
|
||||
# Bayesian font prefs (coherent pair: whitelist + per-family
|
||||
# width scale metrics, both sampled from the same font_pool.json
|
||||
# and conditioned on gpu_class).
|
||||
**{
|
||||
f"font_{k}": v
|
||||
for k, v in derive_font_prefs(
|
||||
bundle["gpu_class"], self._rng
|
||||
).items()
|
||||
},
|
||||
# Bayesian browsing history (per-class P(visited|gpu_class)).
|
||||
# Consumed by _recaptcha_seed.py to seed coherent cookie history
|
||||
# when invisible_playwright is launched with prep_recaptcha=True.
|
||||
"browsing_history": derive_browsing_history(
|
||||
bundle["gpu_class"], self._rng
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def sample(seed: int, fixed_gpu_class: Optional[str] = None) -> Dict[str, Any]:
|
||||
"""Convenience: `Forge(seed).sample(fixed_gpu_class)`."""
|
||||
return Forge(seed).sample(fixed_gpu_class)
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
{
|
||||
"_comment": [
|
||||
"Pool of everyday websites used by the browsing_history node.",
|
||||
"Each entry: { name, category, cookie_profile }.",
|
||||
"- name: bare domain (no scheme, no leading dot).",
|
||||
"- category: dev / shop / news / reference / media / community / misc.",
|
||||
"- cookie_profile: short tag pointing to a cookie-template recipe used by",
|
||||
" _recaptcha_seed.py to generate concrete cookies (so heavy-analytics sites",
|
||||
" get _ga+_gid+OneTrust, simple sites get just _ga, dev tools get GH-style).",
|
||||
"Add new entries here + add per-class probabilities in cpt_browsing_given_class.json."
|
||||
],
|
||||
"entries": [
|
||||
{"name": "youtube.com", "category": "media", "cookie_profile": "ga_only"},
|
||||
{"name": "wikipedia.org", "category": "reference", "cookie_profile": "minimal"},
|
||||
{"name": "mozilla.org", "category": "reference", "cookie_profile": "ga_consent"},
|
||||
{"name": "w3schools.com", "category": "dev", "cookie_profile": "ga_consent_clarity"},
|
||||
{"name": "mdn.io", "category": "dev", "cookie_profile": "minimal"},
|
||||
{"name": "duckduckgo.com", "category": "reference", "cookie_profile": "minimal"},
|
||||
{"name": "github.com", "category": "dev", "cookie_profile": "ga_cf"},
|
||||
{"name": "stackoverflow.com", "category": "dev", "cookie_profile": "ga_consent_clarity"},
|
||||
{"name": "npmjs.com", "category": "dev", "cookie_profile": "ga_consent"},
|
||||
{"name": "gitlab.com", "category": "dev", "cookie_profile": "ga_cf"},
|
||||
{"name": "pypi.org", "category": "dev", "cookie_profile": "minimal"},
|
||||
{"name": "docs.python.org", "category": "dev", "cookie_profile": "minimal"},
|
||||
{"name": "rust-lang.org", "category": "dev", "cookie_profile": "ga_consent"},
|
||||
{"name": "go.dev", "category": "dev", "cookie_profile": "ga_consent"},
|
||||
{"name": "amazon.com", "category": "shop", "cookie_profile": "ga_consent_clarity"},
|
||||
{"name": "ebay.com", "category": "shop", "cookie_profile": "ga_consent"},
|
||||
{"name": "etsy.com", "category": "shop", "cookie_profile": "ga_consent_clarity"},
|
||||
{"name": "bestbuy.com", "category": "shop", "cookie_profile": "ga_consent_clarity"},
|
||||
{"name": "target.com", "category": "shop", "cookie_profile": "ga_consent_clarity"},
|
||||
{"name": "nytimes.com", "category": "news", "cookie_profile": "ga_consent_clarity"},
|
||||
{"name": "cnn.com", "category": "news", "cookie_profile": "ga_consent"},
|
||||
{"name": "bbc.com", "category": "news", "cookie_profile": "ga_consent"},
|
||||
{"name": "theguardian.com", "category": "news", "cookie_profile": "ga_consent_clarity"},
|
||||
{"name": "reuters.com", "category": "news", "cookie_profile": "ga_consent"},
|
||||
{"name": "apnews.com", "category": "news", "cookie_profile": "ga_consent"},
|
||||
{"name": "washingtonpost.com", "category": "news", "cookie_profile": "ga_consent"},
|
||||
{"name": "techcrunch.com", "category": "news", "cookie_profile": "ga_consent_clarity"},
|
||||
{"name": "theverge.com", "category": "news", "cookie_profile": "ga_consent"},
|
||||
{"name": "arstechnica.com", "category": "news", "cookie_profile": "ga_consent"},
|
||||
{"name": "wired.com", "category": "news", "cookie_profile": "ga_consent_clarity"},
|
||||
{"name": "engadget.com", "category": "news", "cookie_profile": "ga_consent"},
|
||||
{"name": "9to5mac.com", "category": "news", "cookie_profile": "ga_consent"},
|
||||
{"name": "medium.com", "category": "community", "cookie_profile": "ga_consent"},
|
||||
{"name": "dev.to", "category": "community", "cookie_profile": "ga_consent"},
|
||||
{"name": "reddit.com", "category": "community", "cookie_profile": "ga_cf"},
|
||||
{"name": "news.ycombinator.com", "category": "community", "cookie_profile": "minimal"},
|
||||
{"name": "quora.com", "category": "community", "cookie_profile": "ga_consent_clarity"},
|
||||
{"name": "stackexchange.com", "category": "community", "cookie_profile": "ga_consent_clarity"},
|
||||
{"name": "imdb.com", "category": "media", "cookie_profile": "ga_consent_clarity"},
|
||||
{"name": "rottentomatoes.com", "category": "media", "cookie_profile": "ga_consent"},
|
||||
{"name": "metacritic.com", "category": "media", "cookie_profile": "ga_consent"},
|
||||
{"name": "allrecipes.com", "category": "misc", "cookie_profile": "ga_consent_clarity"},
|
||||
{"name": "epicurious.com", "category": "misc", "cookie_profile": "ga_consent"},
|
||||
{"name": "tripadvisor.com", "category": "misc", "cookie_profile": "ga_consent_clarity"},
|
||||
{"name": "weather.com", "category": "reference", "cookie_profile": "ga_consent"},
|
||||
{"name": "timeanddate.com", "category": "reference", "cookie_profile": "ga_consent"},
|
||||
{"name": "thesaurus.com", "category": "reference", "cookie_profile": "ga_consent_clarity"},
|
||||
{"name": "kayak.com", "category": "shop", "cookie_profile": "ga_consent_clarity"},
|
||||
{"name": "booking.com", "category": "shop", "cookie_profile": "ga_consent_clarity"},
|
||||
{"name": "airbnb.com", "category": "shop", "cookie_profile": "ga_consent"}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,217 +0,0 @@
|
|||
{
|
||||
"_meta": "audio (rate/latency/channels) given gpu_class. NOTE 2026-06-14: maxChannelCount reflects the OS DEFAULT OUTPUT DEVICE (stereo for the vast majority of users), NOT the GPU — so channels=2 dominates every class (~78-92%) with only a small 6/8 surround tail. The previous tables emitted 45-100% surround on mid/high/workstation, which is unrealistic and lifted FP Pro tampering_ml (surround on a typical consumer profile reads as a coherence anomaly). Rate/latency tuples are unchanged.",
|
||||
"table": {
|
||||
"integrated_old": [
|
||||
{
|
||||
"value": {
|
||||
"rate": 44100,
|
||||
"latency": 50,
|
||||
"channels": 2
|
||||
},
|
||||
"prob": 0.7
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"rate": 48000,
|
||||
"latency": 50,
|
||||
"channels": 2
|
||||
},
|
||||
"prob": 0.3
|
||||
}
|
||||
],
|
||||
"integrated_modern": [
|
||||
{
|
||||
"value": {
|
||||
"rate": 48000,
|
||||
"latency": 30,
|
||||
"channels": 2
|
||||
},
|
||||
"prob": 0.62
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"rate": 44100,
|
||||
"latency": 40,
|
||||
"channels": 2
|
||||
},
|
||||
"prob": 0.3
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"rate": 48000,
|
||||
"latency": 25,
|
||||
"channels": 6
|
||||
},
|
||||
"prob": 0.08
|
||||
}
|
||||
],
|
||||
"low_end": [
|
||||
{
|
||||
"value": {
|
||||
"rate": 48000,
|
||||
"latency": 40,
|
||||
"channels": 2
|
||||
},
|
||||
"prob": 0.6
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"rate": 44100,
|
||||
"latency": 50,
|
||||
"channels": 2
|
||||
},
|
||||
"prob": 0.32
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"rate": 48000,
|
||||
"latency": 30,
|
||||
"channels": 6
|
||||
},
|
||||
"prob": 0.08
|
||||
}
|
||||
],
|
||||
"mid_range": [
|
||||
{
|
||||
"value": {
|
||||
"rate": 48000,
|
||||
"latency": 25,
|
||||
"channels": 2
|
||||
},
|
||||
"prob": 0.5
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"rate": 48000,
|
||||
"latency": 20,
|
||||
"channels": 2
|
||||
},
|
||||
"prob": 0.3
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"rate": 44100,
|
||||
"latency": 30,
|
||||
"channels": 2
|
||||
},
|
||||
"prob": 0.12
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"rate": 48000,
|
||||
"latency": 20,
|
||||
"channels": 6
|
||||
},
|
||||
"prob": 0.06
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"rate": 48000,
|
||||
"latency": 20,
|
||||
"channels": 8
|
||||
},
|
||||
"prob": 0.02
|
||||
}
|
||||
],
|
||||
"high_end": [
|
||||
{
|
||||
"value": {
|
||||
"rate": 48000,
|
||||
"latency": 15,
|
||||
"channels": 2
|
||||
},
|
||||
"prob": 0.6
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"rate": 96000,
|
||||
"latency": 15,
|
||||
"channels": 2
|
||||
},
|
||||
"prob": 0.18
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"rate": 48000,
|
||||
"latency": 15,
|
||||
"channels": 6
|
||||
},
|
||||
"prob": 0.1
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"rate": 48000,
|
||||
"latency": 15,
|
||||
"channels": 8
|
||||
},
|
||||
"prob": 0.05
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"rate": 96000,
|
||||
"latency": 15,
|
||||
"channels": 6
|
||||
},
|
||||
"prob": 0.05
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"rate": 96000,
|
||||
"latency": 15,
|
||||
"channels": 8
|
||||
},
|
||||
"prob": 0.02
|
||||
}
|
||||
],
|
||||
"workstation": [
|
||||
{
|
||||
"value": {
|
||||
"rate": 48000,
|
||||
"latency": 10,
|
||||
"channels": 2
|
||||
},
|
||||
"prob": 0.45
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"rate": 96000,
|
||||
"latency": 10,
|
||||
"channels": 2
|
||||
},
|
||||
"prob": 0.2
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"rate": 48000,
|
||||
"latency": 10,
|
||||
"channels": 8
|
||||
},
|
||||
"prob": 0.12
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"rate": 96000,
|
||||
"latency": 10,
|
||||
"channels": 8
|
||||
},
|
||||
"prob": 0.1
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"rate": 96000,
|
||||
"latency": 10,
|
||||
"channels": 6
|
||||
},
|
||||
"prob": 0.08
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"rate": 192000,
|
||||
"latency": 10,
|
||||
"channels": 8
|
||||
},
|
||||
"prob": 0.05
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,138 +0,0 @@
|
|||
{
|
||||
"_comment": [
|
||||
"Per-class probability that a persona of a given gpu_class has visited each",
|
||||
"site in the pool. Used by the browsing_history node to derive a coherent",
|
||||
"visited-domain list per persona.",
|
||||
"",
|
||||
"Probabilities are tuned so each class samples ~15-30 sites on average",
|
||||
"(sum across all 50 entries falls in that range), giving an established-user",
|
||||
"look. Categories are biased by class:",
|
||||
" - workstation/high_end: higher P(dev) + high P(news/media)",
|
||||
" - mid_range: balanced",
|
||||
" - low_end/integrated_*: lower P(dev), higher P(shop/news/reference)",
|
||||
"",
|
||||
"Missing class falls back to mid_range via Node CPT pool fallback."
|
||||
],
|
||||
"table": {
|
||||
"workstation": {
|
||||
"youtube.com": 0.80, "wikipedia.org": 0.85, "mozilla.org": 0.70,
|
||||
"w3schools.com": 0.40, "mdn.io": 0.55, "duckduckgo.com": 0.45,
|
||||
"github.com": 0.95, "stackoverflow.com": 0.90, "npmjs.com": 0.65,
|
||||
"gitlab.com": 0.50, "pypi.org": 0.55, "docs.python.org": 0.60,
|
||||
"rust-lang.org": 0.35, "go.dev": 0.30,
|
||||
"amazon.com": 0.70, "ebay.com": 0.25, "etsy.com": 0.15,
|
||||
"bestbuy.com": 0.45, "target.com": 0.30,
|
||||
"nytimes.com": 0.55, "cnn.com": 0.40, "bbc.com": 0.55,
|
||||
"theguardian.com": 0.45, "reuters.com": 0.40, "apnews.com": 0.30,
|
||||
"washingtonpost.com": 0.40,
|
||||
"techcrunch.com": 0.65, "theverge.com": 0.60, "arstechnica.com": 0.65,
|
||||
"wired.com": 0.50, "engadget.com": 0.35, "9to5mac.com": 0.30,
|
||||
"medium.com": 0.55, "dev.to": 0.40, "reddit.com": 0.70,
|
||||
"news.ycombinator.com": 0.65, "quora.com": 0.20, "stackexchange.com": 0.60,
|
||||
"imdb.com": 0.45, "rottentomatoes.com": 0.25, "metacritic.com": 0.20,
|
||||
"allrecipes.com": 0.20, "epicurious.com": 0.15, "tripadvisor.com": 0.30,
|
||||
"weather.com": 0.55, "timeanddate.com": 0.30, "thesaurus.com": 0.25,
|
||||
"kayak.com": 0.30, "booking.com": 0.35, "airbnb.com": 0.30
|
||||
},
|
||||
"high_end": {
|
||||
"youtube.com": 0.85, "wikipedia.org": 0.80, "mozilla.org": 0.60,
|
||||
"w3schools.com": 0.45, "mdn.io": 0.45, "duckduckgo.com": 0.40,
|
||||
"github.com": 0.85, "stackoverflow.com": 0.80, "npmjs.com": 0.50,
|
||||
"gitlab.com": 0.40, "pypi.org": 0.45, "docs.python.org": 0.50,
|
||||
"rust-lang.org": 0.30, "go.dev": 0.25,
|
||||
"amazon.com": 0.75, "ebay.com": 0.30, "etsy.com": 0.20,
|
||||
"bestbuy.com": 0.50, "target.com": 0.35,
|
||||
"nytimes.com": 0.50, "cnn.com": 0.50, "bbc.com": 0.50,
|
||||
"theguardian.com": 0.40, "reuters.com": 0.35, "apnews.com": 0.30,
|
||||
"washingtonpost.com": 0.35,
|
||||
"techcrunch.com": 0.60, "theverge.com": 0.65, "arstechnica.com": 0.60,
|
||||
"wired.com": 0.50, "engadget.com": 0.40, "9to5mac.com": 0.35,
|
||||
"medium.com": 0.50, "dev.to": 0.35, "reddit.com": 0.75,
|
||||
"news.ycombinator.com": 0.55, "quora.com": 0.25, "stackexchange.com": 0.55,
|
||||
"imdb.com": 0.55, "rottentomatoes.com": 0.35, "metacritic.com": 0.30,
|
||||
"allrecipes.com": 0.25, "epicurious.com": 0.20, "tripadvisor.com": 0.30,
|
||||
"weather.com": 0.55, "timeanddate.com": 0.30, "thesaurus.com": 0.25,
|
||||
"kayak.com": 0.30, "booking.com": 0.40, "airbnb.com": 0.30
|
||||
},
|
||||
"mid_range": {
|
||||
"youtube.com": 0.85, "wikipedia.org": 0.75, "mozilla.org": 0.45,
|
||||
"w3schools.com": 0.40, "mdn.io": 0.30, "duckduckgo.com": 0.35,
|
||||
"github.com": 0.55, "stackoverflow.com": 0.55, "npmjs.com": 0.30,
|
||||
"gitlab.com": 0.25, "pypi.org": 0.25, "docs.python.org": 0.30,
|
||||
"rust-lang.org": 0.15, "go.dev": 0.15,
|
||||
"amazon.com": 0.80, "ebay.com": 0.40, "etsy.com": 0.30,
|
||||
"bestbuy.com": 0.55, "target.com": 0.40,
|
||||
"nytimes.com": 0.45, "cnn.com": 0.55, "bbc.com": 0.45,
|
||||
"theguardian.com": 0.35, "reuters.com": 0.30, "apnews.com": 0.30,
|
||||
"washingtonpost.com": 0.30,
|
||||
"techcrunch.com": 0.45, "theverge.com": 0.50, "arstechnica.com": 0.40,
|
||||
"wired.com": 0.45, "engadget.com": 0.35, "9to5mac.com": 0.30,
|
||||
"medium.com": 0.45, "dev.to": 0.25, "reddit.com": 0.70,
|
||||
"news.ycombinator.com": 0.30, "quora.com": 0.35, "stackexchange.com": 0.40,
|
||||
"imdb.com": 0.60, "rottentomatoes.com": 0.40, "metacritic.com": 0.35,
|
||||
"allrecipes.com": 0.35, "epicurious.com": 0.25, "tripadvisor.com": 0.40,
|
||||
"weather.com": 0.60, "timeanddate.com": 0.25, "thesaurus.com": 0.30,
|
||||
"kayak.com": 0.35, "booking.com": 0.45, "airbnb.com": 0.40
|
||||
},
|
||||
"low_end": {
|
||||
"youtube.com": 0.85, "wikipedia.org": 0.70, "mozilla.org": 0.35,
|
||||
"w3schools.com": 0.30, "mdn.io": 0.20, "duckduckgo.com": 0.30,
|
||||
"github.com": 0.30, "stackoverflow.com": 0.30, "npmjs.com": 0.15,
|
||||
"gitlab.com": 0.10, "pypi.org": 0.10, "docs.python.org": 0.15,
|
||||
"rust-lang.org": 0.05, "go.dev": 0.05,
|
||||
"amazon.com": 0.85, "ebay.com": 0.50, "etsy.com": 0.40,
|
||||
"bestbuy.com": 0.55, "target.com": 0.45,
|
||||
"nytimes.com": 0.40, "cnn.com": 0.60, "bbc.com": 0.40,
|
||||
"theguardian.com": 0.30, "reuters.com": 0.25, "apnews.com": 0.30,
|
||||
"washingtonpost.com": 0.25,
|
||||
"techcrunch.com": 0.30, "theverge.com": 0.35, "arstechnica.com": 0.25,
|
||||
"wired.com": 0.40, "engadget.com": 0.30, "9to5mac.com": 0.25,
|
||||
"medium.com": 0.35, "dev.to": 0.15, "reddit.com": 0.65,
|
||||
"news.ycombinator.com": 0.15, "quora.com": 0.45, "stackexchange.com": 0.25,
|
||||
"imdb.com": 0.65, "rottentomatoes.com": 0.45, "metacritic.com": 0.35,
|
||||
"allrecipes.com": 0.45, "epicurious.com": 0.30, "tripadvisor.com": 0.45,
|
||||
"weather.com": 0.65, "timeanddate.com": 0.25, "thesaurus.com": 0.35,
|
||||
"kayak.com": 0.35, "booking.com": 0.50, "airbnb.com": 0.40
|
||||
},
|
||||
"integrated_modern": {
|
||||
"youtube.com": 0.85, "wikipedia.org": 0.70, "mozilla.org": 0.40,
|
||||
"w3schools.com": 0.35, "mdn.io": 0.25, "duckduckgo.com": 0.35,
|
||||
"github.com": 0.40, "stackoverflow.com": 0.40, "npmjs.com": 0.20,
|
||||
"gitlab.com": 0.15, "pypi.org": 0.20, "docs.python.org": 0.20,
|
||||
"rust-lang.org": 0.10, "go.dev": 0.10,
|
||||
"amazon.com": 0.80, "ebay.com": 0.40, "etsy.com": 0.30,
|
||||
"bestbuy.com": 0.50, "target.com": 0.40,
|
||||
"nytimes.com": 0.40, "cnn.com": 0.55, "bbc.com": 0.45,
|
||||
"theguardian.com": 0.35, "reuters.com": 0.30, "apnews.com": 0.30,
|
||||
"washingtonpost.com": 0.30,
|
||||
"techcrunch.com": 0.40, "theverge.com": 0.45, "arstechnica.com": 0.30,
|
||||
"wired.com": 0.40, "engadget.com": 0.30, "9to5mac.com": 0.25,
|
||||
"medium.com": 0.40, "dev.to": 0.20, "reddit.com": 0.65,
|
||||
"news.ycombinator.com": 0.25, "quora.com": 0.40, "stackexchange.com": 0.35,
|
||||
"imdb.com": 0.60, "rottentomatoes.com": 0.40, "metacritic.com": 0.30,
|
||||
"allrecipes.com": 0.40, "epicurious.com": 0.25, "tripadvisor.com": 0.40,
|
||||
"weather.com": 0.60, "timeanddate.com": 0.25, "thesaurus.com": 0.30,
|
||||
"kayak.com": 0.35, "booking.com": 0.45, "airbnb.com": 0.40
|
||||
},
|
||||
"integrated_old": {
|
||||
"youtube.com": 0.75, "wikipedia.org": 0.65, "mozilla.org": 0.30,
|
||||
"w3schools.com": 0.20, "mdn.io": 0.10, "duckduckgo.com": 0.25,
|
||||
"github.com": 0.15, "stackoverflow.com": 0.20, "npmjs.com": 0.05,
|
||||
"gitlab.com": 0.05, "pypi.org": 0.05, "docs.python.org": 0.10,
|
||||
"rust-lang.org": 0.02, "go.dev": 0.02,
|
||||
"amazon.com": 0.85, "ebay.com": 0.55, "etsy.com": 0.45,
|
||||
"bestbuy.com": 0.55, "target.com": 0.50,
|
||||
"nytimes.com": 0.45, "cnn.com": 0.65, "bbc.com": 0.40,
|
||||
"theguardian.com": 0.30, "reuters.com": 0.25, "apnews.com": 0.35,
|
||||
"washingtonpost.com": 0.30,
|
||||
"techcrunch.com": 0.20, "theverge.com": 0.25, "arstechnica.com": 0.15,
|
||||
"wired.com": 0.30, "engadget.com": 0.20, "9to5mac.com": 0.20,
|
||||
"medium.com": 0.30, "dev.to": 0.05, "reddit.com": 0.55,
|
||||
"news.ycombinator.com": 0.05, "quora.com": 0.55, "stackexchange.com": 0.15,
|
||||
"imdb.com": 0.70, "rottentomatoes.com": 0.50, "metacritic.com": 0.35,
|
||||
"allrecipes.com": 0.55, "epicurious.com": 0.35, "tripadvisor.com": 0.50,
|
||||
"weather.com": 0.70, "timeanddate.com": 0.30, "thesaurus.com": 0.40,
|
||||
"kayak.com": 0.40, "booking.com": 0.55, "airbnb.com": 0.40
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,147 +0,0 @@
|
|||
{
|
||||
"_meta": "codec given gpu_class",
|
||||
"table": {
|
||||
"integrated_old": [
|
||||
{
|
||||
"value": {
|
||||
"av1_enabled": false,
|
||||
"webm_encoder_enabled": false,
|
||||
"mediasource_webm": true,
|
||||
"mediasource_mp4": true,
|
||||
"webspeech_synth": true
|
||||
},
|
||||
"prob": 1.0
|
||||
}
|
||||
],
|
||||
"integrated_modern": [
|
||||
{
|
||||
"value": {
|
||||
"av1_enabled": true,
|
||||
"webm_encoder_enabled": true,
|
||||
"mediasource_webm": true,
|
||||
"mediasource_mp4": true,
|
||||
"webspeech_synth": true
|
||||
},
|
||||
"prob": 0.55
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"av1_enabled": false,
|
||||
"webm_encoder_enabled": true,
|
||||
"mediasource_webm": true,
|
||||
"mediasource_mp4": true,
|
||||
"webspeech_synth": true
|
||||
},
|
||||
"prob": 0.35
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"av1_enabled": true,
|
||||
"webm_encoder_enabled": true,
|
||||
"mediasource_webm": true,
|
||||
"mediasource_mp4": true,
|
||||
"webspeech_synth": false
|
||||
},
|
||||
"prob": 0.1
|
||||
}
|
||||
],
|
||||
"low_end": [
|
||||
{
|
||||
"value": {
|
||||
"av1_enabled": false,
|
||||
"webm_encoder_enabled": true,
|
||||
"mediasource_webm": true,
|
||||
"mediasource_mp4": true,
|
||||
"webspeech_synth": true
|
||||
},
|
||||
"prob": 0.85
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"av1_enabled": false,
|
||||
"webm_encoder_enabled": true,
|
||||
"mediasource_webm": true,
|
||||
"mediasource_mp4": true,
|
||||
"webspeech_synth": false
|
||||
},
|
||||
"prob": 0.15
|
||||
}
|
||||
],
|
||||
"mid_range": [
|
||||
{
|
||||
"value": {
|
||||
"av1_enabled": true,
|
||||
"webm_encoder_enabled": true,
|
||||
"mediasource_webm": true,
|
||||
"mediasource_mp4": true,
|
||||
"webspeech_synth": true
|
||||
},
|
||||
"prob": 0.55
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"av1_enabled": false,
|
||||
"webm_encoder_enabled": true,
|
||||
"mediasource_webm": true,
|
||||
"mediasource_mp4": true,
|
||||
"webspeech_synth": true
|
||||
},
|
||||
"prob": 0.35
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"av1_enabled": true,
|
||||
"webm_encoder_enabled": true,
|
||||
"mediasource_webm": true,
|
||||
"mediasource_mp4": true,
|
||||
"webspeech_synth": false
|
||||
},
|
||||
"prob": 0.1
|
||||
}
|
||||
],
|
||||
"high_end": [
|
||||
{
|
||||
"value": {
|
||||
"av1_enabled": true,
|
||||
"webm_encoder_enabled": true,
|
||||
"mediasource_webm": true,
|
||||
"mediasource_mp4": true,
|
||||
"webspeech_synth": true
|
||||
},
|
||||
"prob": 0.85
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"av1_enabled": true,
|
||||
"webm_encoder_enabled": true,
|
||||
"mediasource_webm": true,
|
||||
"mediasource_mp4": true,
|
||||
"webspeech_synth": false
|
||||
},
|
||||
"prob": 0.15
|
||||
}
|
||||
],
|
||||
"workstation": [
|
||||
{
|
||||
"value": {
|
||||
"av1_enabled": true,
|
||||
"webm_encoder_enabled": true,
|
||||
"mediasource_webm": true,
|
||||
"mediasource_mp4": true,
|
||||
"webspeech_synth": true
|
||||
},
|
||||
"prob": 0.7
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"av1_enabled": true,
|
||||
"webm_encoder_enabled": true,
|
||||
"mediasource_webm": true,
|
||||
"mediasource_mp4": true,
|
||||
"webspeech_synth": false
|
||||
},
|
||||
"prob": 0.3
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,295 +0,0 @@
|
|||
{
|
||||
"_meta": {
|
||||
"name": "optional_font presence | gpu_class",
|
||||
"parents": [
|
||||
"gpu_class"
|
||||
],
|
||||
"child": "optional_fonts_subset",
|
||||
"description": "Per-optional-font presence probabilities given gpu_class. Each optional font in font_pool.json sampled INDEPENDENTLY with P(present | gpu_class) given here. Integrated_old: fewer language packs (older/cheaper machines). Workstation: more regional/language packs (international users, enterprise deployments).",
|
||||
"rationale": "Near-invariant by design: most optional fonts have baseline P ~ 0.60-0.85 across classes. Per-session variance is small (~3-6 fonts toggling on/off out of 40 optional). Result: fingerprint always looks like 'typical Windows 11 + Office', with small realistic per-user variance in regional support."
|
||||
},
|
||||
"table": {
|
||||
"integrated_old": {
|
||||
"aparajita": 0.45,
|
||||
"calibri": 0.9,
|
||||
"dengxian": 0.3,
|
||||
"dfkai-sb": 0.25,
|
||||
"dokchampa": 0.2,
|
||||
"estrangelo edessa": 0.55,
|
||||
"euphemia": 0.6,
|
||||
"fangsong": 0.35,
|
||||
"gadugi": 0.88,
|
||||
"gautami": 0.55,
|
||||
"helv": 0.7,
|
||||
"iskoola pota": 0.5,
|
||||
"javanese text": 0.85,
|
||||
"kaiti": 0.3,
|
||||
"kalinga": 0.5,
|
||||
"kartika": 0.5,
|
||||
"khmer ui": 0.35,
|
||||
"kokila": 0.55,
|
||||
"lao ui": 0.35,
|
||||
"latha": 0.65,
|
||||
"leelawadee ui": 0.87,
|
||||
"mangal": 0.65,
|
||||
"meiryo": 0.6,
|
||||
"microsoft uighur": 0.4,
|
||||
"ms pmincho": 0.65,
|
||||
"ms reference sans serif": 0.55,
|
||||
"ms reference specialty": 0.5,
|
||||
"ms ui gothic": 0.82,
|
||||
"myanmar text": 0.84,
|
||||
"nyala": 0.55,
|
||||
"plantagenet cherokee": 0.6,
|
||||
"raavi": 0.55,
|
||||
"segoe fluent icons": 0.5,
|
||||
"segoe ui light": 0.75,
|
||||
"shonar bangla": 0.55,
|
||||
"shruti": 0.55,
|
||||
"simkai": 0.25,
|
||||
"small fonts": 0.75,
|
||||
"traditional arabic": 0.55,
|
||||
"tunga": 0.55,
|
||||
"urdu typesetting": 0.45,
|
||||
"utsaah": 0.55,
|
||||
"vani": 0.55,
|
||||
"vijaya": 0.55,
|
||||
"yu mincho": 0.55
|
||||
},
|
||||
"integrated_modern": {
|
||||
"aparajita": 0.65,
|
||||
"calibri": 0.78,
|
||||
"dengxian": 0.5,
|
||||
"dfkai-sb": 0.4,
|
||||
"dokchampa": 0.4,
|
||||
"estrangelo edessa": 0.7,
|
||||
"euphemia": 0.78,
|
||||
"fangsong": 0.55,
|
||||
"gadugi": 0.96,
|
||||
"gautami": 0.75,
|
||||
"helv": 0.6,
|
||||
"iskoola pota": 0.7,
|
||||
"javanese text": 0.94,
|
||||
"kaiti": 0.45,
|
||||
"kalinga": 0.7,
|
||||
"kartika": 0.7,
|
||||
"khmer ui": 0.55,
|
||||
"kokila": 0.75,
|
||||
"lao ui": 0.55,
|
||||
"latha": 0.82,
|
||||
"leelawadee ui": 0.95,
|
||||
"mangal": 0.82,
|
||||
"meiryo": 0.8,
|
||||
"microsoft uighur": 0.55,
|
||||
"ms pmincho": 0.85,
|
||||
"ms reference sans serif": 0.72,
|
||||
"ms reference specialty": 0.68,
|
||||
"ms ui gothic": 0.75,
|
||||
"myanmar text": 0.94,
|
||||
"nyala": 0.72,
|
||||
"plantagenet cherokee": 0.78,
|
||||
"raavi": 0.72,
|
||||
"segoe fluent icons": 0.92,
|
||||
"segoe ui light": 0.72,
|
||||
"shonar bangla": 0.72,
|
||||
"shruti": 0.72,
|
||||
"simkai": 0.4,
|
||||
"small fonts": 0.65,
|
||||
"traditional arabic": 0.72,
|
||||
"tunga": 0.72,
|
||||
"urdu typesetting": 0.65,
|
||||
"utsaah": 0.72,
|
||||
"vani": 0.72,
|
||||
"vijaya": 0.72,
|
||||
"yu mincho": 0.75
|
||||
},
|
||||
"low_end": {
|
||||
"aparajita": 0.55,
|
||||
"calibri": 0.94,
|
||||
"dengxian": 0.4,
|
||||
"dfkai-sb": 0.3,
|
||||
"dokchampa": 0.3,
|
||||
"estrangelo edessa": 0.62,
|
||||
"euphemia": 0.68,
|
||||
"fangsong": 0.45,
|
||||
"gadugi": 0.93,
|
||||
"gautami": 0.65,
|
||||
"helv": 0.78,
|
||||
"iskoola pota": 0.6,
|
||||
"javanese text": 0.91,
|
||||
"kaiti": 0.35,
|
||||
"kalinga": 0.6,
|
||||
"kartika": 0.6,
|
||||
"khmer ui": 0.45,
|
||||
"kokila": 0.65,
|
||||
"lao ui": 0.45,
|
||||
"latha": 0.72,
|
||||
"leelawadee ui": 0.92,
|
||||
"mangal": 0.72,
|
||||
"meiryo": 0.7,
|
||||
"microsoft uighur": 0.48,
|
||||
"ms pmincho": 0.75,
|
||||
"ms reference sans serif": 0.62,
|
||||
"ms reference specialty": 0.58,
|
||||
"ms ui gothic": 0.9,
|
||||
"myanmar text": 0.9,
|
||||
"nyala": 0.62,
|
||||
"plantagenet cherokee": 0.68,
|
||||
"raavi": 0.62,
|
||||
"segoe fluent icons": 0.8,
|
||||
"segoe ui light": 0.88,
|
||||
"shonar bangla": 0.62,
|
||||
"shruti": 0.62,
|
||||
"simkai": 0.3,
|
||||
"small fonts": 0.83,
|
||||
"traditional arabic": 0.62,
|
||||
"tunga": 0.62,
|
||||
"urdu typesetting": 0.55,
|
||||
"utsaah": 0.62,
|
||||
"vani": 0.62,
|
||||
"vijaya": 0.62,
|
||||
"yu mincho": 0.65
|
||||
},
|
||||
"mid_range": {
|
||||
"aparajita": 0.72,
|
||||
"calibri": 0.98,
|
||||
"dengxian": 0.6,
|
||||
"dfkai-sb": 0.5,
|
||||
"dokchampa": 0.5,
|
||||
"estrangelo edessa": 0.78,
|
||||
"euphemia": 0.82,
|
||||
"fangsong": 0.65,
|
||||
"gadugi": 0.97,
|
||||
"gautami": 0.8,
|
||||
"helv": 0.85,
|
||||
"iskoola pota": 0.78,
|
||||
"javanese text": 0.96,
|
||||
"kaiti": 0.55,
|
||||
"kalinga": 0.78,
|
||||
"kartika": 0.78,
|
||||
"khmer ui": 0.65,
|
||||
"kokila": 0.8,
|
||||
"lao ui": 0.65,
|
||||
"latha": 0.85,
|
||||
"leelawadee ui": 0.97,
|
||||
"mangal": 0.85,
|
||||
"meiryo": 0.85,
|
||||
"microsoft uighur": 0.65,
|
||||
"ms pmincho": 0.88,
|
||||
"ms reference sans serif": 0.78,
|
||||
"ms reference specialty": 0.75,
|
||||
"ms ui gothic": 0.96,
|
||||
"myanmar text": 0.96,
|
||||
"nyala": 0.78,
|
||||
"plantagenet cherokee": 0.82,
|
||||
"raavi": 0.78,
|
||||
"segoe fluent icons": 0.94,
|
||||
"segoe ui light": 0.94,
|
||||
"shonar bangla": 0.78,
|
||||
"shruti": 0.78,
|
||||
"simkai": 0.5,
|
||||
"small fonts": 0.9,
|
||||
"traditional arabic": 0.78,
|
||||
"tunga": 0.78,
|
||||
"urdu typesetting": 0.72,
|
||||
"utsaah": 0.78,
|
||||
"vani": 0.78,
|
||||
"vijaya": 0.78,
|
||||
"yu mincho": 0.8
|
||||
},
|
||||
"high_end": {
|
||||
"aparajita": 0.8,
|
||||
"calibri": 0.99,
|
||||
"dengxian": 0.7,
|
||||
"dfkai-sb": 0.6,
|
||||
"dokchampa": 0.6,
|
||||
"estrangelo edessa": 0.85,
|
||||
"euphemia": 0.88,
|
||||
"fangsong": 0.72,
|
||||
"gadugi": 0.98,
|
||||
"gautami": 0.85,
|
||||
"helv": 0.88,
|
||||
"iskoola pota": 0.82,
|
||||
"javanese text": 0.97,
|
||||
"kaiti": 0.65,
|
||||
"kalinga": 0.82,
|
||||
"kartika": 0.82,
|
||||
"khmer ui": 0.72,
|
||||
"kokila": 0.85,
|
||||
"lao ui": 0.72,
|
||||
"latha": 0.9,
|
||||
"leelawadee ui": 0.98,
|
||||
"mangal": 0.9,
|
||||
"meiryo": 0.9,
|
||||
"microsoft uighur": 0.72,
|
||||
"ms pmincho": 0.92,
|
||||
"ms reference sans serif": 0.85,
|
||||
"ms reference specialty": 0.82,
|
||||
"ms ui gothic": 0.98,
|
||||
"myanmar text": 0.97,
|
||||
"nyala": 0.85,
|
||||
"plantagenet cherokee": 0.88,
|
||||
"raavi": 0.85,
|
||||
"segoe fluent icons": 0.97,
|
||||
"segoe ui light": 0.96,
|
||||
"shonar bangla": 0.85,
|
||||
"shruti": 0.85,
|
||||
"simkai": 0.6,
|
||||
"small fonts": 0.92,
|
||||
"traditional arabic": 0.85,
|
||||
"tunga": 0.85,
|
||||
"urdu typesetting": 0.8,
|
||||
"utsaah": 0.85,
|
||||
"vani": 0.85,
|
||||
"vijaya": 0.85,
|
||||
"yu mincho": 0.88
|
||||
},
|
||||
"workstation": {
|
||||
"aparajita": 0.88,
|
||||
"calibri": 0.99,
|
||||
"dengxian": 0.8,
|
||||
"dfkai-sb": 0.75,
|
||||
"dokchampa": 0.72,
|
||||
"estrangelo edessa": 0.9,
|
||||
"euphemia": 0.92,
|
||||
"fangsong": 0.82,
|
||||
"gadugi": 0.99,
|
||||
"gautami": 0.92,
|
||||
"helv": 0.9,
|
||||
"iskoola pota": 0.9,
|
||||
"javanese text": 0.98,
|
||||
"kaiti": 0.78,
|
||||
"kalinga": 0.9,
|
||||
"kartika": 0.9,
|
||||
"khmer ui": 0.8,
|
||||
"kokila": 0.92,
|
||||
"lao ui": 0.8,
|
||||
"latha": 0.95,
|
||||
"leelawadee ui": 0.99,
|
||||
"mangal": 0.95,
|
||||
"meiryo": 0.95,
|
||||
"microsoft uighur": 0.82,
|
||||
"ms pmincho": 0.95,
|
||||
"ms reference sans serif": 0.92,
|
||||
"ms reference specialty": 0.9,
|
||||
"ms ui gothic": 0.98,
|
||||
"myanmar text": 0.98,
|
||||
"nyala": 0.92,
|
||||
"plantagenet cherokee": 0.92,
|
||||
"raavi": 0.92,
|
||||
"segoe fluent icons": 0.98,
|
||||
"segoe ui light": 0.97,
|
||||
"shonar bangla": 0.92,
|
||||
"shruti": 0.92,
|
||||
"simkai": 0.72,
|
||||
"small fonts": 0.94,
|
||||
"traditional arabic": 0.92,
|
||||
"tunga": 0.92,
|
||||
"urdu typesetting": 0.88,
|
||||
"utsaah": 0.92,
|
||||
"vani": 0.92,
|
||||
"vijaya": 0.92,
|
||||
"yu mincho": 0.92
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
{
|
||||
"_meta": {
|
||||
"name": "hw_concurrency | gpu_class",
|
||||
"parents": ["gpu_class"],
|
||||
"child": "hw_concurrency",
|
||||
"source": "Curated from realistic CPU/GPU pairings (Steam HW Survey + DIY build norms + laptop SKU patterns)"
|
||||
},
|
||||
"table": {
|
||||
"integrated_old": [
|
||||
{"value": 2, "prob": 0.20},
|
||||
{"value": 4, "prob": 0.60},
|
||||
{"value": 8, "prob": 0.20}
|
||||
],
|
||||
"integrated_modern": [
|
||||
{"value": 4, "prob": 0.20},
|
||||
{"value": 6, "prob": 0.15},
|
||||
{"value": 8, "prob": 0.35},
|
||||
{"value": 12, "prob": 0.20},
|
||||
{"value": 16, "prob": 0.10}
|
||||
],
|
||||
"low_end": [
|
||||
{"value": 4, "prob": 0.25},
|
||||
{"value": 6, "prob": 0.25},
|
||||
{"value": 8, "prob": 0.35},
|
||||
{"value": 12, "prob": 0.15}
|
||||
],
|
||||
"mid_range": [
|
||||
{"value": 6, "prob": 0.15},
|
||||
{"value": 8, "prob": 0.30},
|
||||
{"value": 12, "prob": 0.30},
|
||||
{"value": 16, "prob": 0.20},
|
||||
{"value": 24, "prob": 0.05}
|
||||
],
|
||||
"high_end": [
|
||||
{"value": 8, "prob": 0.10},
|
||||
{"value": 12, "prob": 0.25},
|
||||
{"value": 16, "prob": 0.35},
|
||||
{"value": 20, "prob": 0.05},
|
||||
{"value": 24, "prob": 0.20},
|
||||
{"value": 32, "prob": 0.05}
|
||||
],
|
||||
"workstation": [
|
||||
{"value": 8, "prob": 0.15},
|
||||
{"value": 12, "prob": 0.20},
|
||||
{"value": 16, "prob": 0.30},
|
||||
{"value": 24, "prob": 0.20},
|
||||
{"value": 32, "prob": 0.15}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,337 +0,0 @@
|
|||
{
|
||||
"_meta": "hardware_concurrency given (gpu_class, intra_tier)",
|
||||
"table": {
|
||||
"[\"integrated_old\", \"budget\"]": [
|
||||
{
|
||||
"value": 2,
|
||||
"prob": 0.65
|
||||
},
|
||||
{
|
||||
"value": 4,
|
||||
"prob": 0.3
|
||||
},
|
||||
{
|
||||
"value": 8,
|
||||
"prob": 0.05
|
||||
}
|
||||
],
|
||||
"[\"integrated_old\", \"standard\"]": [
|
||||
{
|
||||
"value": 2,
|
||||
"prob": 0.3
|
||||
},
|
||||
{
|
||||
"value": 4,
|
||||
"prob": 0.55
|
||||
},
|
||||
{
|
||||
"value": 8,
|
||||
"prob": 0.15
|
||||
}
|
||||
],
|
||||
"[\"integrated_old\", \"premium\"]": [
|
||||
{
|
||||
"value": 4,
|
||||
"prob": 0.65
|
||||
},
|
||||
{
|
||||
"value": 8,
|
||||
"prob": 0.35
|
||||
}
|
||||
],
|
||||
"[\"integrated_modern\", \"budget\"]": [
|
||||
{
|
||||
"value": 6,
|
||||
"prob": 0.45
|
||||
},
|
||||
{
|
||||
"value": 8,
|
||||
"prob": 0.4
|
||||
},
|
||||
{
|
||||
"value": 12,
|
||||
"prob": 0.15
|
||||
}
|
||||
],
|
||||
"[\"integrated_modern\", \"standard\"]": [
|
||||
{
|
||||
"value": 6,
|
||||
"prob": 0.2
|
||||
},
|
||||
{
|
||||
"value": 8,
|
||||
"prob": 0.3
|
||||
},
|
||||
{
|
||||
"value": 10,
|
||||
"prob": 0.2
|
||||
},
|
||||
{
|
||||
"value": 12,
|
||||
"prob": 0.2
|
||||
},
|
||||
{
|
||||
"value": 16,
|
||||
"prob": 0.1
|
||||
}
|
||||
],
|
||||
"[\"integrated_modern\", \"premium\"]": [
|
||||
{
|
||||
"value": 8,
|
||||
"prob": 0.2
|
||||
},
|
||||
{
|
||||
"value": 10,
|
||||
"prob": 0.2
|
||||
},
|
||||
{
|
||||
"value": 12,
|
||||
"prob": 0.3
|
||||
},
|
||||
{
|
||||
"value": 14,
|
||||
"prob": 0.15
|
||||
},
|
||||
{
|
||||
"value": 16,
|
||||
"prob": 0.15
|
||||
}
|
||||
],
|
||||
"[\"low_end\", \"budget\"]": [
|
||||
{
|
||||
"value": 4,
|
||||
"prob": 0.5
|
||||
},
|
||||
{
|
||||
"value": 6,
|
||||
"prob": 0.25
|
||||
},
|
||||
{
|
||||
"value": 8,
|
||||
"prob": 0.2
|
||||
},
|
||||
{
|
||||
"value": 12,
|
||||
"prob": 0.05
|
||||
}
|
||||
],
|
||||
"[\"low_end\", \"standard\"]": [
|
||||
{
|
||||
"value": 4,
|
||||
"prob": 0.1
|
||||
},
|
||||
{
|
||||
"value": 6,
|
||||
"prob": 0.35
|
||||
},
|
||||
{
|
||||
"value": 8,
|
||||
"prob": 0.3
|
||||
},
|
||||
{
|
||||
"value": 12,
|
||||
"prob": 0.18
|
||||
},
|
||||
{
|
||||
"value": 16,
|
||||
"prob": 0.07
|
||||
}
|
||||
],
|
||||
"[\"low_end\", \"premium\"]": [
|
||||
{
|
||||
"value": 6,
|
||||
"prob": 0.1
|
||||
},
|
||||
{
|
||||
"value": 8,
|
||||
"prob": 0.3
|
||||
},
|
||||
{
|
||||
"value": 12,
|
||||
"prob": 0.3
|
||||
},
|
||||
{
|
||||
"value": 16,
|
||||
"prob": 0.22
|
||||
},
|
||||
{
|
||||
"value": 24,
|
||||
"prob": 0.08
|
||||
}
|
||||
],
|
||||
"[\"mid_range\", \"budget\"]": [
|
||||
{
|
||||
"value": 6,
|
||||
"prob": 0.55
|
||||
},
|
||||
{
|
||||
"value": 8,
|
||||
"prob": 0.3
|
||||
},
|
||||
{
|
||||
"value": 12,
|
||||
"prob": 0.15
|
||||
}
|
||||
],
|
||||
"[\"mid_range\", \"standard\"]": [
|
||||
{
|
||||
"value": 6,
|
||||
"prob": 0.4
|
||||
},
|
||||
{
|
||||
"value": 8,
|
||||
"prob": 0.3
|
||||
},
|
||||
{
|
||||
"value": 12,
|
||||
"prob": 0.18
|
||||
},
|
||||
{
|
||||
"value": 16,
|
||||
"prob": 0.1
|
||||
},
|
||||
{
|
||||
"value": 24,
|
||||
"prob": 0.02
|
||||
}
|
||||
],
|
||||
"[\"mid_range\", \"premium\"]": [
|
||||
{
|
||||
"value": 6,
|
||||
"prob": 0.15
|
||||
},
|
||||
{
|
||||
"value": 8,
|
||||
"prob": 0.45
|
||||
},
|
||||
{
|
||||
"value": 12,
|
||||
"prob": 0.2
|
||||
},
|
||||
{
|
||||
"value": 16,
|
||||
"prob": 0.15
|
||||
},
|
||||
{
|
||||
"value": 24,
|
||||
"prob": 0.05
|
||||
}
|
||||
],
|
||||
"[\"high_end\", \"budget\"]": [
|
||||
{
|
||||
"value": 6,
|
||||
"prob": 0.1
|
||||
},
|
||||
{
|
||||
"value": 8,
|
||||
"prob": 0.55
|
||||
},
|
||||
{
|
||||
"value": 12,
|
||||
"prob": 0.2
|
||||
},
|
||||
{
|
||||
"value": 16,
|
||||
"prob": 0.15
|
||||
}
|
||||
],
|
||||
"[\"high_end\", \"standard\"]": [
|
||||
{
|
||||
"value": 8,
|
||||
"prob": 0.3
|
||||
},
|
||||
{
|
||||
"value": 12,
|
||||
"prob": 0.18
|
||||
},
|
||||
{
|
||||
"value": 14,
|
||||
"prob": 0.15
|
||||
},
|
||||
{
|
||||
"value": 16,
|
||||
"prob": 0.27
|
||||
},
|
||||
{
|
||||
"value": 24,
|
||||
"prob": 0.1
|
||||
}
|
||||
],
|
||||
"[\"high_end\", \"premium\"]": [
|
||||
{
|
||||
"value": 12,
|
||||
"prob": 0.1
|
||||
},
|
||||
{
|
||||
"value": 14,
|
||||
"prob": 0.15
|
||||
},
|
||||
{
|
||||
"value": 16,
|
||||
"prob": 0.3
|
||||
},
|
||||
{
|
||||
"value": 24,
|
||||
"prob": 0.35
|
||||
},
|
||||
{
|
||||
"value": 32,
|
||||
"prob": 0.1
|
||||
}
|
||||
],
|
||||
"[\"workstation\", \"budget\"]": [
|
||||
{
|
||||
"value": 8,
|
||||
"prob": 0.4
|
||||
},
|
||||
{
|
||||
"value": 12,
|
||||
"prob": 0.3
|
||||
},
|
||||
{
|
||||
"value": 16,
|
||||
"prob": 0.2
|
||||
},
|
||||
{
|
||||
"value": 24,
|
||||
"prob": 0.1
|
||||
}
|
||||
],
|
||||
"[\"workstation\", \"standard\"]": [
|
||||
{
|
||||
"value": 12,
|
||||
"prob": 0.1
|
||||
},
|
||||
{
|
||||
"value": 16,
|
||||
"prob": 0.4
|
||||
},
|
||||
{
|
||||
"value": 24,
|
||||
"prob": 0.3
|
||||
},
|
||||
{
|
||||
"value": 32,
|
||||
"prob": 0.2
|
||||
}
|
||||
],
|
||||
"[\"workstation\", \"premium\"]": [
|
||||
{
|
||||
"value": 24,
|
||||
"prob": 0.3
|
||||
},
|
||||
{
|
||||
"value": 32,
|
||||
"prob": 0.45
|
||||
},
|
||||
{
|
||||
"value": 48,
|
||||
"prob": 0.15
|
||||
},
|
||||
{
|
||||
"value": 64,
|
||||
"prob": 0.1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
{
|
||||
"_meta": {
|
||||
"name": "intra_tier | gpu_class",
|
||||
"parents": ["gpu_class"],
|
||||
"child": "intra_tier",
|
||||
"value_type": "str (budget|standard|premium)",
|
||||
"rationale": "Hidden variable catching 'intra-class premium-ness'. Within a gpu_class, users vary in how premium their OTHER components are (RAM, SSD, cores). A mid_range GPU paired with 32 cores + 2TB SSD is a 'premium' mid_range user. Without this var, hwc/screen/storage would be statistically independent given gpu_class — unrealistic (real users pick coherent bundles). Tier distribution biased: integrated_old mostly budget/standard, workstation mostly premium."
|
||||
},
|
||||
"table": {
|
||||
"integrated_old": [{"value": "budget", "prob": 0.65}, {"value": "standard", "prob": 0.30}, {"value": "premium", "prob": 0.05}],
|
||||
"integrated_modern": [{"value": "budget", "prob": 0.30}, {"value": "standard", "prob": 0.55}, {"value": "premium", "prob": 0.15}],
|
||||
"low_end": [{"value": "budget", "prob": 0.50}, {"value": "standard", "prob": 0.40}, {"value": "premium", "prob": 0.10}],
|
||||
"mid_range": [{"value": "budget", "prob": 0.25}, {"value": "standard", "prob": 0.50}, {"value": "premium", "prob": 0.25}],
|
||||
"high_end": [{"value": "budget", "prob": 0.15}, {"value": "standard", "prob": 0.50}, {"value": "premium", "prob": 0.35}],
|
||||
"workstation": [{"value": "budget", "prob": 0.10}, {"value": "standard", "prob": 0.40}, {"value": "premium", "prob": 0.50}]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
{
|
||||
"_meta": {
|
||||
"name": "webgl.msaa-samples | gpu_class",
|
||||
"parents": ["gpu_class"],
|
||||
"child": "msaa_samples",
|
||||
"source": "High-end GPUs more likely to have MSAA enabled in games; integrated usually off"
|
||||
},
|
||||
"table": {
|
||||
"integrated_old": [{"value": 0, "prob": 0.85}, {"value": 2, "prob": 0.15}],
|
||||
"integrated_modern": [{"value": 0, "prob": 0.70}, {"value": 2, "prob": 0.25}, {"value": 4, "prob": 0.05}],
|
||||
"low_end": [{"value": 0, "prob": 0.55}, {"value": 2, "prob": 0.35}, {"value": 4, "prob": 0.10}],
|
||||
"mid_range": [{"value": 0, "prob": 0.35}, {"value": 2, "prob": 0.35}, {"value": 4, "prob": 0.30}],
|
||||
"high_end": [{"value": 0, "prob": 0.20}, {"value": 2, "prob": 0.25}, {"value": 4, "prob": 0.55}],
|
||||
"workstation": [{"value": 0, "prob": 0.50}, {"value": 2, "prob": 0.25}, {"value": 4, "prob": 0.25}]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,305 +0,0 @@
|
|||
{
|
||||
"_meta": "msaa_samples given (gpu_class, screen_tier)",
|
||||
"table": {
|
||||
"[\"integrated_old\", \"1080p\"]": [
|
||||
{
|
||||
"value": 2,
|
||||
"prob": 0.75
|
||||
},
|
||||
{
|
||||
"value": 4,
|
||||
"prob": 0.2
|
||||
},
|
||||
{
|
||||
"value": 8,
|
||||
"prob": 0.05
|
||||
}
|
||||
],
|
||||
"[\"integrated_old\", \"1440p\"]": [
|
||||
{
|
||||
"value": 2,
|
||||
"prob": 0.85
|
||||
},
|
||||
{
|
||||
"value": 4,
|
||||
"prob": 0.15
|
||||
}
|
||||
],
|
||||
"[\"integrated_old\", \"2160p\"]": [
|
||||
{
|
||||
"value": 2,
|
||||
"prob": 1.0
|
||||
}
|
||||
],
|
||||
"[\"integrated_old\", \"ultrawide\"]": [
|
||||
{
|
||||
"value": 2,
|
||||
"prob": 1.0
|
||||
}
|
||||
],
|
||||
"[\"integrated_modern\", \"1080p\"]": [
|
||||
{
|
||||
"value": 2,
|
||||
"prob": 0.5
|
||||
},
|
||||
{
|
||||
"value": 4,
|
||||
"prob": 0.4
|
||||
},
|
||||
{
|
||||
"value": 8,
|
||||
"prob": 0.1
|
||||
}
|
||||
],
|
||||
"[\"integrated_modern\", \"1440p\"]": [
|
||||
{
|
||||
"value": 2,
|
||||
"prob": 0.6
|
||||
},
|
||||
{
|
||||
"value": 4,
|
||||
"prob": 0.35
|
||||
},
|
||||
{
|
||||
"value": 8,
|
||||
"prob": 0.05
|
||||
}
|
||||
],
|
||||
"[\"integrated_modern\", \"2160p\"]": [
|
||||
{
|
||||
"value": 2,
|
||||
"prob": 0.85
|
||||
},
|
||||
{
|
||||
"value": 4,
|
||||
"prob": 0.15
|
||||
}
|
||||
],
|
||||
"[\"integrated_modern\", \"ultrawide\"]": [
|
||||
{
|
||||
"value": 2,
|
||||
"prob": 0.8
|
||||
},
|
||||
{
|
||||
"value": 4,
|
||||
"prob": 0.2
|
||||
}
|
||||
],
|
||||
"[\"low_end\", \"1080p\"]": [
|
||||
{
|
||||
"value": 2,
|
||||
"prob": 0.4
|
||||
},
|
||||
{
|
||||
"value": 4,
|
||||
"prob": 0.45
|
||||
},
|
||||
{
|
||||
"value": 8,
|
||||
"prob": 0.15
|
||||
}
|
||||
],
|
||||
"[\"low_end\", \"1440p\"]": [
|
||||
{
|
||||
"value": 2,
|
||||
"prob": 0.55
|
||||
},
|
||||
{
|
||||
"value": 4,
|
||||
"prob": 0.4
|
||||
},
|
||||
{
|
||||
"value": 8,
|
||||
"prob": 0.05
|
||||
}
|
||||
],
|
||||
"[\"low_end\", \"2160p\"]": [
|
||||
{
|
||||
"value": 2,
|
||||
"prob": 0.85
|
||||
},
|
||||
{
|
||||
"value": 4,
|
||||
"prob": 0.15
|
||||
}
|
||||
],
|
||||
"[\"low_end\", \"ultrawide\"]": [
|
||||
{
|
||||
"value": 2,
|
||||
"prob": 0.7
|
||||
},
|
||||
{
|
||||
"value": 4,
|
||||
"prob": 0.3
|
||||
}
|
||||
],
|
||||
"[\"mid_range\", \"1080p\"]": [
|
||||
{
|
||||
"value": 2,
|
||||
"prob": 0.3
|
||||
},
|
||||
{
|
||||
"value": 4,
|
||||
"prob": 0.5
|
||||
},
|
||||
{
|
||||
"value": 8,
|
||||
"prob": 0.2
|
||||
}
|
||||
],
|
||||
"[\"mid_range\", \"1440p\"]": [
|
||||
{
|
||||
"value": 2,
|
||||
"prob": 0.4
|
||||
},
|
||||
{
|
||||
"value": 4,
|
||||
"prob": 0.45
|
||||
},
|
||||
{
|
||||
"value": 8,
|
||||
"prob": 0.15
|
||||
}
|
||||
],
|
||||
"[\"mid_range\", \"2160p\"]": [
|
||||
{
|
||||
"value": 2,
|
||||
"prob": 0.65
|
||||
},
|
||||
{
|
||||
"value": 4,
|
||||
"prob": 0.3
|
||||
},
|
||||
{
|
||||
"value": 8,
|
||||
"prob": 0.05
|
||||
}
|
||||
],
|
||||
"[\"mid_range\", \"ultrawide\"]": [
|
||||
{
|
||||
"value": 2,
|
||||
"prob": 0.55
|
||||
},
|
||||
{
|
||||
"value": 4,
|
||||
"prob": 0.4
|
||||
},
|
||||
{
|
||||
"value": 8,
|
||||
"prob": 0.05
|
||||
}
|
||||
],
|
||||
"[\"high_end\", \"1080p\"]": [
|
||||
{
|
||||
"value": 2,
|
||||
"prob": 0.2
|
||||
},
|
||||
{
|
||||
"value": 4,
|
||||
"prob": 0.45
|
||||
},
|
||||
{
|
||||
"value": 8,
|
||||
"prob": 0.35
|
||||
}
|
||||
],
|
||||
"[\"high_end\", \"1440p\"]": [
|
||||
{
|
||||
"value": 2,
|
||||
"prob": 0.25
|
||||
},
|
||||
{
|
||||
"value": 4,
|
||||
"prob": 0.5
|
||||
},
|
||||
{
|
||||
"value": 8,
|
||||
"prob": 0.25
|
||||
}
|
||||
],
|
||||
"[\"high_end\", \"2160p\"]": [
|
||||
{
|
||||
"value": 2,
|
||||
"prob": 0.4
|
||||
},
|
||||
{
|
||||
"value": 4,
|
||||
"prob": 0.45
|
||||
},
|
||||
{
|
||||
"value": 8,
|
||||
"prob": 0.15
|
||||
}
|
||||
],
|
||||
"[\"high_end\", \"ultrawide\"]": [
|
||||
{
|
||||
"value": 2,
|
||||
"prob": 0.3
|
||||
},
|
||||
{
|
||||
"value": 4,
|
||||
"prob": 0.5
|
||||
},
|
||||
{
|
||||
"value": 8,
|
||||
"prob": 0.2
|
||||
}
|
||||
],
|
||||
"[\"workstation\", \"1080p\"]": [
|
||||
{
|
||||
"value": 2,
|
||||
"prob": 0.15
|
||||
},
|
||||
{
|
||||
"value": 4,
|
||||
"prob": 0.5
|
||||
},
|
||||
{
|
||||
"value": 8,
|
||||
"prob": 0.35
|
||||
}
|
||||
],
|
||||
"[\"workstation\", \"1440p\"]": [
|
||||
{
|
||||
"value": 2,
|
||||
"prob": 0.15
|
||||
},
|
||||
{
|
||||
"value": 4,
|
||||
"prob": 0.55
|
||||
},
|
||||
{
|
||||
"value": 8,
|
||||
"prob": 0.3
|
||||
}
|
||||
],
|
||||
"[\"workstation\", \"2160p\"]": [
|
||||
{
|
||||
"value": 2,
|
||||
"prob": 0.25
|
||||
},
|
||||
{
|
||||
"value": 4,
|
||||
"prob": 0.55
|
||||
},
|
||||
{
|
||||
"value": 8,
|
||||
"prob": 0.2
|
||||
}
|
||||
],
|
||||
"[\"workstation\", \"ultrawide\"]": [
|
||||
{
|
||||
"value": 2,
|
||||
"prob": 0.2
|
||||
},
|
||||
{
|
||||
"value": 4,
|
||||
"prob": 0.55
|
||||
},
|
||||
{
|
||||
"value": 8,
|
||||
"prob": 0.25
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
{
|
||||
"_meta": {
|
||||
"name": "screen | gpu_class",
|
||||
"parents": ["gpu_class"],
|
||||
"child": "screen",
|
||||
"value_shape": {"w": "int css px", "h": "int css px", "dpr": "float", "aw": "availWidth", "ah": "availHeight"},
|
||||
"source": "Curated Windows desktop/laptop resolutions. Restricted to width >= 1920 to avoid FP Pro's server-side screen_resolution normalization (FP Pro sorts non-standard landscape resolutions like 1536x864 into [smaller, larger] order in raw_device_attributes.screen_resolution, appearing flipped vs our spoof). All retained resolutions are common and NOT normalized by FP Pro."
|
||||
},
|
||||
"table": {
|
||||
"integrated_old": [
|
||||
{"value": {"w": 1920, "h": 1080, "dpr": 1.0, "aw": 1920, "ah": 1040}, "prob": 0.90},
|
||||
{"value": {"w": 1920, "h": 1200, "dpr": 1.0, "aw": 1920, "ah": 1160}, "prob": 0.10}
|
||||
],
|
||||
"integrated_modern": [
|
||||
{"value": {"w": 1920, "h": 1080, "dpr": 1.0, "aw": 1920, "ah": 1040}, "prob": 0.60},
|
||||
{"value": {"w": 1920, "h": 1080, "dpr": 1.25, "aw": 1920, "ah": 1040}, "prob": 0.18},
|
||||
{"value": {"w": 2560, "h": 1440, "dpr": 1.0, "aw": 2560, "ah": 1400}, "prob": 0.10},
|
||||
{"value": {"w": 1920, "h": 1200, "dpr": 1.0, "aw": 1920, "ah": 1160}, "prob": 0.08},
|
||||
{"value": {"w": 2560, "h": 1600, "dpr": 1.25, "aw": 2560, "ah": 1560}, "prob": 0.04}
|
||||
],
|
||||
"low_end": [
|
||||
{"value": {"w": 1920, "h": 1080, "dpr": 1.0, "aw": 1920, "ah": 1040}, "prob": 0.70},
|
||||
{"value": {"w": 2560, "h": 1440, "dpr": 1.0, "aw": 2560, "ah": 1400}, "prob": 0.20},
|
||||
{"value": {"w": 1920, "h": 1200, "dpr": 1.0, "aw": 1920, "ah": 1160}, "prob": 0.10}
|
||||
],
|
||||
"mid_range": [
|
||||
{"value": {"w": 1920, "h": 1080, "dpr": 1.0, "aw": 1920, "ah": 1040}, "prob": 0.50},
|
||||
{"value": {"w": 2560, "h": 1440, "dpr": 1.0, "aw": 2560, "ah": 1400}, "prob": 0.25},
|
||||
{"value": {"w": 1920, "h": 1080, "dpr": 1.25, "aw": 1920, "ah": 1040}, "prob": 0.08},
|
||||
{"value": {"w": 2560, "h": 1440, "dpr": 1.25, "aw": 2560, "ah": 1400}, "prob": 0.05},
|
||||
{"value": {"w": 3840, "h": 2160, "dpr": 1.0, "aw": 3840, "ah": 2120}, "prob": 0.04},
|
||||
{"value": {"w": 3840, "h": 2160, "dpr": 1.5, "aw": 3840, "ah": 2120}, "prob": 0.04},
|
||||
{"value": {"w": 2560, "h": 1080, "dpr": 1.0, "aw": 2560, "ah": 1040}, "prob": 0.04}
|
||||
],
|
||||
"high_end": [
|
||||
{"value": {"w": 1920, "h": 1080, "dpr": 1.0, "aw": 1920, "ah": 1040}, "prob": 0.25},
|
||||
{"value": {"w": 2560, "h": 1440, "dpr": 1.0, "aw": 2560, "ah": 1400}, "prob": 0.30},
|
||||
{"value": {"w": 3840, "h": 2160, "dpr": 1.0, "aw": 3840, "ah": 2120}, "prob": 0.15},
|
||||
{"value": {"w": 3840, "h": 2160, "dpr": 1.5, "aw": 3840, "ah": 2120}, "prob": 0.10},
|
||||
{"value": {"w": 3440, "h": 1440, "dpr": 1.0, "aw": 3440, "ah": 1400}, "prob": 0.08},
|
||||
{"value": {"w": 2560, "h": 1440, "dpr": 1.25, "aw": 2560, "ah": 1400}, "prob": 0.05},
|
||||
{"value": {"w": 5120, "h": 1440, "dpr": 1.0, "aw": 5120, "ah": 1400}, "prob": 0.04},
|
||||
{"value": {"w": 3840, "h": 2160, "dpr": 2.0, "aw": 3840, "ah": 2120}, "prob": 0.03}
|
||||
],
|
||||
"workstation": [
|
||||
{"value": {"w": 1920, "h": 1080, "dpr": 1.0, "aw": 1920, "ah": 1040}, "prob": 0.25},
|
||||
{"value": {"w": 2560, "h": 1440, "dpr": 1.0, "aw": 2560, "ah": 1400}, "prob": 0.25},
|
||||
{"value": {"w": 3840, "h": 2160, "dpr": 1.0, "aw": 3840, "ah": 2120}, "prob": 0.25},
|
||||
{"value": {"w": 3840, "h": 2160, "dpr": 1.5, "aw": 3840, "ah": 2120}, "prob": 0.15},
|
||||
{"value": {"w": 5120, "h": 2880, "dpr": 1.0, "aw": 5120, "ah": 2840}, "prob": 0.05},
|
||||
{"value": {"w": 3840, "h": 2400, "dpr": 1.5, "aw": 3840, "ah": 2360}, "prob": 0.05}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,761 +0,0 @@
|
|||
{
|
||||
"_meta": "screen given (gpu_class, intra_tier)",
|
||||
"table": {
|
||||
"[\"integrated_old\", \"budget\"]": [
|
||||
{
|
||||
"value": {
|
||||
"w": 1366,
|
||||
"h": 768,
|
||||
"aw": 1366,
|
||||
"ah": 728,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.78
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 1280,
|
||||
"h": 800,
|
||||
"aw": 1280,
|
||||
"ah": 760,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.15
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 1024,
|
||||
"h": 768,
|
||||
"aw": 1024,
|
||||
"ah": 728,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.07
|
||||
}
|
||||
],
|
||||
"[\"integrated_old\", \"standard\"]": [
|
||||
{
|
||||
"value": {
|
||||
"w": 1366,
|
||||
"h": 768,
|
||||
"aw": 1366,
|
||||
"ah": 728,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.55
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 1600,
|
||||
"h": 900,
|
||||
"aw": 1600,
|
||||
"ah": 860,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.25
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 1280,
|
||||
"h": 800,
|
||||
"aw": 1280,
|
||||
"ah": 760,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.1
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 1920,
|
||||
"h": 1080,
|
||||
"aw": 1920,
|
||||
"ah": 1040,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.1
|
||||
}
|
||||
],
|
||||
"[\"integrated_old\", \"premium\"]": [
|
||||
{
|
||||
"value": {
|
||||
"w": 1600,
|
||||
"h": 900,
|
||||
"aw": 1600,
|
||||
"ah": 860,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.45
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 1920,
|
||||
"h": 1080,
|
||||
"aw": 1920,
|
||||
"ah": 1040,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.4
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 1366,
|
||||
"h": 768,
|
||||
"aw": 1366,
|
||||
"ah": 728,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.15
|
||||
}
|
||||
],
|
||||
"[\"integrated_modern\", \"budget\"]": [
|
||||
{
|
||||
"value": {
|
||||
"w": 1920,
|
||||
"h": 1080,
|
||||
"aw": 1920,
|
||||
"ah": 1040,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.8
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 2560,
|
||||
"h": 1440,
|
||||
"aw": 2560,
|
||||
"ah": 1400,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.15
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 1920,
|
||||
"h": 1200,
|
||||
"aw": 1920,
|
||||
"ah": 1160,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.05
|
||||
}
|
||||
],
|
||||
"[\"integrated_modern\", \"standard\"]": [
|
||||
{
|
||||
"value": {
|
||||
"w": 1920,
|
||||
"h": 1080,
|
||||
"aw": 1920,
|
||||
"ah": 1040,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.7
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 1920,
|
||||
"h": 1200,
|
||||
"aw": 1920,
|
||||
"ah": 1160,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.1
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 2560,
|
||||
"h": 1440,
|
||||
"aw": 2560,
|
||||
"ah": 1400,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.12
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 2560,
|
||||
"h": 1600,
|
||||
"aw": 2560,
|
||||
"ah": 1560,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.08
|
||||
}
|
||||
],
|
||||
"[\"integrated_modern\", \"premium\"]": [
|
||||
{
|
||||
"value": {
|
||||
"w": 1920,
|
||||
"h": 1080,
|
||||
"aw": 1920,
|
||||
"ah": 1040,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.3
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 1920,
|
||||
"h": 1200,
|
||||
"aw": 1920,
|
||||
"ah": 1160,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.1
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 2560,
|
||||
"h": 1440,
|
||||
"aw": 2560,
|
||||
"ah": 1400,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.25
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 2560,
|
||||
"h": 1600,
|
||||
"aw": 2560,
|
||||
"ah": 1560,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.15
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 3840,
|
||||
"h": 2160,
|
||||
"aw": 3840,
|
||||
"ah": 2120,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.2
|
||||
}
|
||||
],
|
||||
"[\"low_end\", \"budget\"]": [
|
||||
{
|
||||
"value": {
|
||||
"w": 1920,
|
||||
"h": 1080,
|
||||
"aw": 1920,
|
||||
"ah": 1040,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.85
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 1920,
|
||||
"h": 1200,
|
||||
"aw": 1920,
|
||||
"ah": 1160,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.1
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 2560,
|
||||
"h": 1440,
|
||||
"aw": 2560,
|
||||
"ah": 1400,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.05
|
||||
}
|
||||
],
|
||||
"[\"low_end\", \"standard\"]": [
|
||||
{
|
||||
"value": {
|
||||
"w": 1920,
|
||||
"h": 1080,
|
||||
"aw": 1920,
|
||||
"ah": 1040,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.6
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 1920,
|
||||
"h": 1200,
|
||||
"aw": 1920,
|
||||
"ah": 1160,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.1
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 2560,
|
||||
"h": 1440,
|
||||
"aw": 2560,
|
||||
"ah": 1400,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.25
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 3840,
|
||||
"h": 2160,
|
||||
"aw": 3840,
|
||||
"ah": 2120,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.05
|
||||
}
|
||||
],
|
||||
"[\"low_end\", \"premium\"]": [
|
||||
{
|
||||
"value": {
|
||||
"w": 1920,
|
||||
"h": 1080,
|
||||
"aw": 1920,
|
||||
"ah": 1040,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.25
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 2560,
|
||||
"h": 1440,
|
||||
"aw": 2560,
|
||||
"ah": 1400,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.45
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 3840,
|
||||
"h": 2160,
|
||||
"aw": 3840,
|
||||
"ah": 2120,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.2
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 3440,
|
||||
"h": 1440,
|
||||
"aw": 3440,
|
||||
"ah": 1400,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.05
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 1920,
|
||||
"h": 1200,
|
||||
"aw": 1920,
|
||||
"ah": 1160,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.05
|
||||
}
|
||||
],
|
||||
"[\"mid_range\", \"budget\"]": [
|
||||
{
|
||||
"value": {
|
||||
"w": 1920,
|
||||
"h": 1080,
|
||||
"aw": 1920,
|
||||
"ah": 1040,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.75
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 2560,
|
||||
"h": 1440,
|
||||
"aw": 2560,
|
||||
"ah": 1400,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.2
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 1920,
|
||||
"h": 1200,
|
||||
"aw": 1920,
|
||||
"ah": 1160,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.05
|
||||
}
|
||||
],
|
||||
"[\"mid_range\", \"standard\"]": [
|
||||
{
|
||||
"value": {
|
||||
"w": 1920,
|
||||
"h": 1080,
|
||||
"aw": 1920,
|
||||
"ah": 1040,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.45
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 2560,
|
||||
"h": 1440,
|
||||
"aw": 2560,
|
||||
"ah": 1400,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.4
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 3840,
|
||||
"h": 2160,
|
||||
"aw": 3840,
|
||||
"ah": 2120,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.1
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 3440,
|
||||
"h": 1440,
|
||||
"aw": 3440,
|
||||
"ah": 1400,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.05
|
||||
}
|
||||
],
|
||||
"[\"mid_range\", \"premium\"]": [
|
||||
{
|
||||
"value": {
|
||||
"w": 1920,
|
||||
"h": 1080,
|
||||
"aw": 1920,
|
||||
"ah": 1040,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.15
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 2560,
|
||||
"h": 1440,
|
||||
"aw": 2560,
|
||||
"ah": 1400,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.5
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 3840,
|
||||
"h": 2160,
|
||||
"aw": 3840,
|
||||
"ah": 2120,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.2
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 3440,
|
||||
"h": 1440,
|
||||
"aw": 3440,
|
||||
"ah": 1400,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.1
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 2560,
|
||||
"h": 1080,
|
||||
"aw": 2560,
|
||||
"ah": 1040,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.05
|
||||
}
|
||||
],
|
||||
"[\"high_end\", \"budget\"]": [
|
||||
{
|
||||
"value": {
|
||||
"w": 1920,
|
||||
"h": 1080,
|
||||
"aw": 1920,
|
||||
"ah": 1040,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.2
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 2560,
|
||||
"h": 1440,
|
||||
"aw": 2560,
|
||||
"ah": 1400,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.55
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 3840,
|
||||
"h": 2160,
|
||||
"aw": 3840,
|
||||
"ah": 2120,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.2
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 3440,
|
||||
"h": 1440,
|
||||
"aw": 3440,
|
||||
"ah": 1400,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.05
|
||||
}
|
||||
],
|
||||
"[\"high_end\", \"standard\"]": [
|
||||
{
|
||||
"value": {
|
||||
"w": 2560,
|
||||
"h": 1440,
|
||||
"aw": 2560,
|
||||
"ah": 1400,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.4
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 3840,
|
||||
"h": 2160,
|
||||
"aw": 3840,
|
||||
"ah": 2120,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.4
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 3440,
|
||||
"h": 1440,
|
||||
"aw": 3440,
|
||||
"ah": 1400,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.15
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 5120,
|
||||
"h": 1440,
|
||||
"aw": 5120,
|
||||
"ah": 1400,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.05
|
||||
}
|
||||
],
|
||||
"[\"high_end\", \"premium\"]": [
|
||||
{
|
||||
"value": {
|
||||
"w": 3840,
|
||||
"h": 2160,
|
||||
"aw": 3840,
|
||||
"ah": 2120,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.55
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 2560,
|
||||
"h": 1440,
|
||||
"aw": 2560,
|
||||
"ah": 1400,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.15
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 3440,
|
||||
"h": 1440,
|
||||
"aw": 3440,
|
||||
"ah": 1400,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.1
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 5120,
|
||||
"h": 1440,
|
||||
"aw": 5120,
|
||||
"ah": 1400,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.15
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 7680,
|
||||
"h": 2160,
|
||||
"aw": 7680,
|
||||
"ah": 2120,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.05
|
||||
}
|
||||
],
|
||||
"[\"workstation\", \"budget\"]": [
|
||||
{
|
||||
"value": {
|
||||
"w": 2560,
|
||||
"h": 1440,
|
||||
"aw": 2560,
|
||||
"ah": 1400,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.55
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 3840,
|
||||
"h": 2160,
|
||||
"aw": 3840,
|
||||
"ah": 2120,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.3
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 1920,
|
||||
"h": 1200,
|
||||
"aw": 1920,
|
||||
"ah": 1160,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.1
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 2560,
|
||||
"h": 1600,
|
||||
"aw": 2560,
|
||||
"ah": 1560,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.05
|
||||
}
|
||||
],
|
||||
"[\"workstation\", \"standard\"]": [
|
||||
{
|
||||
"value": {
|
||||
"w": 2560,
|
||||
"h": 1440,
|
||||
"aw": 2560,
|
||||
"ah": 1400,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.3
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 3840,
|
||||
"h": 2160,
|
||||
"aw": 3840,
|
||||
"ah": 2120,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.45
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 2560,
|
||||
"h": 1600,
|
||||
"aw": 2560,
|
||||
"ah": 1560,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.15
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 3440,
|
||||
"h": 1440,
|
||||
"aw": 3440,
|
||||
"ah": 1400,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.1
|
||||
}
|
||||
],
|
||||
"[\"workstation\", \"premium\"]": [
|
||||
{
|
||||
"value": {
|
||||
"w": 3840,
|
||||
"h": 2160,
|
||||
"aw": 3840,
|
||||
"ah": 2120,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.55
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 2560,
|
||||
"h": 1600,
|
||||
"aw": 2560,
|
||||
"ah": 1560,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.15
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 5120,
|
||||
"h": 2160,
|
||||
"aw": 5120,
|
||||
"ah": 2120,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.15
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 3840,
|
||||
"h": 2400,
|
||||
"aw": 3840,
|
||||
"ah": 2360,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.1
|
||||
},
|
||||
{
|
||||
"value": {
|
||||
"w": 7680,
|
||||
"h": 2160,
|
||||
"aw": 7680,
|
||||
"ah": 2120,
|
||||
"dpr": 1.0
|
||||
},
|
||||
"prob": 0.05
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
{
|
||||
"_meta": {
|
||||
"name": "storage_quota_mb | gpu_class",
|
||||
"parents": ["gpu_class"],
|
||||
"child": "storage_quota_mb",
|
||||
"value_shape": "int megabytes (reported as navigator.storage.estimate().quota / (1024*1024))",
|
||||
"description": "Storage quota override returned by navigator.storage.estimate(). Real Firefox computes quota from disk space, which is a fingerprinting signal (stable per host). We replace it with a realistic per-session value conditioned on gpu_class — modern/high-end users tend to have larger SSDs, integrated_old older/smaller disks.",
|
||||
"source": "Typical Windows 11 disk sizes 2024-2026 per segment: budget laptops 256-512GB SSD, mainstream 512GB-1TB SSD, gaming/workstation 1-4TB SSD. Firefox default storage quota is ~50% of available disk (capped). We simulate that computation per-tier.",
|
||||
"rationale": "Values are pre-quota-limit MB as Firefox would return to navigator.storage.estimate().quota. Probabilities cover realistic ranges centered on the segment median. A single session returns one value; fingerprinters that hash the exact quota get per-session variance, matching cross-user diversity."
|
||||
},
|
||||
"table": {
|
||||
"integrated_old": [
|
||||
{"value": 61440, "prob": 0.30},
|
||||
{"value": 102400, "prob": 0.25},
|
||||
{"value": 122880, "prob": 0.20},
|
||||
{"value": 204800, "prob": 0.15},
|
||||
{"value": 40960, "prob": 0.10}
|
||||
],
|
||||
"integrated_modern": [
|
||||
{"value": 204800, "prob": 0.30},
|
||||
{"value": 307200, "prob": 0.25},
|
||||
{"value": 122880, "prob": 0.15},
|
||||
{"value": 409600, "prob": 0.15},
|
||||
{"value": 102400, "prob": 0.10},
|
||||
{"value": 512000, "prob": 0.05}
|
||||
],
|
||||
"low_end": [
|
||||
{"value": 122880, "prob": 0.30},
|
||||
{"value": 204800, "prob": 0.25},
|
||||
{"value": 102400, "prob": 0.20},
|
||||
{"value": 61440, "prob": 0.15},
|
||||
{"value": 307200, "prob": 0.10}
|
||||
],
|
||||
"mid_range": [
|
||||
{"value": 307200, "prob": 0.30},
|
||||
{"value": 409600, "prob": 0.25},
|
||||
{"value": 204800, "prob": 0.20},
|
||||
{"value": 512000, "prob": 0.15},
|
||||
{"value": 716800, "prob": 0.05},
|
||||
{"value": 122880, "prob": 0.05}
|
||||
],
|
||||
"high_end": [
|
||||
{"value": 512000, "prob": 0.25},
|
||||
{"value": 716800, "prob": 0.25},
|
||||
{"value": 409600, "prob": 0.20},
|
||||
{"value": 1024000, "prob": 0.15},
|
||||
{"value": 307200, "prob": 0.10},
|
||||
{"value": 1536000, "prob": 0.05}
|
||||
],
|
||||
"workstation": [
|
||||
{"value": 1024000, "prob": 0.25},
|
||||
{"value": 1536000, "prob": 0.20},
|
||||
{"value": 716800, "prob": 0.20},
|
||||
{"value": 2048000, "prob": 0.15},
|
||||
{"value": 512000, "prob": 0.10},
|
||||
{"value": 3072000, "prob": 0.05},
|
||||
{"value": 409600, "prob": 0.05}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,297 +0,0 @@
|
|||
{
|
||||
"_meta": "storage_quota_mb given (gpu_class, intra_tier)",
|
||||
"table": {
|
||||
"[\"integrated_old\", \"budget\"]": [
|
||||
{
|
||||
"value": 32000,
|
||||
"prob": 0.3
|
||||
},
|
||||
{
|
||||
"value": 64000,
|
||||
"prob": 0.4
|
||||
},
|
||||
{
|
||||
"value": 128000,
|
||||
"prob": 0.25
|
||||
},
|
||||
{
|
||||
"value": 256000,
|
||||
"prob": 0.05
|
||||
}
|
||||
],
|
||||
"[\"integrated_old\", \"standard\"]": [
|
||||
{
|
||||
"value": 64000,
|
||||
"prob": 0.2
|
||||
},
|
||||
{
|
||||
"value": 128000,
|
||||
"prob": 0.45
|
||||
},
|
||||
{
|
||||
"value": 256000,
|
||||
"prob": 0.25
|
||||
},
|
||||
{
|
||||
"value": 500000,
|
||||
"prob": 0.1
|
||||
}
|
||||
],
|
||||
"[\"integrated_old\", \"premium\"]": [
|
||||
{
|
||||
"value": 128000,
|
||||
"prob": 0.2
|
||||
},
|
||||
{
|
||||
"value": 256000,
|
||||
"prob": 0.45
|
||||
},
|
||||
{
|
||||
"value": 500000,
|
||||
"prob": 0.35
|
||||
}
|
||||
],
|
||||
"[\"integrated_modern\", \"budget\"]": [
|
||||
{
|
||||
"value": 256000,
|
||||
"prob": 0.3
|
||||
},
|
||||
{
|
||||
"value": 500000,
|
||||
"prob": 0.45
|
||||
},
|
||||
{
|
||||
"value": 1000000,
|
||||
"prob": 0.25
|
||||
}
|
||||
],
|
||||
"[\"integrated_modern\", \"standard\"]": [
|
||||
{
|
||||
"value": 256000,
|
||||
"prob": 0.25
|
||||
},
|
||||
{
|
||||
"value": 500000,
|
||||
"prob": 0.45
|
||||
},
|
||||
{
|
||||
"value": 1000000,
|
||||
"prob": 0.25
|
||||
},
|
||||
{
|
||||
"value": 2000000,
|
||||
"prob": 0.05
|
||||
}
|
||||
],
|
||||
"[\"integrated_modern\", \"premium\"]": [
|
||||
{
|
||||
"value": 500000,
|
||||
"prob": 0.25
|
||||
},
|
||||
{
|
||||
"value": 1000000,
|
||||
"prob": 0.5
|
||||
},
|
||||
{
|
||||
"value": 2000000,
|
||||
"prob": 0.2
|
||||
},
|
||||
{
|
||||
"value": 4000000,
|
||||
"prob": 0.05
|
||||
}
|
||||
],
|
||||
"[\"low_end\", \"budget\"]": [
|
||||
{
|
||||
"value": 128000,
|
||||
"prob": 0.2
|
||||
},
|
||||
{
|
||||
"value": 256000,
|
||||
"prob": 0.5
|
||||
},
|
||||
{
|
||||
"value": 500000,
|
||||
"prob": 0.25
|
||||
},
|
||||
{
|
||||
"value": 1000000,
|
||||
"prob": 0.05
|
||||
}
|
||||
],
|
||||
"[\"low_end\", \"standard\"]": [
|
||||
{
|
||||
"value": 256000,
|
||||
"prob": 0.2
|
||||
},
|
||||
{
|
||||
"value": 500000,
|
||||
"prob": 0.5
|
||||
},
|
||||
{
|
||||
"value": 1000000,
|
||||
"prob": 0.25
|
||||
},
|
||||
{
|
||||
"value": 2000000,
|
||||
"prob": 0.05
|
||||
}
|
||||
],
|
||||
"[\"low_end\", \"premium\"]": [
|
||||
{
|
||||
"value": 500000,
|
||||
"prob": 0.2
|
||||
},
|
||||
{
|
||||
"value": 1000000,
|
||||
"prob": 0.5
|
||||
},
|
||||
{
|
||||
"value": 2000000,
|
||||
"prob": 0.25
|
||||
},
|
||||
{
|
||||
"value": 4000000,
|
||||
"prob": 0.05
|
||||
}
|
||||
],
|
||||
"[\"mid_range\", \"budget\"]": [
|
||||
{
|
||||
"value": 256000,
|
||||
"prob": 0.15
|
||||
},
|
||||
{
|
||||
"value": 500000,
|
||||
"prob": 0.5
|
||||
},
|
||||
{
|
||||
"value": 1000000,
|
||||
"prob": 0.3
|
||||
},
|
||||
{
|
||||
"value": 2000000,
|
||||
"prob": 0.05
|
||||
}
|
||||
],
|
||||
"[\"mid_range\", \"standard\"]": [
|
||||
{
|
||||
"value": 500000,
|
||||
"prob": 0.2
|
||||
},
|
||||
{
|
||||
"value": 1000000,
|
||||
"prob": 0.55
|
||||
},
|
||||
{
|
||||
"value": 2000000,
|
||||
"prob": 0.2
|
||||
},
|
||||
{
|
||||
"value": 4000000,
|
||||
"prob": 0.05
|
||||
}
|
||||
],
|
||||
"[\"mid_range\", \"premium\"]": [
|
||||
{
|
||||
"value": 1000000,
|
||||
"prob": 0.4
|
||||
},
|
||||
{
|
||||
"value": 2000000,
|
||||
"prob": 0.45
|
||||
},
|
||||
{
|
||||
"value": 4000000,
|
||||
"prob": 0.15
|
||||
}
|
||||
],
|
||||
"[\"high_end\", \"budget\"]": [
|
||||
{
|
||||
"value": 500000,
|
||||
"prob": 0.15
|
||||
},
|
||||
{
|
||||
"value": 1000000,
|
||||
"prob": 0.5
|
||||
},
|
||||
{
|
||||
"value": 2000000,
|
||||
"prob": 0.3
|
||||
},
|
||||
{
|
||||
"value": 4000000,
|
||||
"prob": 0.05
|
||||
}
|
||||
],
|
||||
"[\"high_end\", \"standard\"]": [
|
||||
{
|
||||
"value": 1000000,
|
||||
"prob": 0.3
|
||||
},
|
||||
{
|
||||
"value": 2000000,
|
||||
"prob": 0.5
|
||||
},
|
||||
{
|
||||
"value": 4000000,
|
||||
"prob": 0.2
|
||||
}
|
||||
],
|
||||
"[\"high_end\", \"premium\"]": [
|
||||
{
|
||||
"value": 2000000,
|
||||
"prob": 0.4
|
||||
},
|
||||
{
|
||||
"value": 4000000,
|
||||
"prob": 0.45
|
||||
},
|
||||
{
|
||||
"value": 8000000,
|
||||
"prob": 0.15
|
||||
}
|
||||
],
|
||||
"[\"workstation\", \"budget\"]": [
|
||||
{
|
||||
"value": 1000000,
|
||||
"prob": 0.3
|
||||
},
|
||||
{
|
||||
"value": 2000000,
|
||||
"prob": 0.5
|
||||
},
|
||||
{
|
||||
"value": 4000000,
|
||||
"prob": 0.2
|
||||
}
|
||||
],
|
||||
"[\"workstation\", \"standard\"]": [
|
||||
{
|
||||
"value": 2000000,
|
||||
"prob": 0.3
|
||||
},
|
||||
{
|
||||
"value": 4000000,
|
||||
"prob": 0.5
|
||||
},
|
||||
{
|
||||
"value": 8000000,
|
||||
"prob": 0.2
|
||||
}
|
||||
],
|
||||
"[\"workstation\", \"premium\"]": [
|
||||
{
|
||||
"value": 4000000,
|
||||
"prob": 0.35
|
||||
},
|
||||
{
|
||||
"value": 8000000,
|
||||
"prob": 0.45
|
||||
},
|
||||
{
|
||||
"value": 16000000,
|
||||
"prob": 0.2
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,650 +0,0 @@
|
|||
{
|
||||
"_source": "browserforge fingerprint-network.zip, FF/Windows slice, 3 userAgent leaves merged",
|
||||
"_ff_win_uas": [
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:145.0) Gecko/20100101 Firefox/145.0",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:135.0) Gecko/20100101 Firefox/135.0",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:147.0) Gecko/20100101 Firefox/147.0"
|
||||
],
|
||||
"_note": "Per-node weighted distributions. Marginal over the 3 Firefox/Windows UAs present in browserforge data.",
|
||||
"distributions": {
|
||||
"appCodeName": [
|
||||
[
|
||||
"Mozilla",
|
||||
1.0
|
||||
]
|
||||
],
|
||||
"appVersion": [
|
||||
[
|
||||
"5.0 (Windows)",
|
||||
1.0
|
||||
]
|
||||
],
|
||||
"doNotTrack": [
|
||||
[
|
||||
"1",
|
||||
1.0
|
||||
]
|
||||
],
|
||||
"extraProperties": [
|
||||
[
|
||||
{
|
||||
"vendorFlavors": [],
|
||||
"globalPrivacyControl": true,
|
||||
"pdfViewerEnabled": true,
|
||||
"installedApps": []
|
||||
},
|
||||
0.6111111111111112
|
||||
],
|
||||
[
|
||||
{
|
||||
"vendorFlavors": [],
|
||||
"globalPrivacyControl": null,
|
||||
"pdfViewerEnabled": null,
|
||||
"installedApps": []
|
||||
},
|
||||
0.3888888888888889
|
||||
]
|
||||
],
|
||||
"maxTouchPoints": [
|
||||
[
|
||||
0,
|
||||
0.5777777777777778
|
||||
],
|
||||
[
|
||||
1,
|
||||
0.3666666666666667
|
||||
],
|
||||
[
|
||||
10,
|
||||
0.05555555555555555
|
||||
]
|
||||
],
|
||||
"oscpu": [
|
||||
[
|
||||
"Windows NT 10.0; Win64; x64",
|
||||
1.0
|
||||
]
|
||||
],
|
||||
"webdriver": [
|
||||
[
|
||||
false,
|
||||
1.0
|
||||
]
|
||||
],
|
||||
"productSub": [
|
||||
[
|
||||
"20100101",
|
||||
1.0
|
||||
]
|
||||
],
|
||||
"hardwareConcurrency": [
|
||||
[
|
||||
12,
|
||||
0.3333333333333333
|
||||
],
|
||||
[
|
||||
16,
|
||||
0.2777777777777778
|
||||
],
|
||||
[
|
||||
8,
|
||||
0.13333333333333333
|
||||
],
|
||||
[
|
||||
24,
|
||||
0.13333333333333333
|
||||
],
|
||||
[
|
||||
32,
|
||||
0.06666666666666667
|
||||
],
|
||||
[
|
||||
6,
|
||||
0.05555555555555555
|
||||
]
|
||||
],
|
||||
"platform": [
|
||||
[
|
||||
"Win32",
|
||||
1.0
|
||||
]
|
||||
],
|
||||
"screen": [
|
||||
[
|
||||
{
|
||||
"availTop": 0,
|
||||
"availLeft": 0,
|
||||
"pageXOffset": 0,
|
||||
"pageYOffset": 0,
|
||||
"screenX": 772,
|
||||
"hasHDR": false,
|
||||
"width": 1536,
|
||||
"height": 960,
|
||||
"availWidth": 1536,
|
||||
"availHeight": 960,
|
||||
"clientWidth": 0,
|
||||
"clientHeight": 21,
|
||||
"innerWidth": 0,
|
||||
"innerHeight": 0,
|
||||
"outerWidth": 751,
|
||||
"outerHeight": 886,
|
||||
"colorDepth": 24,
|
||||
"pixelDepth": 24,
|
||||
"devicePixelRatio": 1.25
|
||||
},
|
||||
0.3333333333333333
|
||||
],
|
||||
[
|
||||
{
|
||||
"availTop": 0,
|
||||
"availLeft": 2176,
|
||||
"pageXOffset": 0,
|
||||
"pageYOffset": 0,
|
||||
"screenX": 2169,
|
||||
"hasHDR": false,
|
||||
"width": 2176,
|
||||
"height": 1224,
|
||||
"availWidth": 2176,
|
||||
"availHeight": 1176,
|
||||
"clientWidth": 0,
|
||||
"clientHeight": 19,
|
||||
"innerWidth": 0,
|
||||
"innerHeight": 0,
|
||||
"outerWidth": 2190,
|
||||
"outerHeight": 1190,
|
||||
"colorDepth": 24,
|
||||
"pixelDepth": 24,
|
||||
"devicePixelRatio": 1.7647058823529411
|
||||
},
|
||||
0.13333333333333333
|
||||
],
|
||||
[
|
||||
{
|
||||
"availTop": 0,
|
||||
"availLeft": 0,
|
||||
"pageXOffset": 0,
|
||||
"pageYOffset": 0,
|
||||
"screenX": 318,
|
||||
"hasHDR": false,
|
||||
"width": 2560,
|
||||
"height": 1440,
|
||||
"availWidth": 2560,
|
||||
"availHeight": 1440,
|
||||
"clientWidth": 0,
|
||||
"clientHeight": 18,
|
||||
"innerWidth": 0,
|
||||
"innerHeight": 0,
|
||||
"outerWidth": 2121,
|
||||
"outerHeight": 1191,
|
||||
"colorDepth": 24,
|
||||
"pixelDepth": 24,
|
||||
"devicePixelRatio": 1
|
||||
},
|
||||
0.1111111111111111
|
||||
],
|
||||
[
|
||||
{
|
||||
"availTop": 0,
|
||||
"availLeft": 0,
|
||||
"pageXOffset": 0,
|
||||
"pageYOffset": 0,
|
||||
"screenX": 652,
|
||||
"hasHDR": false,
|
||||
"width": 2752,
|
||||
"height": 1152,
|
||||
"availWidth": 2752,
|
||||
"availHeight": 1104,
|
||||
"clientWidth": 0,
|
||||
"clientHeight": 19,
|
||||
"innerWidth": 0,
|
||||
"innerHeight": 0,
|
||||
"outerWidth": 1806,
|
||||
"outerHeight": 1104,
|
||||
"colorDepth": 24,
|
||||
"pixelDepth": 24,
|
||||
"devicePixelRatio": 1.25
|
||||
},
|
||||
0.06666666666666667
|
||||
],
|
||||
[
|
||||
{
|
||||
"availTop": 0,
|
||||
"availLeft": 0,
|
||||
"pageXOffset": 0,
|
||||
"pageYOffset": 0,
|
||||
"screenX": 1912,
|
||||
"hasHDR": false,
|
||||
"width": 1920,
|
||||
"height": 1080,
|
||||
"availWidth": 1920,
|
||||
"availHeight": 1032,
|
||||
"clientWidth": 0,
|
||||
"clientHeight": 18,
|
||||
"innerWidth": 0,
|
||||
"innerHeight": 0,
|
||||
"outerWidth": 1936,
|
||||
"outerHeight": 1048,
|
||||
"colorDepth": 24,
|
||||
"pixelDepth": 24,
|
||||
"devicePixelRatio": 1
|
||||
},
|
||||
0.06666666666666667
|
||||
],
|
||||
[
|
||||
{
|
||||
"availTop": 29,
|
||||
"availLeft": 21,
|
||||
"pageXOffset": 0,
|
||||
"pageYOffset": 0,
|
||||
"screenX": 15,
|
||||
"hasHDR": false,
|
||||
"width": 1376,
|
||||
"height": 774,
|
||||
"availWidth": 1355,
|
||||
"availHeight": 745,
|
||||
"clientWidth": 0,
|
||||
"clientHeight": 21,
|
||||
"innerWidth": 0,
|
||||
"innerHeight": 0,
|
||||
"outerWidth": 1352,
|
||||
"outerHeight": 749,
|
||||
"colorDepth": 24,
|
||||
"pixelDepth": 24,
|
||||
"devicePixelRatio": 1
|
||||
},
|
||||
0.05555555555555555
|
||||
],
|
||||
[
|
||||
{
|
||||
"availTop": 0,
|
||||
"availLeft": 0,
|
||||
"pageXOffset": 0,
|
||||
"pageYOffset": 0,
|
||||
"screenX": 58,
|
||||
"hasHDR": false,
|
||||
"width": 1707,
|
||||
"height": 960,
|
||||
"availWidth": 1707,
|
||||
"availHeight": 928,
|
||||
"clientWidth": 0,
|
||||
"clientHeight": 19,
|
||||
"innerWidth": 0,
|
||||
"innerHeight": 0,
|
||||
"outerWidth": 1564,
|
||||
"outerHeight": 793,
|
||||
"colorDepth": 24,
|
||||
"pixelDepth": 24,
|
||||
"devicePixelRatio": 1
|
||||
},
|
||||
0.05555555555555555
|
||||
],
|
||||
[
|
||||
{
|
||||
"availTop": 0,
|
||||
"availLeft": 0,
|
||||
"pageXOffset": 0,
|
||||
"pageYOffset": 0,
|
||||
"screenX": 34,
|
||||
"hasHDR": false,
|
||||
"width": 5120,
|
||||
"height": 1440,
|
||||
"availWidth": 5120,
|
||||
"availHeight": 1392,
|
||||
"clientWidth": 0,
|
||||
"clientHeight": 18,
|
||||
"innerWidth": 0,
|
||||
"innerHeight": 0,
|
||||
"outerWidth": 2462,
|
||||
"outerHeight": 1399,
|
||||
"colorDepth": 24,
|
||||
"pixelDepth": 24,
|
||||
"devicePixelRatio": 1
|
||||
},
|
||||
0.05555555555555555
|
||||
],
|
||||
[
|
||||
{
|
||||
"availTop": 0,
|
||||
"availLeft": 1920,
|
||||
"pageXOffset": 0,
|
||||
"pageYOffset": 0,
|
||||
"screenX": 1912,
|
||||
"hasHDR": false,
|
||||
"width": 1920,
|
||||
"height": 1080,
|
||||
"availWidth": 1920,
|
||||
"availHeight": 1032,
|
||||
"clientWidth": 0,
|
||||
"clientHeight": 18,
|
||||
"innerWidth": 0,
|
||||
"innerHeight": 0,
|
||||
"outerWidth": 1936,
|
||||
"outerHeight": 1048,
|
||||
"colorDepth": 24,
|
||||
"pixelDepth": 24,
|
||||
"devicePixelRatio": 1
|
||||
},
|
||||
0.05555555555555555
|
||||
],
|
||||
[
|
||||
{
|
||||
"availTop": 0,
|
||||
"availLeft": 0,
|
||||
"pageXOffset": 0,
|
||||
"pageYOffset": 0,
|
||||
"screenX": 95,
|
||||
"hasHDR": false,
|
||||
"width": 1366,
|
||||
"height": 768,
|
||||
"availWidth": 1366,
|
||||
"availHeight": 768,
|
||||
"clientWidth": 0,
|
||||
"clientHeight": 18,
|
||||
"innerWidth": 0,
|
||||
"innerHeight": 0,
|
||||
"outerWidth": 1294,
|
||||
"outerHeight": 1280,
|
||||
"colorDepth": 24,
|
||||
"pixelDepth": 24,
|
||||
"devicePixelRatio": 1
|
||||
},
|
||||
0.03333333333333333
|
||||
],
|
||||
[
|
||||
{
|
||||
"availTop": 0,
|
||||
"availLeft": 0,
|
||||
"pageXOffset": 0,
|
||||
"pageYOffset": 0,
|
||||
"screenX": 1273,
|
||||
"hasHDR": false,
|
||||
"width": 2560,
|
||||
"height": 1440,
|
||||
"availWidth": 2560,
|
||||
"availHeight": 1400,
|
||||
"clientWidth": 0,
|
||||
"clientHeight": 18,
|
||||
"innerWidth": 0,
|
||||
"innerHeight": 0,
|
||||
"outerWidth": 1294,
|
||||
"outerHeight": 1407,
|
||||
"colorDepth": 24,
|
||||
"pixelDepth": 24,
|
||||
"devicePixelRatio": 1
|
||||
},
|
||||
0.03333333333333333
|
||||
]
|
||||
],
|
||||
"pluginsData": [
|
||||
[
|
||||
{
|
||||
"plugins": [
|
||||
{
|
||||
"name": "PDF Viewer",
|
||||
"description": "Portable Document Format",
|
||||
"filename": "internal-pdf-viewer",
|
||||
"mimeTypes": [
|
||||
{
|
||||
"type": "application/pdf",
|
||||
"suffixes": "pdf",
|
||||
"description": "Portable Document Format",
|
||||
"enabledPlugin": "PDF Viewer"
|
||||
},
|
||||
{
|
||||
"type": "text/pdf",
|
||||
"suffixes": "pdf",
|
||||
"description": "Portable Document Format",
|
||||
"enabledPlugin": "PDF Viewer"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Chrome PDF Viewer",
|
||||
"description": "Portable Document Format",
|
||||
"filename": "internal-pdf-viewer",
|
||||
"mimeTypes": [
|
||||
{
|
||||
"type": "application/pdf",
|
||||
"suffixes": "pdf",
|
||||
"description": "Portable Document Format",
|
||||
"enabledPlugin": "PDF Viewer"
|
||||
},
|
||||
{
|
||||
"type": "text/pdf",
|
||||
"suffixes": "pdf",
|
||||
"description": "Portable Document Format",
|
||||
"enabledPlugin": "PDF Viewer"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Chromium PDF Viewer",
|
||||
"description": "Portable Document Format",
|
||||
"filename": "internal-pdf-viewer",
|
||||
"mimeTypes": [
|
||||
{
|
||||
"type": "application/pdf",
|
||||
"suffixes": "pdf",
|
||||
"description": "Portable Document Format",
|
||||
"enabledPlugin": "PDF Viewer"
|
||||
},
|
||||
{
|
||||
"type": "text/pdf",
|
||||
"suffixes": "pdf",
|
||||
"description": "Portable Document Format",
|
||||
"enabledPlugin": "PDF Viewer"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Microsoft Edge PDF Viewer",
|
||||
"description": "Portable Document Format",
|
||||
"filename": "internal-pdf-viewer",
|
||||
"mimeTypes": [
|
||||
{
|
||||
"type": "application/pdf",
|
||||
"suffixes": "pdf",
|
||||
"description": "Portable Document Format",
|
||||
"enabledPlugin": "PDF Viewer"
|
||||
},
|
||||
{
|
||||
"type": "text/pdf",
|
||||
"suffixes": "pdf",
|
||||
"description": "Portable Document Format",
|
||||
"enabledPlugin": "PDF Viewer"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "WebKit built-in PDF",
|
||||
"description": "Portable Document Format",
|
||||
"filename": "internal-pdf-viewer",
|
||||
"mimeTypes": [
|
||||
{
|
||||
"type": "application/pdf",
|
||||
"suffixes": "pdf",
|
||||
"description": "Portable Document Format",
|
||||
"enabledPlugin": "PDF Viewer"
|
||||
},
|
||||
{
|
||||
"type": "text/pdf",
|
||||
"suffixes": "pdf",
|
||||
"description": "Portable Document Format",
|
||||
"enabledPlugin": "PDF Viewer"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"mimeTypes": [
|
||||
"Portable Document Format~~application/pdf~~pdf",
|
||||
"Portable Document Format~~text/pdf~~pdf"
|
||||
]
|
||||
},
|
||||
0.6111111111111112
|
||||
],
|
||||
[
|
||||
{
|
||||
"plugins": [],
|
||||
"mimeTypes": []
|
||||
},
|
||||
0.3888888888888889
|
||||
]
|
||||
],
|
||||
"audioCodecs": [
|
||||
[
|
||||
{
|
||||
"ogg": "probably",
|
||||
"mp3": "maybe",
|
||||
"wav": "probably",
|
||||
"m4a": "maybe",
|
||||
"aac": "maybe"
|
||||
},
|
||||
1.0
|
||||
]
|
||||
],
|
||||
"videoCodecs": [
|
||||
[
|
||||
{
|
||||
"ogg": "",
|
||||
"h264": "probably",
|
||||
"webm": "probably"
|
||||
},
|
||||
1.0
|
||||
]
|
||||
],
|
||||
"videoCard": [
|
||||
[
|
||||
{
|
||||
"renderer": "ANGLE (NVIDIA, NVIDIA GeForce GTX 980 Direct3D11 vs_5_0 ps_5_0), or similar",
|
||||
"vendor": "Google Inc. (NVIDIA)"
|
||||
},
|
||||
0.8444444444444444
|
||||
],
|
||||
[
|
||||
{
|
||||
"renderer": "ANGLE (AMD, Radeon R9 200 Series Direct3D11 vs_5_0 ps_5_0), or similar",
|
||||
"vendor": "Google Inc. (AMD)"
|
||||
},
|
||||
0.08888888888888889
|
||||
],
|
||||
[
|
||||
{
|
||||
"renderer": "ANGLE (Intel, Intel(R) HD Graphics 400 Direct3D11 vs_5_0 ps_5_0), or similar",
|
||||
"vendor": "Google Inc. (Intel)"
|
||||
},
|
||||
0.06666666666666667
|
||||
]
|
||||
],
|
||||
"multimediaDevices": [
|
||||
[
|
||||
{
|
||||
"speakers": [],
|
||||
"micros": [],
|
||||
"webcams": []
|
||||
},
|
||||
1.0
|
||||
]
|
||||
],
|
||||
"fonts": [
|
||||
[
|
||||
[],
|
||||
0.3888888888888889
|
||||
],
|
||||
[
|
||||
[
|
||||
"Calibri",
|
||||
"HELV",
|
||||
"MS UI Gothic",
|
||||
"Marlett",
|
||||
"Segoe UI Light",
|
||||
"Small Fonts"
|
||||
],
|
||||
0.3222222222222222
|
||||
],
|
||||
[
|
||||
[
|
||||
"Agency FB",
|
||||
"Calibri",
|
||||
"Century",
|
||||
"Century Gothic",
|
||||
"Franklin Gothic",
|
||||
"HELV",
|
||||
"Haettenschweiler",
|
||||
"Leelawadee",
|
||||
"Lucida Bright",
|
||||
"Lucida Sans",
|
||||
"MS Outlook",
|
||||
"MS Reference Specialty",
|
||||
"MS UI Gothic",
|
||||
"MT Extra",
|
||||
"Marlett",
|
||||
"Microsoft Uighur",
|
||||
"Monotype Corsiva",
|
||||
"Pristina",
|
||||
"Segoe UI Light",
|
||||
"Small Fonts"
|
||||
],
|
||||
0.13333333333333333
|
||||
],
|
||||
[
|
||||
[
|
||||
"AvantGarde Bk BT",
|
||||
"Calibri",
|
||||
"Clarendon",
|
||||
"Franklin Gothic",
|
||||
"Futura Bk BT",
|
||||
"Futura Md BT",
|
||||
"GOTHAM",
|
||||
"HELV",
|
||||
"Humanst521 BT",
|
||||
"MS UI Gothic",
|
||||
"MYRIAD PRO",
|
||||
"Marlett",
|
||||
"Minion Pro",
|
||||
"Segoe UI Light",
|
||||
"Small Fonts",
|
||||
"TRAJAN PRO"
|
||||
],
|
||||
0.06666666666666667
|
||||
],
|
||||
[
|
||||
[
|
||||
"Calibri",
|
||||
"MS UI Gothic",
|
||||
"Marlett",
|
||||
"Segoe UI Light"
|
||||
],
|
||||
0.05555555555555555
|
||||
],
|
||||
[
|
||||
[
|
||||
"Agency FB",
|
||||
"Calibri",
|
||||
"Century",
|
||||
"Century Gothic",
|
||||
"Franklin Gothic",
|
||||
"Futura Bk BT",
|
||||
"Futura Md BT",
|
||||
"GOTHAM",
|
||||
"HELV",
|
||||
"Haettenschweiler",
|
||||
"Humanst521 BT",
|
||||
"Lucida Bright",
|
||||
"Lucida Sans",
|
||||
"MS Outlook",
|
||||
"MS Reference Specialty",
|
||||
"MS UI Gothic",
|
||||
"MT Extra",
|
||||
"MYRIAD PRO",
|
||||
"Marlett",
|
||||
"Minion Pro",
|
||||
"Monotype Corsiva",
|
||||
"Pristina",
|
||||
"Segoe UI Light",
|
||||
"Small Fonts"
|
||||
],
|
||||
0.03333333333333333
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,173 +0,0 @@
|
|||
{
|
||||
"core": [
|
||||
{
|
||||
"name": "arial"
|
||||
},
|
||||
{
|
||||
"name": "calibri"
|
||||
},
|
||||
{
|
||||
"name": "cambria"
|
||||
},
|
||||
{
|
||||
"name": "cambria math"
|
||||
},
|
||||
{
|
||||
"name": "candara"
|
||||
},
|
||||
{
|
||||
"name": "comic sans ms"
|
||||
},
|
||||
{
|
||||
"name": "consolas"
|
||||
},
|
||||
{
|
||||
"name": "constantia"
|
||||
},
|
||||
{
|
||||
"name": "corbel"
|
||||
},
|
||||
{
|
||||
"name": "courier new"
|
||||
},
|
||||
{
|
||||
"name": "ebrima"
|
||||
},
|
||||
{
|
||||
"name": "franklin gothic medium"
|
||||
},
|
||||
{
|
||||
"name": "gabriola"
|
||||
},
|
||||
{
|
||||
"name": "gadugi"
|
||||
},
|
||||
{
|
||||
"name": "georgia"
|
||||
},
|
||||
{
|
||||
"name": "impact"
|
||||
},
|
||||
{
|
||||
"name": "javanese text"
|
||||
},
|
||||
{
|
||||
"name": "lucida console"
|
||||
},
|
||||
{
|
||||
"name": "lucida sans unicode"
|
||||
},
|
||||
{
|
||||
"name": "ms gothic"
|
||||
},
|
||||
{
|
||||
"name": "ms pgothic"
|
||||
},
|
||||
{
|
||||
"name": "mv boli"
|
||||
},
|
||||
{
|
||||
"name": "malgun gothic"
|
||||
},
|
||||
{
|
||||
"name": "marlett"
|
||||
},
|
||||
{
|
||||
"name": "microsoft himalaya"
|
||||
},
|
||||
{
|
||||
"name": "microsoft jhenghei"
|
||||
},
|
||||
{
|
||||
"name": "microsoft jhenghei ui"
|
||||
},
|
||||
{
|
||||
"name": "microsoft new tai lue"
|
||||
},
|
||||
{
|
||||
"name": "microsoft phagspa"
|
||||
},
|
||||
{
|
||||
"name": "microsoft sans serif"
|
||||
},
|
||||
{
|
||||
"name": "microsoft yahei"
|
||||
},
|
||||
{
|
||||
"name": "microsoft yi baiti"
|
||||
},
|
||||
{
|
||||
"name": "mingliu-extb"
|
||||
},
|
||||
{
|
||||
"name": "mongolian baiti"
|
||||
},
|
||||
{
|
||||
"name": "myanmar text"
|
||||
},
|
||||
{
|
||||
"name": "pmingliu-extb"
|
||||
},
|
||||
{
|
||||
"name": "palatino linotype"
|
||||
},
|
||||
{
|
||||
"name": "segoe print"
|
||||
},
|
||||
{
|
||||
"name": "segoe script"
|
||||
},
|
||||
{
|
||||
"name": "segoe ui"
|
||||
},
|
||||
{
|
||||
"name": "segoe ui symbol"
|
||||
},
|
||||
{
|
||||
"name": "simsun"
|
||||
},
|
||||
{
|
||||
"name": "simsun-extb"
|
||||
},
|
||||
{
|
||||
"name": "sitka small"
|
||||
},
|
||||
{
|
||||
"name": "sylfaen"
|
||||
},
|
||||
{
|
||||
"name": "trebuchet ms"
|
||||
},
|
||||
{
|
||||
"name": "verdana"
|
||||
},
|
||||
{
|
||||
"name": "webdings"
|
||||
},
|
||||
{
|
||||
"name": "yu gothic"
|
||||
},
|
||||
{
|
||||
"name": "courier"
|
||||
},
|
||||
{
|
||||
"name": "helvetica"
|
||||
},
|
||||
{
|
||||
"name": "segoe ui light"
|
||||
}
|
||||
],
|
||||
"optional": [],
|
||||
"profiles": [
|
||||
{
|
||||
"name": "win_clean",
|
||||
"weight": 35,
|
||||
"optional": []
|
||||
},
|
||||
{
|
||||
"name": "win_office",
|
||||
"weight": 65,
|
||||
"optional": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
{
|
||||
"_meta": {
|
||||
"name": "audio (joint: sample_rate, output_latency_ms, max_channel_count)",
|
||||
"parents": [],
|
||||
"child": "audio",
|
||||
"source": "Curated from common Windows WASAPI/DirectSound configs on FF desktop"
|
||||
},
|
||||
"table": [
|
||||
{"value": {"rate": 44100, "latency": 40, "channels": 2}, "prob": 0.20},
|
||||
{"value": {"rate": 48000, "latency": 30, "channels": 2}, "prob": 0.25},
|
||||
{"value": {"rate": 48000, "latency": 20, "channels": 2}, "prob": 0.15},
|
||||
{"value": {"rate": 48000, "latency": 40, "channels": 6}, "prob": 0.08},
|
||||
{"value": {"rate": 48000, "latency": 60, "channels": 2}, "prob": 0.12},
|
||||
{"value": {"rate": 44100, "latency": 50, "channels": 2}, "prob": 0.10},
|
||||
{"value": {"rate": 48000, "latency": 25, "channels": 2}, "prob": 0.10}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
{
|
||||
"_meta": {
|
||||
"name": "Independent marginal priors (no parents)",
|
||||
"source": "StatCounter GlobalStats + Firefox defaults + community data 2025-2026",
|
||||
"note": "Codec prefs (av1_enabled, webm_encoder_enabled, hw_video_decoding, wmf_enabled, ffvpx_enabled) moved to cpt_codec_given_class.json — they correlate with GPU class via Firefox version / user-tier distribution."
|
||||
},
|
||||
"dark_theme": {
|
||||
"_note": "0=light, 1=dark. StatCounter ~40% users report dark theme preference on desktop.",
|
||||
"table": [
|
||||
{"value": 0, "prob": 0.60},
|
||||
{"value": 1, "prob": 0.40}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,476 +0,0 @@
|
|||
{
|
||||
"win": [
|
||||
{
|
||||
"vendor": "Google Inc. (NVIDIA)",
|
||||
"renderer_out": "ANGLE (NVIDIA, NVIDIA GeForce GTX 980 Direct3D11 vs_5_0 ps_5_0), or similar",
|
||||
"prob": 0.470921,
|
||||
"prefs": {
|
||||
"zoom.stealth.webgl.renderer": "ANGLE (NVIDIA, NVIDIA GeForce GTX 980 Direct3D11 vs_5_0 ps_5_0, D3D11)",
|
||||
"zoom.stealth.webgl.vendor": "Google Inc. (NVIDIA)",
|
||||
"zoom.stealth.webgl.extensions": "ANGLE_instanced_arrays,EXT_blend_minmax,EXT_color_buffer_half_float,EXT_float_blend,EXT_frag_depth,EXT_shader_texture_lod,EXT_sRGB,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_element_index_uint,OES_fbo_render_mipmap,OES_standard_derivatives,OES_texture_float,OES_texture_float_linear,OES_texture_half_float,OES_texture_half_float_linear,OES_vertex_array_object,WEBGL_color_buffer_float,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_depth_texture,WEBGL_draw_buffers,WEBGL_lose_context,WEBGL_provoking_vertex",
|
||||
"zoom.stealth.webgl2.extensions": "EXT_color_buffer_float,EXT_float_blend,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_draw_buffers_indexed,OES_texture_float_linear,OVR_multiview2,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_lose_context,WEBGL_provoking_vertex",
|
||||
"zoom.stealth.webgl.int_params": "2849|1,2885|1029,2886|2305,2931|1,2932|513,2961|0,2962|519,2963|2147483647,2964|7680,2965|7680,2966|7680,2967|0,2968|2147483647,3074|1029,3314|0,3315|0,3316|0,3317|4,3330|0,3331|0,3332|0,3333|4,3379|16384,3408|4,3410|8,3411|8,3412|8,3413|8,3414|24,3415|0,10752|0,32777|32774,32824|0,32877|0,32878|0,32883|2048,32936|1,32937|4,32938|1,32968|0,32969|1,32970|0,32971|1,33000|2147483647,33001|2147483647,33170|4352,34016|33984,34024|16384,34045|2,34076|16384,34816|519,34817|7680,34818|7680,34819|7680,34852|8,34853|1029,34854|0,34855|0,34856|0,34857|0,34858|0,34859|0,34860|0,34877|32774,34921|16,34930|16,35071|2048,35076|-8,35077|7,35371|12,35373|12,35374|24,35375|24,35376|65536,35377|212988,35379|200704,35380|256,35657|4096,35658|16380,35659|120,35660|16,35661|32,35723|4352,35738|5121,35739|6408,35968|4,35978|120,35979|4,36003|0,36004|2147483647,36005|2147483647,36063|8,36183|8,36203|4294967294,36347|4095,36348|30,36349|1024,37137|0,37154|120,37157|120,37443|37444,37447|1000000000",
|
||||
"zoom.stealth.webgl.int2_params": "2928|0:1,3386|32767:32767,33901|1:1024,33902|1:1",
|
||||
"zoom.stealth.webgl.float_params": "",
|
||||
"zoom.stealth.webgl.shader_precisions": "35633*36336|127:127:23,35633*36337|127:127:23,35633*36338|127:127:23,35633*36339|31:30:0,35633*36340|31:30:0,35633*36341|31:30:0,35632*36336|127:127:23,35632*36337|127:127:23,35632*36338|127:127:23,35632*36339|31:30:0,35632*36340|31:30:0,35632*36341|31:30:0",
|
||||
"zoom.stealth.webgl2.enabled": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"vendor": "Google Inc. (Intel)",
|
||||
"renderer_out": "ANGLE (Intel, Intel(R) HD Graphics Direct3D11 vs_5_0 ps_5_0), or similar",
|
||||
"prob": 0.195745,
|
||||
"prefs": {
|
||||
"zoom.stealth.webgl.renderer": "ANGLE (Intel, Intel(R) HD Graphics Direct3D11 vs_5_0 ps_5_0, D3D11)",
|
||||
"zoom.stealth.webgl.vendor": "Google Inc. (Intel)",
|
||||
"zoom.stealth.webgl.extensions": "ANGLE_instanced_arrays,EXT_blend_minmax,EXT_color_buffer_half_float,EXT_float_blend,EXT_frag_depth,EXT_shader_texture_lod,EXT_sRGB,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_element_index_uint,OES_fbo_render_mipmap,OES_standard_derivatives,OES_texture_float,OES_texture_float_linear,OES_texture_half_float,OES_texture_half_float_linear,OES_vertex_array_object,WEBGL_color_buffer_float,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_depth_texture,WEBGL_draw_buffers,WEBGL_lose_context,WEBGL_provoking_vertex",
|
||||
"zoom.stealth.webgl2.extensions": "EXT_color_buffer_float,EXT_float_blend,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_draw_buffers_indexed,OES_texture_float_linear,OVR_multiview2,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_lose_context,WEBGL_provoking_vertex",
|
||||
"zoom.stealth.webgl.int_params": "2849|1,2885|1029,2886|2305,2931|1,2932|513,2961|0,2962|519,2963|2147483647,2964|7680,2965|7680,2966|7680,2967|0,2968|2147483647,3074|1029,3314|0,3315|0,3316|0,3317|4,3330|0,3331|0,3332|0,3333|4,3379|16384,3408|4,3410|8,3411|8,3412|8,3413|8,3414|24,3415|0,10752|0,32777|32774,32824|0,32877|0,32878|0,32883|2048,32936|1,32937|4,32938|1,32968|0,32969|1,32970|0,32971|1,33000|2147483647,33001|2147483647,33170|4352,34016|33984,34024|16384,34045|2,34076|16384,34816|519,34817|7680,34818|7680,34819|7680,34852|8,34853|1029,34854|0,34855|0,34856|0,34857|0,34858|0,34859|0,34860|0,34877|32774,34921|16,34930|16,35071|2048,35076|-8,35077|7,35371|12,35373|12,35374|24,35375|24,35376|65536,35377|212992,35379|200704,35380|256,35657|4096,35658|16384,35659|120,35660|16,35661|32,35723|4352,35738|5121,35739|6408,35968|4,35978|120,35979|4,36003|0,36004|2147483647,36005|2147483647,36063|8,36183|8,36203|4294967294,36347|4096,36348|30,36349|1024,37137|0,37154|120,37157|120,37443|37444,37447|1000000000",
|
||||
"zoom.stealth.webgl.int2_params": "2928|0:1,3386|32767:32767,33901|1:1024,33902|1:1",
|
||||
"zoom.stealth.webgl.float_params": "",
|
||||
"zoom.stealth.webgl.shader_precisions": "35633*36336|127:127:23,35633*36337|127:127:23,35633*36338|127:127:23,35633*36339|31:30:0,35633*36340|31:30:0,35633*36341|31:30:0,35632*36336|127:127:23,35632*36337|127:127:23,35632*36338|127:127:23,35632*36339|31:30:0,35632*36340|31:30:0,35632*36341|31:30:0",
|
||||
"zoom.stealth.webgl2.enabled": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"vendor": "Google Inc. (Intel)",
|
||||
"renderer_out": "ANGLE (Intel, Intel(R) HD Graphics 400 Direct3D11 vs_5_0 ps_5_0), or similar",
|
||||
"prob": 0.160284,
|
||||
"prefs": {
|
||||
"zoom.stealth.webgl.renderer": "ANGLE (Intel, Intel(R) HD Graphics 400 Direct3D11 vs_5_0 ps_5_0, D3D11)",
|
||||
"zoom.stealth.webgl.vendor": "Google Inc. (Intel)",
|
||||
"zoom.stealth.webgl.extensions": "ANGLE_instanced_arrays,EXT_blend_minmax,EXT_color_buffer_half_float,EXT_float_blend,EXT_frag_depth,EXT_shader_texture_lod,EXT_sRGB,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_element_index_uint,OES_fbo_render_mipmap,OES_standard_derivatives,OES_texture_float,OES_texture_float_linear,OES_texture_half_float,OES_texture_half_float_linear,OES_vertex_array_object,WEBGL_color_buffer_float,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_depth_texture,WEBGL_draw_buffers,WEBGL_lose_context,WEBGL_provoking_vertex",
|
||||
"zoom.stealth.webgl2.extensions": "EXT_color_buffer_float,EXT_float_blend,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_draw_buffers_indexed,OES_texture_float_linear,OVR_multiview2,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_lose_context,WEBGL_provoking_vertex",
|
||||
"zoom.stealth.webgl.int_params": "2849|1,2885|1029,2886|2305,2931|1,2932|513,2961|0,2962|519,2963|2147483647,2964|7680,2965|7680,2966|7680,2967|0,2968|2147483647,3074|1029,3314|0,3315|0,3316|0,3317|4,3330|0,3331|0,3332|0,3333|4,3379|16384,3408|4,3410|8,3411|8,3412|8,3413|8,3414|24,3415|0,10752|0,32777|32774,32824|0,32877|0,32878|0,32883|2048,32936|1,32937|4,32938|1,32968|0,32969|1,32970|0,32971|1,33000|2147483647,33001|2147483647,33170|4352,34016|33984,34024|16384,34045|2,34076|16384,34816|519,34817|7680,34818|7680,34819|7680,34852|8,34853|1029,34854|0,34855|0,34856|0,34857|0,34858|0,34859|0,34860|0,34877|32774,34921|16,34930|16,35071|2048,35076|-8,35077|7,35371|12,35373|12,35374|24,35375|24,35376|65536,35377|212992,35379|200704,35380|256,35657|4096,35658|16384,35659|120,35660|16,35661|32,35723|4352,35738|5121,35739|6408,35968|4,35978|120,35979|4,36003|0,36004|2147483647,36005|2147483647,36063|8,36183|16,36203|4294967294,36347|4096,36348|30,36349|1024,37137|0,37154|120,37157|120,37443|37444,37447|1000000000",
|
||||
"zoom.stealth.webgl.int2_params": "2928|0:1,3386|32767:32767,33901|1:1024,33902|1:1",
|
||||
"zoom.stealth.webgl.float_params": "",
|
||||
"zoom.stealth.webgl.shader_precisions": "35633*36336|127:127:23,35633*36337|127:127:23,35633*36338|127:127:23,35633*36339|31:30:0,35633*36340|31:30:0,35633*36341|31:30:0,35632*36336|127:127:23,35632*36337|127:127:23,35632*36338|127:127:23,35632*36339|31:30:0,35632*36340|31:30:0,35632*36341|31:30:0",
|
||||
"zoom.stealth.webgl2.enabled": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"vendor": "Google Inc. (AMD)",
|
||||
"renderer_out": "ANGLE (AMD, Radeon HD 3200 Graphics Direct3D11 vs_5_0 ps_5_0), or similar",
|
||||
"prob": 0.086524,
|
||||
"prefs": {
|
||||
"zoom.stealth.webgl.renderer": "ANGLE (AMD, Radeon HD 3200 Graphics Direct3D11 vs_5_0 ps_5_0, D3D11)",
|
||||
"zoom.stealth.webgl.vendor": "Google Inc. (AMD)",
|
||||
"zoom.stealth.webgl.extensions": "ANGLE_instanced_arrays,EXT_blend_minmax,EXT_color_buffer_half_float,EXT_float_blend,EXT_frag_depth,EXT_shader_texture_lod,EXT_sRGB,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_element_index_uint,OES_fbo_render_mipmap,OES_standard_derivatives,OES_texture_float,OES_texture_float_linear,OES_texture_half_float,OES_texture_half_float_linear,OES_vertex_array_object,WEBGL_color_buffer_float,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_depth_texture,WEBGL_draw_buffers,WEBGL_lose_context,WEBGL_provoking_vertex",
|
||||
"zoom.stealth.webgl2.extensions": "EXT_color_buffer_float,EXT_float_blend,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_draw_buffers_indexed,OES_texture_float_linear,OVR_multiview2,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_lose_context,WEBGL_provoking_vertex",
|
||||
"zoom.stealth.webgl.int_params": "2849|1,2885|1029,2886|2305,2931|1,2932|513,2961|0,2962|519,2963|2147483647,2964|7680,2965|7680,2966|7680,2967|0,2968|2147483647,3074|1029,3314|0,3315|0,3316|0,3317|4,3330|0,3331|0,3332|0,3333|4,3379|16384,3408|4,3410|8,3411|8,3412|8,3413|8,3414|24,3415|0,10752|0,32777|32774,32824|0,32877|0,32878|0,32883|2048,32936|1,32937|4,32938|1,32968|0,32969|1,32970|0,32971|1,33000|2147483647,33001|2147483647,33170|4352,34016|33984,34024|16384,34045|2,34076|16384,34816|519,34817|7680,34818|7680,34819|7680,34852|8,34853|1029,34854|0,34855|0,34856|0,34857|0,34858|0,34859|0,34860|0,34877|32774,34921|16,34930|16,35071|2048,35076|-8,35077|7,35371|12,35373|12,35374|24,35375|24,35376|65536,35377|212992,35379|200704,35380|256,35657|4096,35658|16384,35659|120,35660|16,35661|32,35723|4352,35738|5121,35739|6408,35968|4,35978|120,35979|4,36003|0,36004|2147483647,36005|2147483647,36063|8,36183|8,36203|4294967294,36347|4096,36348|30,36349|1024,37137|0,37154|120,37157|120,37443|37444,37447|1000000000",
|
||||
"zoom.stealth.webgl.int2_params": "2928|0:1,3386|32767:32767,33901|1:1024,33902|1:1",
|
||||
"zoom.stealth.webgl.float_params": "",
|
||||
"zoom.stealth.webgl.shader_precisions": "35633*36336|127:127:23,35633*36337|127:127:23,35633*36338|127:127:23,35633*36339|31:30:0,35633*36340|31:30:0,35633*36341|31:30:0,35632*36336|127:127:23,35632*36337|127:127:23,35632*36338|127:127:23,35632*36339|31:30:0,35632*36340|31:30:0,35632*36341|31:30:0",
|
||||
"zoom.stealth.webgl2.enabled": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"vendor": "Google Inc. (AMD)",
|
||||
"renderer_out": "ANGLE (AMD, Radeon R9 200 Series Direct3D11 vs_5_0 ps_5_0), or similar",
|
||||
"prob": 0.053901,
|
||||
"prefs": {
|
||||
"zoom.stealth.webgl.renderer": "ANGLE (AMD, Radeon R9 200 Series Direct3D11 vs_5_0 ps_5_0, D3D11)",
|
||||
"zoom.stealth.webgl.vendor": "Google Inc. (AMD)",
|
||||
"zoom.stealth.webgl.extensions": "ANGLE_instanced_arrays,EXT_blend_minmax,EXT_color_buffer_half_float,EXT_float_blend,EXT_frag_depth,EXT_shader_texture_lod,EXT_sRGB,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_element_index_uint,OES_fbo_render_mipmap,OES_standard_derivatives,OES_texture_float,OES_texture_float_linear,OES_texture_half_float,OES_texture_half_float_linear,OES_vertex_array_object,WEBGL_color_buffer_float,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_depth_texture,WEBGL_draw_buffers,WEBGL_lose_context,WEBGL_provoking_vertex",
|
||||
"zoom.stealth.webgl2.extensions": "EXT_color_buffer_float,EXT_float_blend,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_draw_buffers_indexed,OES_texture_float_linear,OVR_multiview2,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_lose_context,WEBGL_provoking_vertex",
|
||||
"zoom.stealth.webgl.int_params": "2849|1,2885|1029,2886|2305,2931|1,2932|513,2961|0,2962|519,2963|2147483647,2964|7680,2965|7680,2966|7680,2967|0,2968|2147483647,3074|1029,3314|0,3315|0,3316|0,3317|4,3330|0,3331|0,3332|0,3333|4,3379|16384,3408|4,3410|8,3411|8,3412|8,3413|8,3414|24,3415|0,10752|0,32777|32774,32824|0,32877|0,32878|0,32883|2048,32936|1,32937|4,32938|1,32968|0,32969|1,32970|0,32971|1,33000|2147483647,33001|2147483647,33170|4352,34016|33984,34024|16384,34045|2,34076|16384,34816|519,34817|7680,34818|7680,34819|7680,34852|8,34853|1029,34854|0,34855|0,34856|0,34857|0,34858|0,34859|0,34860|0,34877|32774,34921|16,34930|16,35071|2048,35076|-8,35077|7,35371|12,35373|12,35374|24,35375|24,35376|65536,35377|212992,35379|200704,35380|256,35657|4096,35658|16384,35659|120,35660|16,35661|32,35723|4352,35738|5121,35739|6408,35968|4,35978|120,35979|4,36003|0,36004|2147483647,36005|2147483647,36063|8,36183|8,36203|4294967294,36347|4096,36348|30,36349|1024,37137|0,37154|120,37157|120,37443|37444,37447|1000000000",
|
||||
"zoom.stealth.webgl.int2_params": "2928|0:1,3386|32767:32767,33901|1:1024,33902|1:1",
|
||||
"zoom.stealth.webgl.float_params": "",
|
||||
"zoom.stealth.webgl.shader_precisions": "35633*36336|127:127:23,35633*36337|127:127:23,35633*36338|127:127:23,35633*36339|31:30:0,35633*36340|31:30:0,35633*36341|31:30:0,35632*36336|127:127:23,35632*36337|127:127:23,35632*36338|127:127:23,35632*36339|31:30:0,35632*36340|31:30:0,35632*36341|31:30:0",
|
||||
"zoom.stealth.webgl2.enabled": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"vendor": "Google Inc. (Microsoft)",
|
||||
"renderer_out": "ANGLE (Microsoft, Microsoft Basic Render Driver Direct3D11 vs_5_0 ps_5_0), or similar",
|
||||
"prob": 0.012766,
|
||||
"prefs": {
|
||||
"zoom.stealth.webgl.renderer": "ANGLE (Microsoft, Microsoft Basic Render Driver Direct3D11 vs_5_0 ps_5_0, D3D11)",
|
||||
"zoom.stealth.webgl.vendor": "Google Inc. (Microsoft)",
|
||||
"zoom.stealth.webgl.extensions": "ANGLE_instanced_arrays,EXT_blend_minmax,EXT_color_buffer_half_float,EXT_float_blend,EXT_frag_depth,EXT_shader_texture_lod,EXT_sRGB,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_element_index_uint,OES_fbo_render_mipmap,OES_standard_derivatives,OES_texture_float,OES_texture_float_linear,OES_texture_half_float,OES_texture_half_float_linear,OES_vertex_array_object,WEBGL_color_buffer_float,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_depth_texture,WEBGL_draw_buffers,WEBGL_lose_context,WEBGL_provoking_vertex",
|
||||
"zoom.stealth.webgl2.extensions": "EXT_color_buffer_float,EXT_float_blend,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_draw_buffers_indexed,OES_texture_float_linear,OVR_multiview2,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_lose_context,WEBGL_provoking_vertex",
|
||||
"zoom.stealth.webgl.int_params": "2849|1,2885|1029,2886|2305,2931|1,2932|513,2961|0,2962|519,2963|2147483647,2964|7680,2965|7680,2966|7680,2967|0,2968|2147483647,3074|1029,3314|0,3315|0,3316|0,3317|4,3330|0,3331|0,3332|0,3333|4,3379|16384,3408|4,3410|8,3411|8,3412|8,3413|8,3414|24,3415|0,10752|0,32777|32774,32824|0,32877|0,32878|0,32883|2048,32936|0,32937|0,32938|1,32968|0,32969|1,32970|0,32971|1,33000|2147483647,33001|2147483647,33170|4352,34016|33984,34024|16384,34045|2,34076|16384,34816|519,34817|7680,34818|7680,34819|7680,34852|8,34853|1029,34854|0,34855|0,34856|0,34857|0,34858|0,34859|0,34860|0,34877|32774,34921|16,34930|16,35071|2048,35076|-8,35077|7,35371|12,35373|12,35374|24,35375|24,35376|65536,35377|212992,35379|200704,35380|256,35657|4096,35658|16384,35659|120,35660|16,35661|32,35723|4352,35738|5121,35739|6408,35968|4,35978|120,35979|4,36003|0,36004|2147483647,36005|2147483647,36063|8,36183|16,36203|4294967294,36347|4096,36348|30,36349|1024,37137|0,37154|120,37157|120,37443|37444,37447|1000000000",
|
||||
"zoom.stealth.webgl.int2_params": "2928|0:1,3386|32767:32767,33901|1:1024,33902|1:1",
|
||||
"zoom.stealth.webgl.float_params": "",
|
||||
"zoom.stealth.webgl.shader_precisions": "35633*36336|127:127:23,35633*36337|127:127:23,35633*36338|127:127:23,35633*36339|31:30:0,35633*36340|31:30:0,35633*36341|31:30:0,35632*36336|127:127:23,35632*36337|127:127:23,35632*36338|127:127:23,35632*36339|31:30:0,35632*36340|31:30:0,35632*36341|31:30:0",
|
||||
"zoom.stealth.webgl2.enabled": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"vendor": "Google Inc. (Intel)",
|
||||
"renderer_out": "ANGLE (Intel, Intel(R) HD Graphics Direct3D11 vs_5_0 ps_5_0)",
|
||||
"prob": 0.007092,
|
||||
"prefs": {
|
||||
"zoom.stealth.webgl.renderer": "ANGLE (Intel, Intel(R) HD Graphics Direct3D11 vs_5_0 ps_5_0, D3D11)",
|
||||
"zoom.stealth.webgl.vendor": "Google Inc. (Intel)",
|
||||
"zoom.stealth.webgl.extensions": "ANGLE_instanced_arrays,EXT_blend_minmax,EXT_color_buffer_half_float,EXT_float_blend,EXT_frag_depth,EXT_shader_texture_lod,EXT_sRGB,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_element_index_uint,OES_fbo_render_mipmap,OES_standard_derivatives,OES_texture_float,OES_texture_float_linear,OES_texture_half_float,OES_texture_half_float_linear,OES_vertex_array_object,WEBGL_color_buffer_float,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_depth_texture,WEBGL_draw_buffers,WEBGL_lose_context,WEBGL_provoking_vertex",
|
||||
"zoom.stealth.webgl2.extensions": "EXT_color_buffer_float,EXT_float_blend,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_draw_buffers_indexed,OES_texture_float_linear,OVR_multiview2,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_lose_context,WEBGL_provoking_vertex",
|
||||
"zoom.stealth.webgl.int_params": "2849|1,2885|1029,2886|2305,2931|1,2932|513,2961|0,2962|519,2963|2147483647,2964|7680,2965|7680,2966|7680,2967|0,2968|2147483647,3074|1029,3314|0,3315|0,3316|0,3317|4,3330|0,3331|0,3332|0,3333|4,3379|16384,3408|4,3410|8,3411|8,3412|8,3413|8,3414|24,3415|0,10752|0,32777|32774,32824|0,32877|0,32878|0,32883|2048,32936|1,32937|4,32938|1,32968|0,32969|1,32970|0,32971|1,33000|2147483647,33001|2147483647,33170|4352,34016|33984,34024|16384,34045|2,34076|16384,34816|519,34817|7680,34818|7680,34819|7680,34852|8,34853|1029,34854|0,34855|0,34856|0,34857|0,34858|0,34859|0,34860|0,34877|32774,34921|16,34930|16,35071|2048,35076|-8,35077|7,35371|12,35373|12,35374|24,35375|24,35376|65536,35377|212992,35379|200704,35380|256,35657|4096,35658|16384,35659|120,35660|16,35661|32,35723|4352,35738|5121,35739|6408,35968|4,35978|120,35979|4,36003|0,36004|2147483647,36005|2147483647,36063|8,36183|8,36203|4294967294,36347|4096,36348|30,36349|1024,37137|0,37154|120,37157|120,37443|37444,37447|1000000000",
|
||||
"zoom.stealth.webgl.int2_params": "2928|0:1,3386|32767:32767,33901|1:1024,33902|1:1",
|
||||
"zoom.stealth.webgl.float_params": "",
|
||||
"zoom.stealth.webgl.shader_precisions": "35633*36336|127:127:23,35633*36337|127:127:23,35633*36338|127:127:23,35633*36339|31:30:0,35633*36340|31:30:0,35633*36341|31:30:0,35632*36336|127:127:23,35632*36337|127:127:23,35632*36338|127:127:23,35632*36339|31:30:0,35632*36340|31:30:0,35632*36341|31:30:0",
|
||||
"zoom.stealth.webgl2.enabled": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"vendor": "Google Inc. (Intel)",
|
||||
"renderer_out": "ANGLE (Intel, Intel(R) HD Graphics Direct3D11 vs_4_1 ps_4_1), or similar",
|
||||
"prob": 0.004255,
|
||||
"prefs": {
|
||||
"zoom.stealth.webgl.renderer": "ANGLE (Intel, Intel(R) HD Graphics Direct3D11 vs_4_1 ps_4_1, D3D11)",
|
||||
"zoom.stealth.webgl.vendor": "Google Inc. (Intel)",
|
||||
"zoom.stealth.webgl.extensions": "ANGLE_instanced_arrays,EXT_blend_minmax,EXT_color_buffer_half_float,EXT_float_blend,EXT_frag_depth,EXT_shader_texture_lod,EXT_sRGB,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_element_index_uint,OES_fbo_render_mipmap,OES_standard_derivatives,OES_texture_float,OES_texture_float_linear,OES_texture_half_float,OES_texture_half_float_linear,OES_vertex_array_object,WEBGL_color_buffer_float,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_depth_texture,WEBGL_draw_buffers,WEBGL_lose_context,WEBGL_provoking_vertex",
|
||||
"zoom.stealth.webgl2.extensions": "EXT_color_buffer_float,EXT_float_blend,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_draw_buffers_indexed,OES_texture_float_linear,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_lose_context,WEBGL_provoking_vertex",
|
||||
"zoom.stealth.webgl.int_params": "2849|1,2885|1029,2886|2305,2931|1,2932|513,2961|0,2962|519,2963|2147483647,2964|7680,2965|7680,2966|7680,2967|0,2968|2147483647,3074|1029,3314|0,3315|0,3316|0,3317|4,3330|0,3331|0,3332|0,3333|4,3379|8192,3408|4,3410|8,3411|8,3412|8,3413|8,3414|24,3415|0,10752|0,32777|32774,32824|0,32877|0,32878|0,32883|2048,32936|1,32937|4,32938|1,32968|0,32969|1,32970|0,32971|1,33000|2147483647,33001|2147483647,33170|4352,34016|33984,34024|8192,34045|2,34076|8192,34816|519,34817|7680,34818|7680,34819|7680,34852|8,34853|1029,34854|0,34855|0,34856|0,34857|0,34858|0,34859|0,34860|0,34877|32774,34921|16,34930|16,35071|512,35076|-8,35077|7,35371|12,35373|12,35374|24,35375|24,35376|65536,35377|212992,35379|200704,35380|256,35657|4096,35658|16384,35659|120,35660|16,35661|32,35723|4352,35738|5121,35739|6408,35968|4,35978|120,35979|4,36003|0,36004|2147483647,36005|2147483647,36063|8,36183|4,36203|4294967294,36347|4096,36348|30,36349|1024,37137|0,37154|120,37157|120,37443|37444,37447|1000000000",
|
||||
"zoom.stealth.webgl.int2_params": "2928|0:1,3386|16383:16383,33901|1:1024,33902|1:1",
|
||||
"zoom.stealth.webgl.float_params": "",
|
||||
"zoom.stealth.webgl.shader_precisions": "35633*36336|127:127:23,35633*36337|127:127:23,35633*36338|127:127:23,35633*36339|31:30:0,35633*36340|31:30:0,35633*36341|31:30:0,35632*36336|127:127:23,35632*36337|127:127:23,35632*36338|127:127:23,35632*36339|31:30:0,35632*36340|31:30:0,35632*36341|31:30:0",
|
||||
"zoom.stealth.webgl2.enabled": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"vendor": "Google Inc. (AMD)",
|
||||
"renderer_out": "ANGLE (AMD, Radeon R9 200 Series Direct3D11 vs_5_0 ps_5_0)",
|
||||
"prob": 0.001419,
|
||||
"prefs": {
|
||||
"zoom.stealth.webgl.renderer": "ANGLE (AMD, Radeon R9 200 Series Direct3D11 vs_5_0 ps_5_0, D3D11)",
|
||||
"zoom.stealth.webgl.vendor": "Google Inc. (AMD)",
|
||||
"zoom.stealth.webgl.extensions": "ANGLE_instanced_arrays,EXT_blend_minmax,EXT_color_buffer_half_float,EXT_float_blend,EXT_frag_depth,EXT_shader_texture_lod,EXT_sRGB,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_element_index_uint,OES_fbo_render_mipmap,OES_standard_derivatives,OES_texture_float,OES_texture_float_linear,OES_texture_half_float,OES_texture_half_float_linear,OES_vertex_array_object,WEBGL_color_buffer_float,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_depth_texture,WEBGL_draw_buffers,WEBGL_lose_context,WEBGL_provoking_vertex",
|
||||
"zoom.stealth.webgl2.extensions": "EXT_color_buffer_float,EXT_float_blend,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_draw_buffers_indexed,OES_texture_float_linear,OVR_multiview2,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_lose_context,WEBGL_provoking_vertex",
|
||||
"zoom.stealth.webgl.int_params": "2849|1,2885|1029,2886|2305,2931|1,2932|513,2961|0,2962|519,2963|2147483647,2964|7680,2965|7680,2966|7680,2967|0,2968|2147483647,3074|1029,3314|0,3315|0,3316|0,3317|4,3330|0,3331|0,3332|0,3333|4,3379|16384,3408|4,3410|8,3411|8,3412|8,3413|8,3414|24,3415|0,10752|0,32777|32774,32824|0,32877|0,32878|0,32883|2048,32936|1,32937|4,32938|1,32968|0,32969|1,32970|0,32971|1,33000|2147483647,33001|2147483647,33170|4352,34016|33984,34024|16384,34045|2,34076|16384,34816|519,34817|7680,34818|7680,34819|7680,34852|8,34853|1029,34854|0,34855|0,34856|0,34857|0,34858|0,34859|0,34860|0,34877|32774,34921|16,34930|16,35071|2048,35076|-8,35077|7,35371|12,35373|12,35374|24,35375|24,35376|65536,35377|212992,35379|200704,35380|256,35657|4096,35658|16384,35659|120,35660|16,35661|32,35723|4352,35738|5121,35739|6408,35968|4,35978|120,35979|4,36003|0,36004|2147483647,36005|2147483647,36063|8,36183|8,36203|4294967294,36347|4096,36348|30,36349|1024,37137|0,37154|120,37157|120,37443|37444,37447|1000000000",
|
||||
"zoom.stealth.webgl.int2_params": "2928|0:1,3386|32767:32767,33901|1:1024,33902|1:1",
|
||||
"zoom.stealth.webgl.float_params": "",
|
||||
"zoom.stealth.webgl.shader_precisions": "35633*36336|127:127:23,35633*36337|127:127:23,35633*36338|127:127:23,35633*36339|31:30:0,35633*36340|31:30:0,35633*36341|31:30:0,35632*36336|127:127:23,35632*36337|127:127:23,35632*36338|127:127:23,35632*36339|31:30:0,35632*36340|31:30:0,35632*36341|31:30:0",
|
||||
"zoom.stealth.webgl2.enabled": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"vendor": "Google Inc. (Intel)",
|
||||
"renderer_out": "ANGLE (Intel, Intel(R) HD Graphics Direct3D11 vs_4_0 ps_4_0)",
|
||||
"prob": 0.001419,
|
||||
"prefs": {
|
||||
"zoom.stealth.webgl.renderer": "ANGLE (Intel, Intel(R) HD Graphics Direct3D11 vs_4_0 ps_4_0, D3D11)",
|
||||
"zoom.stealth.webgl.vendor": "Google Inc. (Intel)",
|
||||
"zoom.stealth.webgl.extensions": "ANGLE_instanced_arrays,EXT_blend_minmax,EXT_color_buffer_half_float,EXT_float_blend,EXT_frag_depth,EXT_shader_texture_lod,EXT_sRGB,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_element_index_uint,OES_fbo_render_mipmap,OES_standard_derivatives,OES_texture_float,OES_texture_float_linear,OES_texture_half_float,OES_texture_half_float_linear,OES_vertex_array_object,WEBGL_color_buffer_float,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_depth_texture,WEBGL_draw_buffers,WEBGL_lose_context,WEBGL_provoking_vertex",
|
||||
"zoom.stealth.webgl2.extensions": "",
|
||||
"zoom.stealth.webgl.int_params": "2849|1,2885|1029,2886|2305,2931|1,2932|513,2961|0,2962|519,2963|2147483647,2964|7680,2965|7680,2966|7680,2967|0,2968|2147483647,3317|4,3333|4,3379|8192,3408|4,3410|8,3411|8,3412|8,3413|8,3414|24,3415|0,10752|0,32777|32774,32824|0,32936|0,32937|0,32938|1,32968|0,32969|1,32970|0,32971|1,33170|4352,34016|33984,34024|8192,34076|8192,34816|519,34817|7680,34818|7680,34819|7680,34877|32774,34921|16,34930|16,35660|16,35661|32,35738|5121,35739|6408,36003|0,36004|2147483647,36005|2147483647,36347|4096,36348|14,36349|1024,37443|37444",
|
||||
"zoom.stealth.webgl.int2_params": "2928|0:1,3386|16383:16383,33901|1:1024,33902|1:1",
|
||||
"zoom.stealth.webgl.float_params": "",
|
||||
"zoom.stealth.webgl.shader_precisions": "35633*36336|127:127:23,35633*36337|127:127:23,35633*36338|127:127:23,35633*36339|31:30:0,35633*36340|31:30:0,35633*36341|31:30:0,35632*36336|127:127:23,35632*36337|127:127:23,35632*36338|127:127:23,35632*36339|31:30:0,35632*36340|31:30:0,35632*36341|31:30:0",
|
||||
"zoom.stealth.webgl2.enabled": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"vendor": "Google Inc. (NVIDIA)",
|
||||
"renderer_out": "ANGLE (NVIDIA, NVIDIA GeForce 8800 GTX Direct3D11 vs_4_0 ps_4_0)",
|
||||
"prob": 0.001419,
|
||||
"prefs": {
|
||||
"zoom.stealth.webgl.renderer": "ANGLE (NVIDIA, NVIDIA GeForce 8800 GTX Direct3D11 vs_4_0 ps_4_0, D3D11)",
|
||||
"zoom.stealth.webgl.vendor": "Google Inc. (NVIDIA)",
|
||||
"zoom.stealth.webgl.extensions": "ANGLE_instanced_arrays,EXT_blend_minmax,EXT_color_buffer_half_float,EXT_float_blend,EXT_frag_depth,EXT_shader_texture_lod,EXT_sRGB,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_element_index_uint,OES_fbo_render_mipmap,OES_standard_derivatives,OES_texture_float,OES_texture_float_linear,OES_texture_half_float,OES_texture_half_float_linear,OES_vertex_array_object,WEBGL_color_buffer_float,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_depth_texture,WEBGL_draw_buffers,WEBGL_lose_context,WEBGL_provoking_vertex",
|
||||
"zoom.stealth.webgl2.extensions": "",
|
||||
"zoom.stealth.webgl.int_params": "2849|1,2885|1029,2886|2305,2931|1,2932|513,2961|0,2962|519,2963|2147483647,2964|7680,2965|7680,2966|7680,2967|0,2968|2147483647,3317|4,3333|4,3379|8192,3408|4,3410|8,3411|8,3412|8,3413|8,3414|24,3415|0,10752|0,32777|32774,32824|0,32936|1,32937|4,32938|1,32968|0,32969|1,32970|0,32971|1,33170|4352,34016|33984,34024|8192,34076|8192,34816|519,34817|7680,34818|7680,34819|7680,34877|32774,34921|16,34930|16,35660|16,35661|32,35738|5121,35739|6408,36003|0,36004|2147483647,36005|2147483647,36347|4095,36348|14,36349|1024,37443|37444",
|
||||
"zoom.stealth.webgl.int2_params": "2928|0:1,3386|16383:16383,33901|1:1024,33902|1:1",
|
||||
"zoom.stealth.webgl.float_params": "",
|
||||
"zoom.stealth.webgl.shader_precisions": "35633*36336|127:127:23,35633*36337|127:127:23,35633*36338|127:127:23,35633*36339|31:30:0,35633*36340|31:30:0,35633*36341|31:30:0,35632*36336|127:127:23,35632*36337|127:127:23,35632*36338|127:127:23,35632*36339|31:30:0,35632*36340|31:30:0,35632*36341|31:30:0",
|
||||
"zoom.stealth.webgl2.enabled": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"vendor": "Google Inc. (Intel)",
|
||||
"renderer_out": "ANGLE (Intel, Intel 945GM Direct3D11 vs_4_0 ps_4_0)",
|
||||
"prob": 0.001419,
|
||||
"prefs": {
|
||||
"zoom.stealth.webgl.renderer": "ANGLE (Intel, Intel 945GM Direct3D11 vs_4_0 ps_4_0, D3D11)",
|
||||
"zoom.stealth.webgl.vendor": "Google Inc. (Intel)",
|
||||
"zoom.stealth.webgl.extensions": "ANGLE_instanced_arrays,EXT_blend_minmax,EXT_color_buffer_half_float,EXT_float_blend,EXT_frag_depth,EXT_shader_texture_lod,EXT_sRGB,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_element_index_uint,OES_fbo_render_mipmap,OES_standard_derivatives,OES_texture_float,OES_texture_half_float,OES_texture_half_float_linear,OES_vertex_array_object,WEBGL_color_buffer_float,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_depth_texture,WEBGL_draw_buffers,WEBGL_lose_context,WEBGL_provoking_vertex",
|
||||
"zoom.stealth.webgl2.extensions": "",
|
||||
"zoom.stealth.webgl.int_params": "2849|1,2885|1029,2886|2305,2931|1,2932|513,2961|0,2962|519,2963|2147483647,2964|7680,2965|7680,2966|7680,2967|0,2968|2147483647,3317|4,3333|4,3379|8192,3408|4,3410|8,3411|8,3412|8,3413|8,3414|24,3415|0,10752|0,32777|32774,32824|0,32936|0,32937|0,32938|1,32968|0,32969|1,32970|0,32971|1,33170|4352,34016|33984,34024|8192,34076|8192,34816|519,34817|7680,34818|7680,34819|7680,34877|32774,34921|16,34930|16,35660|16,35661|32,35738|5121,35739|6408,36003|0,36004|2147483647,36005|2147483647,36347|4096,36348|14,36349|1024,37443|37444",
|
||||
"zoom.stealth.webgl.int2_params": "2928|0:1,3386|16383:16383,33901|1:1024,33902|1:1",
|
||||
"zoom.stealth.webgl.float_params": "",
|
||||
"zoom.stealth.webgl.shader_precisions": "35633*36336|127:127:23,35633*36337|127:127:23,35633*36338|127:127:23,35633*36339|31:30:0,35633*36340|31:30:0,35633*36341|31:30:0,35632*36336|127:127:23,35632*36337|127:127:23,35632*36338|127:127:23,35632*36339|31:30:0,35632*36340|31:30:0,35632*36341|31:30:0",
|
||||
"zoom.stealth.webgl2.enabled": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"vendor": "Google Inc. (Intel)",
|
||||
"renderer_out": "ANGLE (Intel, Intel(R) HD Graphics Direct3D11 vs_4_1 ps_4_1)",
|
||||
"prob": 0.001419,
|
||||
"prefs": {
|
||||
"zoom.stealth.webgl.renderer": "ANGLE (Intel, Intel(R) HD Graphics Direct3D11 vs_4_1 ps_4_1, D3D11)",
|
||||
"zoom.stealth.webgl.vendor": "Google Inc. (Intel)",
|
||||
"zoom.stealth.webgl.extensions": "ANGLE_instanced_arrays,EXT_blend_minmax,EXT_color_buffer_half_float,EXT_float_blend,EXT_frag_depth,EXT_shader_texture_lod,EXT_sRGB,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_element_index_uint,OES_fbo_render_mipmap,OES_standard_derivatives,OES_texture_float,OES_texture_float_linear,OES_texture_half_float,OES_texture_half_float_linear,OES_vertex_array_object,WEBGL_color_buffer_float,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_depth_texture,WEBGL_draw_buffers,WEBGL_lose_context,WEBGL_provoking_vertex",
|
||||
"zoom.stealth.webgl2.extensions": "EXT_color_buffer_float,EXT_float_blend,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_draw_buffers_indexed,OES_texture_float_linear,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_lose_context,WEBGL_provoking_vertex",
|
||||
"zoom.stealth.webgl.int_params": "2849|1,2885|1029,2886|2305,2931|1,2932|513,2961|0,2962|519,2963|2147483647,2964|7680,2965|7680,2966|7680,2967|0,2968|2147483647,3074|1029,3314|0,3315|0,3316|0,3317|4,3330|0,3331|0,3332|0,3333|4,3379|8192,3408|4,3410|8,3411|8,3412|8,3413|8,3414|24,3415|0,10752|0,32777|32774,32824|0,32877|0,32878|0,32883|2048,32936|1,32937|4,32938|1,32968|0,32969|1,32970|0,32971|1,33000|2147483647,33001|2147483647,33170|4352,34016|33984,34024|8192,34045|2,34076|8192,34816|519,34817|7680,34818|7680,34819|7680,34852|8,34853|1029,34854|0,34855|0,34856|0,34857|0,34858|0,34859|0,34860|0,34877|32774,34921|16,34930|16,35071|512,35076|-8,35077|7,35371|12,35373|12,35374|24,35375|24,35376|65536,35377|212992,35379|200704,35380|256,35657|4096,35658|16384,35659|120,35660|16,35661|32,35723|4352,35738|5121,35739|6408,35968|4,35978|120,35979|4,36003|0,36004|2147483647,36005|2147483647,36063|8,36183|4,36203|4294967294,36347|4096,36348|30,36349|1024,37137|0,37154|120,37157|120,37443|37444,37447|1000000000",
|
||||
"zoom.stealth.webgl.int2_params": "2928|0:1,3386|16383:16383,33901|1:1024,33902|1:1",
|
||||
"zoom.stealth.webgl.float_params": "",
|
||||
"zoom.stealth.webgl.shader_precisions": "35633*36336|127:127:23,35633*36337|127:127:23,35633*36338|127:127:23,35633*36339|31:30:0,35633*36340|31:30:0,35633*36341|31:30:0,35632*36336|127:127:23,35632*36337|127:127:23,35632*36338|127:127:23,35632*36339|31:30:0,35632*36340|31:30:0,35632*36341|31:30:0",
|
||||
"zoom.stealth.webgl2.enabled": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"vendor": "Google Inc. (NVIDIA)",
|
||||
"renderer_out": "ANGLE (NVIDIA, NVIDIA GeForce GTX 980 Direct3D11 vs_5_0 ps_5_0)",
|
||||
"prob": 0.001419,
|
||||
"prefs": {
|
||||
"zoom.stealth.webgl.renderer": "ANGLE (NVIDIA, NVIDIA GeForce GTX 980 Direct3D11 vs_5_0 ps_5_0, D3D11)",
|
||||
"zoom.stealth.webgl.vendor": "Google Inc. (NVIDIA)",
|
||||
"zoom.stealth.webgl.extensions": "ANGLE_instanced_arrays,EXT_blend_minmax,EXT_color_buffer_half_float,EXT_float_blend,EXT_frag_depth,EXT_shader_texture_lod,EXT_sRGB,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_element_index_uint,OES_fbo_render_mipmap,OES_standard_derivatives,OES_texture_float,OES_texture_float_linear,OES_texture_half_float,OES_texture_half_float_linear,OES_vertex_array_object,WEBGL_color_buffer_float,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_depth_texture,WEBGL_draw_buffers,WEBGL_lose_context,WEBGL_provoking_vertex",
|
||||
"zoom.stealth.webgl2.extensions": "EXT_color_buffer_float,EXT_float_blend,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_draw_buffers_indexed,OES_texture_float_linear,OVR_multiview2,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_lose_context,WEBGL_provoking_vertex",
|
||||
"zoom.stealth.webgl.int_params": "2849|1,2885|1029,2886|2305,2931|1,2932|513,2961|0,2962|519,2963|2147483647,2964|7680,2965|7680,2966|7680,2967|0,2968|2147483647,3074|1029,3314|0,3315|0,3316|0,3317|4,3330|0,3331|0,3332|0,3333|4,3379|16384,3408|4,3410|8,3411|8,3412|8,3413|8,3414|24,3415|0,10752|0,32777|32774,32824|0,32877|0,32878|0,32883|2048,32936|1,32937|4,32938|1,32968|0,32969|1,32970|0,32971|1,33000|2147483647,33001|2147483647,33170|4352,34016|33984,34024|16384,34045|2,34076|16384,34816|519,34817|7680,34818|7680,34819|7680,34852|8,34853|1029,34854|0,34855|0,34856|0,34857|0,34858|0,34859|0,34860|0,34877|32774,34921|16,34930|16,35071|2048,35076|-8,35077|7,35371|12,35373|12,35374|24,35375|24,35376|65536,35377|212988,35379|200704,35380|256,35657|4096,35658|16380,35659|120,35660|16,35661|32,35723|4352,35738|5121,35739|6408,35968|4,35978|120,35979|4,36003|0,36004|2147483647,36005|2147483647,36063|8,36183|8,36203|4294967294,36347|4095,36348|30,36349|1024,37137|0,37154|120,37157|120,37443|37444,37447|1000000000",
|
||||
"zoom.stealth.webgl.int2_params": "2928|0:1,3386|32767:32767,33901|1:1024,33902|1:1",
|
||||
"zoom.stealth.webgl.float_params": "",
|
||||
"zoom.stealth.webgl.shader_precisions": "35633*36336|127:127:23,35633*36337|127:127:23,35633*36338|127:127:23,35633*36339|31:30:0,35633*36340|31:30:0,35633*36341|31:30:0,35632*36336|127:127:23,35632*36337|127:127:23,35632*36338|127:127:23,35632*36339|31:30:0,35632*36340|31:30:0,35632*36341|31:30:0",
|
||||
"zoom.stealth.webgl2.enabled": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"mac": [
|
||||
{
|
||||
"vendor": "Apple",
|
||||
"renderer_out": "Apple M1, or similar",
|
||||
"prob": 0.84322,
|
||||
"prefs": {
|
||||
"zoom.stealth.webgl.renderer": "Apple M1, or similar",
|
||||
"zoom.stealth.webgl.vendor": "Apple",
|
||||
"zoom.stealth.webgl.extensions": "ANGLE_instanced_arrays,EXT_blend_minmax,EXT_color_buffer_half_float,EXT_depth_clamp,EXT_float_blend,EXT_frag_depth,EXT_shader_texture_lod,EXT_sRGB,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_element_index_uint,OES_fbo_render_mipmap,OES_standard_derivatives,OES_texture_float,OES_texture_float_linear,OES_texture_half_float,OES_texture_half_float_linear,OES_vertex_array_object,WEBGL_color_buffer_float,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_depth_texture,WEBGL_draw_buffers,WEBGL_lose_context,WEBGL_provoking_vertex",
|
||||
"zoom.stealth.webgl2.extensions": "EXT_color_buffer_float,EXT_depth_clamp,EXT_float_blend,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_draw_buffers_indexed,OES_texture_float_linear,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_lose_context,WEBGL_provoking_vertex",
|
||||
"zoom.stealth.webgl.int_params": "2849|1,2885|1029,2886|2305,2931|1,2932|513,2961|0,2962|519,2963|4294967295,2964|7680,2965|7680,2966|7680,2967|0,2968|4294967295,3074|1029,3314|0,3315|0,3316|0,3317|4,3330|0,3331|0,3332|0,3333|4,3379|8192,3408|4,3410|8,3411|8,3412|8,3413|8,3414|24,3415|0,10752|0,32777|32774,32824|0,32877|0,32878|0,32883|2048,32936|1,32937|4,32938|1,32968|0,32969|1,32970|0,32971|1,33000|1048575,33001|150000,33170|4352,34016|33984,34024|8192,34045|16,34076|8192,34816|519,34817|7680,34818|7680,34819|7680,34852|8,34853|1029,34854|0,34855|0,34856|0,34857|0,34858|0,34859|0,34860|0,34877|32774,34921|16,34930|16,35071|2048,35076|-8,35077|7,35371|16,35373|16,35374|80,35375|80,35376|65536,35377|266240,35379|266240,35380|256,35657|4096,35658|4096,35659|124,35660|16,35661|80,35723|4352,35738|5121,35739|6408,35968|4,35978|64,35979|4,36003|0,36004|4294967295,36005|4294967295,36063|8,36183|4,36203|4294967295,36347|1024,36348|32,36349|1024,37137|18446744073709552000,37154|64,37157|128,37443|37444,37447|1000000000",
|
||||
"zoom.stealth.webgl.int2_params": "2928|0:1,3386|16384:16384,33901|1:64,33902|1:1",
|
||||
"zoom.stealth.webgl.float_params": "",
|
||||
"zoom.stealth.webgl.shader_precisions": "35633*36336|127:127:23,35633*36337|127:127:23,35633*36338|127:127:23,35633*36339|24:24:0,35633*36340|24:24:0,35633*36341|24:24:0,35632*36336|127:127:23,35632*36337|127:127:23,35632*36338|127:127:23,35632*36339|24:24:0,35632*36340|24:24:0,35632*36341|24:24:0",
|
||||
"zoom.stealth.webgl2.enabled": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"vendor": "Intel Inc.",
|
||||
"renderer_out": "Intel(R) HD Graphics 400, or similar",
|
||||
"prob": 0.076271,
|
||||
"prefs": {
|
||||
"zoom.stealth.webgl.renderer": "Intel(R) HD Graphics 400, or similar",
|
||||
"zoom.stealth.webgl.vendor": "Intel Inc.",
|
||||
"zoom.stealth.webgl.extensions": "ANGLE_instanced_arrays,EXT_blend_minmax,EXT_color_buffer_half_float,EXT_depth_clamp,EXT_float_blend,EXT_frag_depth,EXT_shader_texture_lod,EXT_sRGB,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_element_index_uint,OES_fbo_render_mipmap,OES_standard_derivatives,OES_texture_float,OES_texture_float_linear,OES_texture_half_float,OES_texture_half_float_linear,OES_vertex_array_object,WEBGL_color_buffer_float,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_depth_texture,WEBGL_draw_buffers,WEBGL_lose_context,WEBGL_provoking_vertex",
|
||||
"zoom.stealth.webgl2.extensions": "EXT_color_buffer_float,EXT_depth_clamp,EXT_float_blend,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_draw_buffers_indexed,OES_texture_float_linear,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_lose_context,WEBGL_provoking_vertex",
|
||||
"zoom.stealth.webgl.int_params": "2849|1,2885|1029,2886|2305,2931|1,2932|513,2961|0,2962|519,2963|4294967295,2964|7680,2965|7680,2966|7680,2967|0,2968|4294967295,3074|1029,3314|0,3315|0,3316|0,3317|4,3330|0,3331|0,3332|0,3333|4,3379|8192,3408|8,3410|8,3411|8,3412|8,3413|8,3414|24,3415|0,10752|0,32777|32774,32824|0,32877|0,32878|0,32883|2048,32936|1,32937|4,32938|1,32968|0,32969|1,32970|0,32971|1,33000|1048575,33001|150000,33170|4352,34016|33984,34024|8192,34045|16,34076|8192,34816|519,34817|7680,34818|7680,34819|7680,34852|8,34853|1029,34854|0,34855|0,34856|0,34857|0,34858|0,34859|0,34860|0,34877|32774,34921|16,34930|16,35071|2048,35076|-8,35077|7,35371|15,35373|15,35374|75,35375|75,35376|65536,35377|249856,35379|249856,35380|256,35657|4096,35658|4096,35659|60,35660|16,35661|80,35723|4352,35738|5121,35739|6408,35968|4,35978|64,35979|4,36003|0,36004|4294967295,36005|4294967295,36063|8,36183|16,36203|4294967295,36347|1024,36348|32,36349|1024,37137|18446744073709552000,37154|128,37157|128,37443|37444,37447|1000000000",
|
||||
"zoom.stealth.webgl.int2_params": "2928|0:1,3386|16384:16384,33902|1:1",
|
||||
"zoom.stealth.webgl.float_params": "33901|1:255.875",
|
||||
"zoom.stealth.webgl.shader_precisions": "35633*36336|127:127:23,35633*36337|127:127:23,35633*36338|127:127:23,35633*36339|24:24:0,35633*36340|24:24:0,35633*36341|24:24:0,35632*36336|127:127:23,35632*36337|127:127:23,35632*36338|127:127:23,35632*36339|24:24:0,35632*36340|24:24:0,35632*36341|24:24:0",
|
||||
"zoom.stealth.webgl2.enabled": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"vendor": "ATI Technologies Inc.",
|
||||
"renderer_out": "Radeon R9 200 Series, or similar",
|
||||
"prob": 0.038136,
|
||||
"prefs": {
|
||||
"zoom.stealth.webgl.renderer": "Radeon R9 200 Series, or similar",
|
||||
"zoom.stealth.webgl.vendor": "ATI Technologies Inc.",
|
||||
"zoom.stealth.webgl.extensions": "ANGLE_instanced_arrays,EXT_blend_minmax,EXT_color_buffer_half_float,EXT_depth_clamp,EXT_float_blend,EXT_frag_depth,EXT_shader_texture_lod,EXT_sRGB,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_element_index_uint,OES_fbo_render_mipmap,OES_standard_derivatives,OES_texture_float,OES_texture_float_linear,OES_texture_half_float,OES_texture_half_float_linear,OES_vertex_array_object,WEBGL_color_buffer_float,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_depth_texture,WEBGL_draw_buffers,WEBGL_lose_context,WEBGL_provoking_vertex",
|
||||
"zoom.stealth.webgl2.extensions": "EXT_color_buffer_float,EXT_depth_clamp,EXT_float_blend,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_draw_buffers_indexed,OES_texture_float_linear,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_lose_context,WEBGL_provoking_vertex",
|
||||
"zoom.stealth.webgl.int_params": "2849|1,2885|1029,2886|2305,2931|1,2932|513,2961|0,2962|519,2963|4294967295,2964|7680,2965|7680,2966|7680,2967|0,2968|4294967295,3074|1029,3314|0,3315|0,3316|0,3317|4,3330|0,3331|0,3332|0,3333|4,3379|8192,3408|8,3410|8,3411|8,3412|8,3413|8,3414|24,3415|0,10752|0,32777|32774,32824|0,32877|0,32878|0,32883|16384,32936|1,32937|4,32938|1,32968|0,32969|1,32970|0,32971|1,33000|1048575,33001|150000,33170|4352,34016|33984,34024|8192,34045|16,34076|8192,34816|519,34817|7680,34818|7680,34819|7680,34852|8,34853|1029,34854|0,34855|0,34856|0,34857|0,34858|0,34859|0,34860|0,34877|32774,34921|16,34930|16,35071|2048,35076|-8,35077|7,35371|16,35373|16,35374|80,35375|80,35376|65536,35377|266240,35379|266240,35380|256,35657|4096,35658|4096,35659|128,35660|16,35661|80,35723|4352,35738|5121,35739|6408,35968|4,35978|64,35979|4,36003|0,36004|4294967295,36005|4294967295,36063|8,36183|8,36203|4294967295,36347|1024,36348|32,36349|1024,37137|18446744073709552000,37154|128,37157|128,37443|37444,37447|1000000000",
|
||||
"zoom.stealth.webgl.int2_params": "2928|0:1,3386|16384:16384,33901|1:8191,33902|1:1",
|
||||
"zoom.stealth.webgl.float_params": "",
|
||||
"zoom.stealth.webgl.shader_precisions": "35633*36336|127:127:23,35633*36337|127:127:23,35633*36338|127:127:23,35633*36339|24:24:0,35633*36340|24:24:0,35633*36341|24:24:0,35632*36336|127:127:23,35632*36337|127:127:23,35632*36338|127:127:23,35632*36339|24:24:0,35632*36340|24:24:0,35632*36341|24:24:0",
|
||||
"zoom.stealth.webgl2.enabled": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"vendor": "Intel Inc.",
|
||||
"renderer_out": "Intel(R) HD Graphics, or similar",
|
||||
"prob": 0.033898,
|
||||
"prefs": {
|
||||
"zoom.stealth.webgl.renderer": "Intel(R) HD Graphics, or similar",
|
||||
"zoom.stealth.webgl.vendor": "Intel Inc.",
|
||||
"zoom.stealth.webgl.extensions": "ANGLE_instanced_arrays,EXT_blend_minmax,EXT_color_buffer_half_float,EXT_depth_clamp,EXT_float_blend,EXT_frag_depth,EXT_shader_texture_lod,EXT_sRGB,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_element_index_uint,OES_fbo_render_mipmap,OES_standard_derivatives,OES_texture_float,OES_texture_float_linear,OES_texture_half_float,OES_texture_half_float_linear,OES_vertex_array_object,WEBGL_color_buffer_float,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_depth_texture,WEBGL_draw_buffers,WEBGL_lose_context,WEBGL_provoking_vertex",
|
||||
"zoom.stealth.webgl2.extensions": "EXT_color_buffer_float,EXT_depth_clamp,EXT_float_blend,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_draw_buffers_indexed,OES_texture_float_linear,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_lose_context,WEBGL_provoking_vertex",
|
||||
"zoom.stealth.webgl.int_params": "2849|1,2885|1029,2886|2305,2931|1,2932|513,2961|0,2962|519,2963|4294967295,2964|7680,2965|7680,2966|7680,2967|0,2968|4294967295,3074|1029,3314|0,3315|0,3316|0,3317|4,3330|0,3331|0,3332|0,3333|4,3379|8192,3408|8,3410|8,3411|8,3412|8,3413|8,3414|24,3415|0,10752|0,32777|32774,32824|0,32877|0,32878|0,32883|2048,32936|1,32937|4,32938|1,32968|0,32969|1,32970|0,32971|1,33000|1048575,33001|150000,33170|4352,34016|33984,34024|8192,34045|16,34076|8192,34816|519,34817|7680,34818|7680,34819|7680,34852|8,34853|1029,34854|0,34855|0,34856|0,34857|0,34858|0,34859|0,34860|0,34877|32774,34921|16,34930|16,35071|2048,35076|-8,35077|7,35371|15,35373|15,35374|75,35375|75,35376|65536,35377|249856,35379|249856,35380|256,35657|4096,35658|4096,35659|60,35660|16,35661|80,35723|4352,35738|5121,35739|6408,35968|4,35978|64,35979|4,36003|0,36004|4294967295,36005|4294967295,36063|8,36183|16,36203|4294967295,36347|1024,36348|32,36349|1024,37137|18446744073709552000,37154|128,37157|128,37443|37444,37447|1000000000",
|
||||
"zoom.stealth.webgl.int2_params": "2928|0:1,3386|16384:16384,33902|1:1",
|
||||
"zoom.stealth.webgl.float_params": "33901|1:255.875",
|
||||
"zoom.stealth.webgl.shader_precisions": "35633*36336|127:127:23,35633*36337|127:127:23,35633*36338|127:127:23,35633*36339|24:24:0,35633*36340|24:24:0,35633*36341|24:24:0,35632*36336|127:127:23,35632*36337|127:127:23,35632*36338|127:127:23,35632*36339|24:24:0,35632*36340|24:24:0,35632*36341|24:24:0",
|
||||
"zoom.stealth.webgl2.enabled": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"vendor": "Google Inc. (NVIDIA)",
|
||||
"renderer_out": "ANGLE (NVIDIA, NVIDIA GeForce GTX 980 Direct3D11 vs_5_0 ps_5_0), or similar",
|
||||
"prob": 0.008475,
|
||||
"prefs": {
|
||||
"zoom.stealth.webgl.renderer": "ANGLE (NVIDIA, NVIDIA GeForce GTX 980 Direct3D11 vs_5_0 ps_5_0, D3D11)",
|
||||
"zoom.stealth.webgl.vendor": "Google Inc. (NVIDIA)",
|
||||
"zoom.stealth.webgl.extensions": "ANGLE_instanced_arrays,EXT_blend_minmax,EXT_color_buffer_half_float,EXT_float_blend,EXT_frag_depth,EXT_shader_texture_lod,EXT_sRGB,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_element_index_uint,OES_fbo_render_mipmap,OES_standard_derivatives,OES_texture_float,OES_texture_float_linear,OES_texture_half_float,OES_texture_half_float_linear,OES_vertex_array_object,WEBGL_color_buffer_float,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_depth_texture,WEBGL_draw_buffers,WEBGL_lose_context,WEBGL_provoking_vertex",
|
||||
"zoom.stealth.webgl2.extensions": "EXT_color_buffer_float,EXT_float_blend,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_draw_buffers_indexed,OES_texture_float_linear,OVR_multiview2,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_lose_context,WEBGL_provoking_vertex",
|
||||
"zoom.stealth.webgl.int_params": "2849|1,2885|1029,2886|2305,2931|1,2932|513,2961|0,2962|519,2963|2147483647,2964|7680,2965|7680,2966|7680,2967|0,2968|2147483647,3074|1029,3314|0,3315|0,3316|0,3317|4,3330|0,3331|0,3332|0,3333|4,3379|16384,3408|4,3410|8,3411|8,3412|8,3413|8,3414|24,3415|0,10752|0,32777|32774,32824|0,32877|0,32878|0,32883|2048,32936|1,32937|4,32938|1,32968|0,32969|1,32970|0,32971|1,33000|2147483647,33001|2147483647,33170|4352,34016|33984,34024|16384,34045|2,34076|16384,34816|519,34817|7680,34818|7680,34819|7680,34852|8,34853|1029,34854|0,34855|0,34856|0,34857|0,34858|0,34859|0,34860|0,34877|32774,34921|16,34930|16,35071|2048,35076|-8,35077|7,35371|12,35373|12,35374|24,35375|24,35376|65536,35377|212988,35379|200704,35380|256,35657|4096,35658|16380,35659|120,35660|16,35661|32,35723|4352,35738|5121,35739|6408,35968|4,35978|120,35979|4,36003|0,36004|2147483647,36005|2147483647,36063|8,36183|8,36203|4294967294,36347|4095,36348|30,36349|1024,37137|0,37154|120,37157|120,37443|37444,37447|1000000000",
|
||||
"zoom.stealth.webgl.int2_params": "2928|0:1,3386|32767:32767,33901|1:1024,33902|1:1",
|
||||
"zoom.stealth.webgl.float_params": "",
|
||||
"zoom.stealth.webgl.shader_precisions": "35633*36336|127:127:23,35633*36337|127:127:23,35633*36338|127:127:23,35633*36339|31:30:0,35633*36340|31:30:0,35633*36341|31:30:0,35632*36336|127:127:23,35632*36337|127:127:23,35632*36338|127:127:23,35632*36339|31:30:0,35632*36340|31:30:0,35632*36341|31:30:0",
|
||||
"zoom.stealth.webgl2.enabled": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"lin": [
|
||||
{
|
||||
"vendor": "Intel",
|
||||
"renderer_out": "Intel(R) HD Graphics, or similar",
|
||||
"prob": 0.282051,
|
||||
"prefs": {
|
||||
"zoom.stealth.webgl.renderer": "Intel(R) HD Graphics, or similar",
|
||||
"zoom.stealth.webgl.vendor": "Intel",
|
||||
"zoom.stealth.webgl.extensions": "ANGLE_instanced_arrays,EXT_blend_minmax,EXT_color_buffer_half_float,EXT_depth_clamp,EXT_float_blend,EXT_frag_depth,EXT_shader_texture_lod,EXT_sRGB,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_element_index_uint,OES_fbo_render_mipmap,OES_standard_derivatives,OES_texture_float,OES_texture_float_linear,OES_texture_half_float,OES_texture_half_float_linear,OES_vertex_array_object,WEBGL_color_buffer_float,WEBGL_compressed_texture_astc,WEBGL_compressed_texture_etc,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_depth_texture,WEBGL_draw_buffers,WEBGL_lose_context",
|
||||
"zoom.stealth.webgl2.extensions": "EXT_color_buffer_float,EXT_depth_clamp,EXT_float_blend,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_draw_buffers_indexed,OES_texture_float_linear,WEBGL_compressed_texture_astc,WEBGL_compressed_texture_etc,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_lose_context",
|
||||
"zoom.stealth.webgl.int_params": "2849|1,2885|1029,2886|2305,2931|1,2932|513,2961|0,2962|519,2963|255,2964|7680,2965|7680,2966|7680,2967|0,2968|255,3074|1029,3314|0,3315|0,3316|0,3317|4,3330|0,3331|0,3332|0,3333|4,3379|8192,3408|4,3410|8,3411|8,3412|8,3413|8,3414|24,3415|0,10752|0,32777|32774,32824|0,32877|0,32878|0,32883|2048,32936|1,32937|4,32938|1,32968|0,32969|1,32970|0,32971|1,33000|3000,33001|3000,33170|4352,34016|33984,34024|8192,34045|15,34076|8192,34816|519,34817|7680,34818|7680,34819|7680,34852|8,34853|1029,34854|0,34855|0,34856|0,34857|0,34858|0,34859|0,34860|0,34877|32774,34921|16,34930|32,35071|2048,35076|-8,35077|7,35371|15,35373|15,35374|90,35375|90,35376|65536,35377|262144,35379|262144,35380|32,35657|16384,35658|16384,35659|128,35660|32,35661|192,35723|4352,35738|5121,35739|6408,35968|16,35978|64,35979|4,36003|0,36004|255,36005|255,36063|8,36183|16,36203|4294967295,36347|4096,36348|32,36349|4096,37137|9223372034707292000,37154|128,37157|128,37443|37444,37447|1000000000",
|
||||
"zoom.stealth.webgl.int2_params": "2928|0:1,3386|16384:16384,33901|1:255,33902|1:1",
|
||||
"zoom.stealth.webgl.float_params": "",
|
||||
"zoom.stealth.webgl.shader_precisions": "35633*36336|127:127:23,35633*36337|127:127:23,35633*36338|127:127:23,35633*36339|24:24:0,35633*36340|24:24:0,35633*36341|24:24:0,35632*36336|127:127:23,35632*36337|127:127:23,35632*36338|127:127:23,35632*36339|24:24:0,35632*36340|24:24:0,35632*36341|24:24:0",
|
||||
"zoom.stealth.webgl2.enabled": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"vendor": "NVIDIA Corporation",
|
||||
"renderer_out": "NVIDIA GeForce GTX 980, or similar",
|
||||
"prob": 0.25641,
|
||||
"prefs": {
|
||||
"zoom.stealth.webgl.renderer": "NVIDIA GeForce GTX 980, or similar",
|
||||
"zoom.stealth.webgl.vendor": "NVIDIA Corporation",
|
||||
"zoom.stealth.webgl.extensions": "ANGLE_instanced_arrays,EXT_blend_minmax,EXT_color_buffer_half_float,EXT_depth_clamp,EXT_float_blend,EXT_frag_depth,EXT_shader_texture_lod,EXT_sRGB,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_element_index_uint,OES_fbo_render_mipmap,OES_standard_derivatives,OES_texture_float,OES_texture_float_linear,OES_texture_half_float,OES_texture_half_float_linear,OES_vertex_array_object,WEBGL_color_buffer_float,WEBGL_compressed_texture_etc,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_depth_texture,WEBGL_draw_buffers,WEBGL_lose_context",
|
||||
"zoom.stealth.webgl2.extensions": "EXT_color_buffer_float,EXT_depth_clamp,EXT_float_blend,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_draw_buffers_indexed,OES_texture_float_linear,OVR_multiview2,WEBGL_compressed_texture_etc,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_lose_context",
|
||||
"zoom.stealth.webgl.int_params": "2849|1,2885|1029,2886|2305,2931|1,2932|513,2961|0,2962|519,2963|4294967295,2964|7680,2965|7680,2966|7680,2967|0,2968|4294967295,3074|1029,3314|0,3315|0,3316|0,3317|4,3330|0,3331|0,3332|0,3333|4,3379|32768,3408|8,3410|8,3411|8,3412|8,3413|8,3414|24,3415|0,10752|0,32777|32774,32824|0,32877|0,32878|0,32883|16384,32936|1,32937|4,32938|1,32968|0,32969|1,32970|0,32971|1,33000|1048576,33001|1048576,33170|4352,34016|33984,34024|32768,34045|15,34076|32768,34816|519,34817|7680,34818|7680,34819|7680,34852|8,34853|1029,34854|0,34855|0,34856|0,34857|0,34858|0,34859|0,34860|0,34877|32774,34921|16,34930|32,35071|2048,35076|-8,35077|7,35371|14,35373|14,35374|84,35375|84,35376|65536,35377|233472,35379|233472,35380|256,35657|4096,35658|4096,35659|124,35660|32,35661|192,35723|4352,35738|5121,35739|6408,35968|4,35978|128,35979|4,36003|0,36004|4294967295,36005|4294967295,36063|8,36183|32,36203|4294967295,36347|1024,36348|32,36349|1024,37137|18446744073709552000,37154|128,37157|128,37443|37444,37447|1000000000",
|
||||
"zoom.stealth.webgl.int2_params": "2928|0:1,3386|32768:32768,33901|1:2047,33902|1:1",
|
||||
"zoom.stealth.webgl.float_params": "",
|
||||
"zoom.stealth.webgl.shader_precisions": "35633*36336|127:127:23,35633*36337|127:127:23,35633*36338|127:127:23,35633*36339|24:24:0,35633*36340|24:24:0,35633*36341|24:24:0,35632*36336|127:127:23,35632*36337|127:127:23,35632*36338|127:127:23,35632*36339|24:24:0,35632*36340|24:24:0,35632*36341|24:24:0",
|
||||
"zoom.stealth.webgl2.enabled": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"vendor": "Intel",
|
||||
"renderer_out": "Intel(R) HD Graphics 400, or similar",
|
||||
"prob": 0.153846,
|
||||
"prefs": {
|
||||
"zoom.stealth.webgl.renderer": "Intel(R) HD Graphics 400, or similar",
|
||||
"zoom.stealth.webgl.vendor": "Intel",
|
||||
"zoom.stealth.webgl.extensions": "ANGLE_instanced_arrays,EXT_blend_minmax,EXT_color_buffer_half_float,EXT_depth_clamp,EXT_float_blend,EXT_frag_depth,EXT_shader_texture_lod,EXT_sRGB,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_element_index_uint,OES_fbo_render_mipmap,OES_standard_derivatives,OES_texture_float,OES_texture_float_linear,OES_texture_half_float,OES_texture_half_float_linear,OES_vertex_array_object,WEBGL_color_buffer_float,WEBGL_compressed_texture_astc,WEBGL_compressed_texture_etc,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_depth_texture,WEBGL_draw_buffers,WEBGL_lose_context",
|
||||
"zoom.stealth.webgl2.extensions": "EXT_color_buffer_float,EXT_depth_clamp,EXT_float_blend,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_draw_buffers_indexed,OES_texture_float_linear,WEBGL_compressed_texture_astc,WEBGL_compressed_texture_etc,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_lose_context",
|
||||
"zoom.stealth.webgl.int_params": "2849|1,2885|1029,2886|2305,2931|1,2932|513,2961|0,2962|519,2963|255,2964|7680,2965|7680,2966|7680,2967|0,2968|255,3074|1029,3314|0,3315|0,3316|0,3317|4,3330|0,3331|0,3332|0,3333|4,3379|8192,3408|4,3410|8,3411|8,3412|8,3413|8,3414|24,3415|0,10752|0,32777|32774,32824|0,32877|0,32878|0,32883|2048,32936|1,32937|4,32938|1,32968|0,32969|1,32970|0,32971|1,33000|3000,33001|3000,33170|4352,34016|33984,34024|8192,34045|15,34076|8192,34816|519,34817|7680,34818|7680,34819|7680,34852|8,34853|1029,34854|0,34855|0,34856|0,34857|0,34858|0,34859|0,34860|0,34877|32774,34921|16,34930|32,35071|2048,35076|-8,35077|7,35371|15,35373|15,35374|90,35375|90,35376|65536,35377|262144,35379|262144,35380|32,35657|16384,35658|16384,35659|128,35660|32,35661|192,35723|4352,35738|5121,35739|6408,35968|16,35978|64,35979|4,36003|0,36004|255,36005|255,36063|8,36183|8,36203|4294967295,36347|4096,36348|32,36349|4096,37137|9223372034707292000,37154|128,37157|128,37443|37444,37447|1000000000",
|
||||
"zoom.stealth.webgl.int2_params": "2928|0:1,3386|16384:16384,33901|1:255,33902|1:1",
|
||||
"zoom.stealth.webgl.float_params": "",
|
||||
"zoom.stealth.webgl.shader_precisions": "35633*36336|127:127:23,35633*36337|127:127:23,35633*36338|127:127:23,35633*36339|24:24:0,35633*36340|24:24:0,35633*36341|24:24:0,35632*36336|127:127:23,35632*36337|127:127:23,35632*36338|127:127:23,35632*36339|24:24:0,35632*36340|24:24:0,35632*36341|24:24:0",
|
||||
"zoom.stealth.webgl2.enabled": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"vendor": "AMD",
|
||||
"renderer_out": "Radeon R9 200 Series, or similar",
|
||||
"prob": 0.102564,
|
||||
"prefs": {
|
||||
"zoom.stealth.webgl.renderer": "Radeon R9 200 Series, or similar",
|
||||
"zoom.stealth.webgl.vendor": "AMD",
|
||||
"zoom.stealth.webgl.extensions": "ANGLE_instanced_arrays,EXT_blend_minmax,EXT_color_buffer_half_float,EXT_depth_clamp,EXT_float_blend,EXT_frag_depth,EXT_shader_texture_lod,EXT_sRGB,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_element_index_uint,OES_fbo_render_mipmap,OES_standard_derivatives,OES_texture_float,OES_texture_float_linear,OES_texture_half_float,OES_texture_half_float_linear,OES_vertex_array_object,WEBGL_color_buffer_float,WEBGL_compressed_texture_astc,WEBGL_compressed_texture_etc,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_depth_texture,WEBGL_draw_buffers,WEBGL_lose_context",
|
||||
"zoom.stealth.webgl2.extensions": "EXT_color_buffer_float,EXT_depth_clamp,EXT_float_blend,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_draw_buffers_indexed,OES_texture_float_linear,WEBGL_compressed_texture_astc,WEBGL_compressed_texture_etc,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_lose_context",
|
||||
"zoom.stealth.webgl.int_params": "2849|1,2885|1029,2886|2305,2931|1,2932|513,2961|0,2962|519,2963|255,2964|7680,2965|7680,2966|7680,2967|0,2968|255,3074|1029,3314|0,3315|0,3316|0,3317|4,3330|0,3331|0,3332|0,3333|4,3379|16384,3408|8,3410|8,3411|8,3412|8,3413|8,3414|24,3415|0,10752|0,32777|32774,32824|0,32877|0,32878|0,32883|2048,32936|1,32937|4,32938|1,32968|0,32969|1,32970|0,32971|1,33000|3000,33001|3000,33170|4352,34016|33984,34024|16384,34045|16,34076|16384,34816|519,34817|7680,34818|7680,34819|7680,34852|8,34853|1029,34854|0,34855|0,34856|0,34857|0,34858|0,34859|0,34860|0,34877|32774,34921|16,34930|32,35071|2048,35076|-32,35077|31,35371|15,35373|15,35374|90,35375|90,35376|4294967292,35377|16106143729,35379|16106143729,35380|4,35657|16384,35658|16384,35659|128,35660|32,35661|192,35723|4352,35738|5121,35739|6408,35968|128,35978|128,35979|4,36003|0,36004|255,36005|255,36063|8,36183|8,36203|4294967295,36347|4096,36348|32,36349|4096,37137|9223372034707292000,37154|128,37157|128,37443|37444,37447|1000000000",
|
||||
"zoom.stealth.webgl.int2_params": "2928|0:1,3386|16384:16384,33902|1:1",
|
||||
"zoom.stealth.webgl.float_params": "33901|0.125:2048",
|
||||
"zoom.stealth.webgl.shader_precisions": "35633*36336|127:127:23,35633*36337|127:127:23,35633*36338|127:127:23,35633*36339|24:24:0,35633*36340|24:24:0,35633*36341|24:24:0,35632*36336|127:127:23,35632*36337|127:127:23,35632*36338|127:127:23,35632*36339|24:24:0,35632*36340|24:24:0,35632*36341|24:24:0",
|
||||
"zoom.stealth.webgl2.enabled": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"vendor": "AMD",
|
||||
"renderer_out": "Radeon HD 3200 Graphics, or similar",
|
||||
"prob": 0.083333,
|
||||
"prefs": {
|
||||
"zoom.stealth.webgl.renderer": "Radeon HD 3200 Graphics, or similar",
|
||||
"zoom.stealth.webgl.vendor": "AMD",
|
||||
"zoom.stealth.webgl.extensions": "ANGLE_instanced_arrays,EXT_blend_minmax,EXT_color_buffer_half_float,EXT_depth_clamp,EXT_float_blend,EXT_frag_depth,EXT_shader_texture_lod,EXT_sRGB,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_element_index_uint,OES_fbo_render_mipmap,OES_standard_derivatives,OES_texture_float,OES_texture_float_linear,OES_texture_half_float,OES_texture_half_float_linear,OES_vertex_array_object,WEBGL_color_buffer_float,WEBGL_compressed_texture_astc,WEBGL_compressed_texture_etc,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_depth_texture,WEBGL_draw_buffers,WEBGL_lose_context",
|
||||
"zoom.stealth.webgl2.extensions": "EXT_color_buffer_float,EXT_depth_clamp,EXT_float_blend,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_draw_buffers_indexed,OES_texture_float_linear,WEBGL_compressed_texture_astc,WEBGL_compressed_texture_etc,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_lose_context",
|
||||
"zoom.stealth.webgl.int_params": "2849|1,2885|1029,2886|2305,2931|1,2932|513,2961|0,2962|519,2963|255,2964|7680,2965|7680,2966|7680,2967|0,2968|255,3074|1029,3314|0,3315|0,3316|0,3317|4,3330|0,3331|0,3332|0,3333|4,3379|16384,3408|8,3410|8,3411|8,3412|8,3413|8,3414|24,3415|0,10752|0,32777|32774,32824|0,32877|0,32878|0,32883|2048,32936|1,32937|4,32938|1,32968|0,32969|1,32970|0,32971|1,33000|3000,33001|3000,33170|4352,34016|33984,34024|16384,34045|16,34076|16384,34816|519,34817|7680,34818|7680,34819|7680,34852|8,34853|1029,34854|0,34855|0,34856|0,34857|0,34858|0,34859|0,34860|0,34877|32774,34921|16,34930|32,35071|2048,35076|-32,35077|31,35371|15,35373|15,35374|90,35375|90,35376|4051211264,35377|15192058624,35379|15192058624,35380|4,35657|16384,35658|16384,35659|128,35660|32,35661|192,35723|4352,35738|5121,35739|6408,35968|128,35978|128,35979|4,36003|0,36004|255,36005|255,36063|8,36183|8,36203|4294967295,36347|4096,36348|32,36349|4096,37137|9223372034707292000,37154|128,37157|128,37443|37444,37447|1000000000",
|
||||
"zoom.stealth.webgl.int2_params": "2928|0:1,3386|16384:16384,33902|1:1",
|
||||
"zoom.stealth.webgl.float_params": "33901|0.125:2048",
|
||||
"zoom.stealth.webgl.shader_precisions": "35633*36336|127:127:23,35633*36337|127:127:23,35633*36338|127:127:23,35633*36339|24:24:0,35633*36340|24:24:0,35633*36341|24:24:0,35632*36336|127:127:23,35632*36337|127:127:23,35632*36338|127:127:23,35632*36339|24:24:0,35632*36340|24:24:0,35632*36341|24:24:0",
|
||||
"zoom.stealth.webgl2.enabled": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"vendor": "Mesa",
|
||||
"renderer_out": "llvmpipe, or similar",
|
||||
"prob": 0.070513,
|
||||
"prefs": {
|
||||
"zoom.stealth.webgl.renderer": "llvmpipe, or similar",
|
||||
"zoom.stealth.webgl.vendor": "Mesa",
|
||||
"zoom.stealth.webgl.extensions": "ANGLE_instanced_arrays,EXT_blend_minmax,EXT_color_buffer_half_float,EXT_depth_clamp,EXT_float_blend,EXT_frag_depth,EXT_shader_texture_lod,EXT_sRGB,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_element_index_uint,OES_fbo_render_mipmap,OES_standard_derivatives,OES_texture_float,OES_texture_float_linear,OES_texture_half_float,OES_texture_half_float_linear,OES_vertex_array_object,WEBGL_color_buffer_float,WEBGL_compressed_texture_astc,WEBGL_compressed_texture_etc,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_depth_texture,WEBGL_draw_buffers,WEBGL_lose_context",
|
||||
"zoom.stealth.webgl2.extensions": "EXT_color_buffer_float,EXT_depth_clamp,EXT_float_blend,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_draw_buffers_indexed,OES_texture_float_linear,WEBGL_compressed_texture_astc,WEBGL_compressed_texture_etc,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_lose_context",
|
||||
"zoom.stealth.webgl.int_params": "2849|1,2885|1029,2886|2305,2931|1,2932|513,2961|0,2962|519,2963|255,2964|7680,2965|7680,2966|7680,2967|0,2968|255,3074|1029,3314|0,3315|0,3316|0,3317|4,3330|0,3331|0,3332|0,3333|4,3379|16384,3408|8,3410|8,3411|8,3412|8,3413|8,3414|24,3415|0,10752|0,32777|32774,32824|0,32877|0,32878|0,32883|2048,32936|1,32937|4,32938|1,32968|0,32969|1,32970|0,32971|1,33000|3000,33001|3000,33170|4352,34016|33984,34024|16384,34045|16,34076|16384,34816|519,34817|7680,34818|7680,34819|7680,34852|8,34853|1029,34854|0,34855|0,34856|0,34857|0,34858|0,34859|0,34860|0,34877|32774,34921|16,34930|32,35071|2048,35076|-32,35077|31,35371|15,35373|15,35374|90,35375|90,35376|65536,35377|262144,35379|262144,35380|16,35657|16384,35658|16384,35659|128,35660|32,35661|192,35723|4352,35738|5121,35739|6408,35968|64,35978|64,35979|4,36003|0,36004|255,36005|255,36063|8,36183|4,36203|4294967295,36347|4096,36348|32,36349|4096,37137|9223372034707292000,37154|128,37157|128,37443|37444,37447|1000000000",
|
||||
"zoom.stealth.webgl.int2_params": "2928|0:1,3386|16384:16384,33901|1:255,33902|1:1",
|
||||
"zoom.stealth.webgl.float_params": "",
|
||||
"zoom.stealth.webgl.shader_precisions": "35633*36336|127:127:23,35633*36337|127:127:23,35633*36338|127:127:23,35633*36339|24:24:0,35633*36340|24:24:0,35633*36341|24:24:0,35632*36336|127:127:23,35632*36337|127:127:23,35632*36338|127:127:23,35632*36339|24:24:0,35632*36340|24:24:0,35632*36341|24:24:0",
|
||||
"zoom.stealth.webgl2.enabled": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"vendor": "Intel",
|
||||
"renderer_out": "Intel(R) HD Graphics 400",
|
||||
"prob": 0.019231,
|
||||
"prefs": {
|
||||
"zoom.stealth.webgl.renderer": "Intel(R) HD Graphics 400",
|
||||
"zoom.stealth.webgl.vendor": "Intel",
|
||||
"zoom.stealth.webgl.extensions": "ANGLE_instanced_arrays,EXT_blend_minmax,EXT_color_buffer_half_float,EXT_float_blend,EXT_frag_depth,EXT_shader_texture_lod,EXT_sRGB,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_element_index_uint,OES_fbo_render_mipmap,OES_standard_derivatives,OES_texture_float,OES_texture_float_linear,OES_texture_half_float,OES_texture_half_float_linear,OES_vertex_array_object,WEBGL_color_buffer_float,WEBGL_compressed_texture_astc,WEBGL_compressed_texture_etc,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_depth_texture,WEBGL_draw_buffers,WEBGL_lose_context",
|
||||
"zoom.stealth.webgl2.extensions": "EXT_color_buffer_float,EXT_float_blend,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_draw_buffers_indexed,OES_texture_float_linear,WEBGL_compressed_texture_astc,WEBGL_compressed_texture_etc,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_lose_context",
|
||||
"zoom.stealth.webgl.int_params": "2849|1,2885|1029,2886|2305,2931|1,2932|513,2961|0,2962|519,2963|255,2964|7680,2965|7680,2966|7680,2967|0,2968|255,3074|1029,3314|0,3315|0,3316|0,3317|4,3330|0,3331|0,3332|0,3333|4,3379|8192,3408|4,3410|8,3411|8,3412|8,3413|8,3414|24,3415|0,10752|0,32777|32774,32824|0,32877|0,32878|0,32883|2048,32936|1,32937|4,32938|1,32968|0,32969|1,32970|0,32971|1,33000|3000,33001|3000,33170|4352,34016|33984,34024|8192,34045|15,34076|8192,34816|519,34817|7680,34818|7680,34819|7680,34852|8,34853|1029,34854|0,34855|0,34856|0,34857|0,34858|0,34859|0,34860|0,34877|32774,34921|16,34930|32,35071|2048,35076|-8,35077|7,35371|15,35373|15,35374|90,35375|90,35376|65536,35377|262144,35379|262144,35380|32,35657|16384,35658|16384,35659|128,35660|32,35661|192,35723|4352,35738|5121,35739|6408,35968|16,35978|64,35979|4,36003|0,36004|255,36005|255,36063|8,36183|8,36203|4294967295,36347|4096,36348|32,36349|4096,37137|9223372034707292000,37154|128,37157|128,37443|37444,37447|1000000000",
|
||||
"zoom.stealth.webgl.int2_params": "2928|0:1,3386|16384:16384,33901|1:255,33902|1:1",
|
||||
"zoom.stealth.webgl.float_params": "",
|
||||
"zoom.stealth.webgl.shader_precisions": "35633*36336|127:127:23,35633*36337|127:127:23,35633*36338|127:127:23,35633*36339|24:24:0,35633*36340|24:24:0,35633*36341|24:24:0,35632*36336|127:127:23,35632*36337|127:127:23,35632*36338|127:127:23,35632*36339|24:24:0,35632*36340|24:24:0,35632*36341|24:24:0",
|
||||
"zoom.stealth.webgl2.enabled": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"vendor": "Mesa",
|
||||
"renderer_out": "llvmpipe",
|
||||
"prob": 0.019231,
|
||||
"prefs": {
|
||||
"zoom.stealth.webgl.renderer": "llvmpipe",
|
||||
"zoom.stealth.webgl.vendor": "Mesa",
|
||||
"zoom.stealth.webgl.extensions": "ANGLE_instanced_arrays,EXT_blend_minmax,EXT_color_buffer_half_float,EXT_float_blend,EXT_frag_depth,EXT_shader_texture_lod,EXT_sRGB,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_element_index_uint,OES_fbo_render_mipmap,OES_standard_derivatives,OES_texture_float,OES_texture_float_linear,OES_texture_half_float,OES_texture_half_float_linear,OES_vertex_array_object,WEBGL_color_buffer_float,WEBGL_compressed_texture_astc,WEBGL_compressed_texture_etc,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_depth_texture,WEBGL_draw_buffers,WEBGL_lose_context",
|
||||
"zoom.stealth.webgl2.extensions": "EXT_color_buffer_float,EXT_float_blend,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_draw_buffers_indexed,OES_texture_float_linear,WEBGL_compressed_texture_astc,WEBGL_compressed_texture_etc,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_lose_context",
|
||||
"zoom.stealth.webgl.int_params": "2849|1,2885|1029,2886|2305,2931|1,2932|513,2961|0,2962|519,2963|255,2964|7680,2965|7680,2966|7680,2967|0,2968|255,3074|1029,3314|0,3315|0,3316|0,3317|4,3330|0,3331|0,3332|0,3333|4,3379|16384,3408|8,3410|8,3411|8,3412|8,3413|8,3414|24,3415|0,10752|0,32777|32774,32824|0,32877|0,32878|0,32883|2048,32936|1,32937|4,32938|1,32968|0,32969|1,32970|0,32971|1,33000|3000,33001|3000,33170|4352,34016|33984,34024|16384,34045|16,34076|16384,34816|519,34817|7680,34818|7680,34819|7680,34852|8,34853|1029,34854|0,34855|0,34856|0,34857|0,34858|0,34859|0,34860|0,34877|32774,34921|16,34930|32,35071|2048,35076|-32,35077|31,35371|15,35373|15,35374|90,35375|90,35376|65536,35377|262144,35379|262144,35380|16,35657|16384,35658|16384,35659|128,35660|32,35661|192,35723|4352,35738|5121,35739|6408,35968|64,35978|64,35979|4,36003|0,36004|255,36005|255,36063|8,36183|4,36203|4294967295,36347|4096,36348|32,36349|4096,37137|9223372034707292000,37154|128,37157|128,37443|37444,37447|1000000000",
|
||||
"zoom.stealth.webgl.int2_params": "2928|0:1,3386|16384:16384,33901|1:255,33902|1:1",
|
||||
"zoom.stealth.webgl.float_params": "",
|
||||
"zoom.stealth.webgl.shader_precisions": "35633*36336|127:127:23,35633*36337|127:127:23,35633*36338|127:127:23,35633*36339|24:24:0,35633*36340|24:24:0,35633*36341|24:24:0,35632*36336|127:127:23,35632*36337|127:127:23,35632*36338|127:127:23,35632*36339|24:24:0,35632*36340|24:24:0,35632*36341|24:24:0",
|
||||
"zoom.stealth.webgl2.enabled": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"vendor": "NVIDIA Corporation",
|
||||
"renderer_out": "NVIDIA GeForce GTX 980/PCIe/SSE2",
|
||||
"prob": 0.00641,
|
||||
"prefs": {
|
||||
"zoom.stealth.webgl.renderer": "NVIDIA GeForce GTX 980/PCIe/SSE2",
|
||||
"zoom.stealth.webgl.vendor": "NVIDIA Corporation",
|
||||
"zoom.stealth.webgl.extensions": "ANGLE_instanced_arrays,EXT_blend_minmax,EXT_color_buffer_half_float,EXT_float_blend,EXT_frag_depth,EXT_shader_texture_lod,EXT_sRGB,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_element_index_uint,OES_fbo_render_mipmap,OES_standard_derivatives,OES_texture_float,OES_texture_float_linear,OES_texture_half_float,OES_texture_half_float_linear,OES_vertex_array_object,WEBGL_color_buffer_float,WEBGL_compressed_texture_etc,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_depth_texture,WEBGL_draw_buffers,WEBGL_lose_context",
|
||||
"zoom.stealth.webgl2.extensions": "EXT_color_buffer_float,EXT_float_blend,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_draw_buffers_indexed,OES_texture_float_linear,OVR_multiview2,WEBGL_compressed_texture_etc,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_lose_context",
|
||||
"zoom.stealth.webgl.int_params": "2849|1,2885|1029,2886|2305,2931|1,2932|513,2961|0,2962|519,2963|4294967295,2964|7680,2965|7680,2966|7680,2967|0,2968|4294967295,3074|1029,3314|0,3315|0,3316|0,3317|4,3330|0,3331|0,3332|0,3333|4,3379|32768,3408|8,3410|8,3411|8,3412|8,3413|8,3414|24,3415|0,10752|0,32777|32774,32824|0,32877|0,32878|0,32883|16384,32936|1,32937|4,32938|1,32968|0,32969|1,32970|0,32971|1,33000|1048576,33001|1048576,33170|4352,34016|33984,34024|32768,34045|15,34076|32768,34816|519,34817|7680,34818|7680,34819|7680,34852|8,34853|1029,34854|0,34855|0,34856|0,34857|0,34858|0,34859|0,34860|0,34877|32774,34921|16,34930|32,35071|2048,35076|-8,35077|7,35371|14,35373|14,35374|84,35375|84,35376|65536,35377|233472,35379|233472,35380|256,35657|4096,35658|4096,35659|124,35660|32,35661|192,35723|4352,35738|5121,35739|6408,35968|4,35978|128,35979|4,36003|0,36004|4294967295,36005|4294967295,36063|8,36183|32,36203|4294967295,36347|1024,36348|32,36349|1024,37137|18446744073709552000,37154|128,37157|128,37443|37444,37447|1000000000",
|
||||
"zoom.stealth.webgl.int2_params": "2928|0:1,3386|32768:32768,33901|1:2047,33902|1:1",
|
||||
"zoom.stealth.webgl.float_params": "",
|
||||
"zoom.stealth.webgl.shader_precisions": "35633*36336|127:127:23,35633*36337|127:127:23,35633*36338|127:127:23,35633*36339|24:24:0,35633*36340|24:24:0,35633*36341|24:24:0,35632*36336|127:127:23,35632*36337|127:127:23,35632*36338|127:127:23,35632*36339|24:24:0,35632*36340|24:24:0,35632*36341|24:24:0",
|
||||
"zoom.stealth.webgl2.enabled": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"vendor": "Mesa",
|
||||
"renderer_out": "GeForce GTX 980, or similar",
|
||||
"prob": 0.00641,
|
||||
"prefs": {
|
||||
"zoom.stealth.webgl.renderer": "GeForce GTX 980, or similar",
|
||||
"zoom.stealth.webgl.vendor": "Mesa",
|
||||
"zoom.stealth.webgl.extensions": "ANGLE_instanced_arrays,EXT_blend_minmax,EXT_color_buffer_half_float,EXT_depth_clamp,EXT_float_blend,EXT_frag_depth,EXT_shader_texture_lod,EXT_sRGB,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_element_index_uint,OES_fbo_render_mipmap,OES_standard_derivatives,OES_texture_float,OES_texture_float_linear,OES_texture_half_float,OES_texture_half_float_linear,OES_vertex_array_object,WEBGL_color_buffer_float,WEBGL_compressed_texture_astc,WEBGL_compressed_texture_etc,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_depth_texture,WEBGL_draw_buffers,WEBGL_lose_context",
|
||||
"zoom.stealth.webgl2.extensions": "EXT_color_buffer_float,EXT_depth_clamp,EXT_float_blend,EXT_texture_compression_bptc,EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_draw_buffers_indexed,OES_texture_float_linear,WEBGL_compressed_texture_astc,WEBGL_compressed_texture_etc,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_lose_context",
|
||||
"zoom.stealth.webgl.int_params": "2849|1,2885|1029,2886|2305,2931|1,2932|513,2961|0,2962|519,2963|255,2964|7680,2965|7680,2966|7680,2967|0,2968|255,3074|1029,3314|0,3315|0,3316|0,3317|4,3330|0,3331|0,3332|0,3333|4,3379|16384,3408|8,3410|8,3411|8,3412|8,3413|8,3414|24,3415|0,10752|0,32777|32774,32824|0,32877|0,32878|0,32883|2048,32936|1,32937|4,32938|1,32968|0,32969|1,32970|0,32971|1,33000|3000,33001|3000,33170|4352,34016|33984,34024|16384,34045|15,34076|16384,34816|519,34817|7680,34818|7680,34819|7680,34852|8,34853|1029,34854|0,34855|0,34856|0,34857|0,34858|0,34859|0,34860|0,34877|32774,34921|16,34930|32,35071|2048,35076|-8,35077|7,35371|14,35373|14,35374|84,35375|84,35376|65536,35377|245760,35379|245760,35380|256,35657|16384,35658|16384,35659|124,35660|32,35661|192,35723|4352,35738|5121,35739|6408,35968|128,35978|128,35979|4,36003|0,36004|255,36005|255,36063|8,36183|8,36203|4294967295,36347|4096,36348|32,36349|4096,37137|9223372034707292000,37154|128,37157|128,37443|37444,37447|1000000000",
|
||||
"zoom.stealth.webgl.int2_params": "2928|0:1,3386|16384:16384,33902|1:1",
|
||||
"zoom.stealth.webgl.float_params": "33901|1:63.375",
|
||||
"zoom.stealth.webgl.shader_precisions": "35633*36336|127:127:23,35633*36337|127:127:23,35633*36338|127:127:23,35633*36339|24:24:0,35633*36340|24:24:0,35633*36341|24:24:0,35632*36336|127:127:23,35632*36337|127:127:23,35632*36338|127:127:23,35632*36339|24:24:0,35632*36340|24:24:0,35632*36341|24:24:0",
|
||||
"zoom.stealth.webgl2.enabled": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"_meta": {
|
||||
"source": "camoufox webgl_data.db (offline extract; real Firefox WebGL telemetry)",
|
||||
"note": "prefs are zoom.stealth.webgl.* override values; prob normalized per-OS"
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,469 +0,0 @@
|
|||
{
|
||||
"_meta": {
|
||||
"source": "browserforge Windows UAs (offline; OS-level screen/cores marginal)"
|
||||
},
|
||||
"screens": [
|
||||
{
|
||||
"w": 1536,
|
||||
"h": 864,
|
||||
"availW": 1536,
|
||||
"availH": 816,
|
||||
"dpr": 1.25,
|
||||
"prob": 0.264935
|
||||
},
|
||||
{
|
||||
"w": 1920,
|
||||
"h": 1080,
|
||||
"availW": 1920,
|
||||
"availH": 1032,
|
||||
"dpr": 1,
|
||||
"prob": 0.161965
|
||||
},
|
||||
{
|
||||
"w": 1280,
|
||||
"h": 720,
|
||||
"availW": 1280,
|
||||
"availH": 672,
|
||||
"dpr": 1.5,
|
||||
"prob": 0.07915
|
||||
},
|
||||
{
|
||||
"w": 1920,
|
||||
"h": 1080,
|
||||
"availW": 1920,
|
||||
"availH": 1040,
|
||||
"dpr": 1,
|
||||
"prob": 0.06376
|
||||
},
|
||||
{
|
||||
"w": 1366,
|
||||
"h": 768,
|
||||
"availW": 1366,
|
||||
"availH": 728,
|
||||
"dpr": 1,
|
||||
"prob": 0.054416
|
||||
},
|
||||
{
|
||||
"w": 1366,
|
||||
"h": 768,
|
||||
"availW": 1366,
|
||||
"availH": 720,
|
||||
"dpr": 1,
|
||||
"prob": 0.046171
|
||||
},
|
||||
{
|
||||
"w": 1536,
|
||||
"h": 864,
|
||||
"availW": 1536,
|
||||
"availH": 824,
|
||||
"dpr": 1.25,
|
||||
"prob": 0.036644
|
||||
},
|
||||
{
|
||||
"w": 1536,
|
||||
"h": 960,
|
||||
"availW": 1536,
|
||||
"availH": 912,
|
||||
"dpr": 1.25,
|
||||
"prob": 0.029681
|
||||
},
|
||||
{
|
||||
"w": 2560,
|
||||
"h": 1440,
|
||||
"availW": 2560,
|
||||
"availH": 1392,
|
||||
"dpr": 1,
|
||||
"prob": 0.029133
|
||||
},
|
||||
{
|
||||
"w": 1280,
|
||||
"h": 800,
|
||||
"availW": 1280,
|
||||
"availH": 752,
|
||||
"dpr": 1.5,
|
||||
"prob": 0.024917
|
||||
},
|
||||
{
|
||||
"w": 1536,
|
||||
"h": 864,
|
||||
"availW": 1536,
|
||||
"availH": 864,
|
||||
"dpr": 1.25,
|
||||
"prob": 0.024733
|
||||
},
|
||||
{
|
||||
"w": 1920,
|
||||
"h": 1080,
|
||||
"availW": 1920,
|
||||
"availH": 1080,
|
||||
"dpr": 1,
|
||||
"prob": 0.013742
|
||||
},
|
||||
{
|
||||
"w": 1280,
|
||||
"h": 720,
|
||||
"availW": 1280,
|
||||
"availH": 680,
|
||||
"dpr": 1.5,
|
||||
"prob": 0.012643
|
||||
},
|
||||
{
|
||||
"w": 1280,
|
||||
"h": 720,
|
||||
"availW": 1280,
|
||||
"availH": 720,
|
||||
"dpr": 1.5,
|
||||
"prob": 0.01081
|
||||
},
|
||||
{
|
||||
"w": 1920,
|
||||
"h": 1080,
|
||||
"availW": 1920,
|
||||
"availH": 1032,
|
||||
"dpr": 1,
|
||||
"prob": 0.009527
|
||||
},
|
||||
{
|
||||
"w": 1707,
|
||||
"h": 1067,
|
||||
"availW": 1707,
|
||||
"availH": 1019,
|
||||
"dpr": 1.5,
|
||||
"prob": 0.009344
|
||||
},
|
||||
{
|
||||
"w": 1440,
|
||||
"h": 900,
|
||||
"availW": 1440,
|
||||
"availH": 852,
|
||||
"dpr": 2,
|
||||
"prob": 0.008795
|
||||
},
|
||||
{
|
||||
"w": 1920,
|
||||
"h": 1080,
|
||||
"availW": 1920,
|
||||
"availH": 1032,
|
||||
"dpr": 1,
|
||||
"prob": 0.008795
|
||||
},
|
||||
{
|
||||
"w": 1920,
|
||||
"h": 1200,
|
||||
"availW": 1920,
|
||||
"availH": 1152,
|
||||
"dpr": 1,
|
||||
"prob": 0.007513
|
||||
},
|
||||
{
|
||||
"w": 1920,
|
||||
"h": 1080,
|
||||
"availW": 1920,
|
||||
"availH": 1032,
|
||||
"dpr": 1,
|
||||
"prob": 0.007146
|
||||
},
|
||||
{
|
||||
"w": 1920,
|
||||
"h": 1080,
|
||||
"availW": 1920,
|
||||
"availH": 1032,
|
||||
"dpr": 1,
|
||||
"prob": 0.006596
|
||||
},
|
||||
{
|
||||
"w": 1600,
|
||||
"h": 900,
|
||||
"availW": 1600,
|
||||
"availH": 860,
|
||||
"dpr": 1,
|
||||
"prob": 0.006229
|
||||
},
|
||||
{
|
||||
"w": 2048,
|
||||
"h": 1152,
|
||||
"availW": 2048,
|
||||
"availH": 1104,
|
||||
"dpr": 1.25,
|
||||
"prob": 0.006045
|
||||
},
|
||||
{
|
||||
"w": 1920,
|
||||
"h": 1080,
|
||||
"availW": 1920,
|
||||
"availH": 1032,
|
||||
"dpr": 1,
|
||||
"prob": 0.005864
|
||||
},
|
||||
{
|
||||
"w": 1536,
|
||||
"h": 864,
|
||||
"availW": 1536,
|
||||
"availH": 816,
|
||||
"dpr": 1.25,
|
||||
"prob": 0.005864
|
||||
},
|
||||
{
|
||||
"w": 1366,
|
||||
"h": 768,
|
||||
"availW": 1366,
|
||||
"availH": 768,
|
||||
"dpr": 1,
|
||||
"prob": 0.00568
|
||||
},
|
||||
{
|
||||
"w": 2560,
|
||||
"h": 1440,
|
||||
"availW": 2560,
|
||||
"availH": 1392,
|
||||
"dpr": 1.5,
|
||||
"prob": 0.00513
|
||||
},
|
||||
{
|
||||
"w": 1707,
|
||||
"h": 960,
|
||||
"availW": 1707,
|
||||
"availH": 912,
|
||||
"dpr": 1.5,
|
||||
"prob": 0.004946
|
||||
},
|
||||
{
|
||||
"w": 1920,
|
||||
"h": 1080,
|
||||
"availW": 1920,
|
||||
"availH": 1040,
|
||||
"dpr": 1,
|
||||
"prob": 0.004946
|
||||
},
|
||||
{
|
||||
"w": 3440,
|
||||
"h": 1440,
|
||||
"availW": 3440,
|
||||
"availH": 1392,
|
||||
"dpr": 1,
|
||||
"prob": 0.00458
|
||||
},
|
||||
{
|
||||
"w": 1504,
|
||||
"h": 1003,
|
||||
"availW": 1504,
|
||||
"availH": 955,
|
||||
"dpr": 1.5,
|
||||
"prob": 0.004396
|
||||
},
|
||||
{
|
||||
"w": 1920,
|
||||
"h": 1080,
|
||||
"availW": 1920,
|
||||
"availH": 1032,
|
||||
"dpr": 1,
|
||||
"prob": 0.004396
|
||||
},
|
||||
{
|
||||
"w": 1280,
|
||||
"h": 800,
|
||||
"availW": 1280,
|
||||
"availH": 752,
|
||||
"dpr": 2,
|
||||
"prob": 0.004396
|
||||
},
|
||||
{
|
||||
"w": 1536,
|
||||
"h": 864,
|
||||
"availW": 1536,
|
||||
"availH": 816,
|
||||
"dpr": 1.25,
|
||||
"prob": 0.004396
|
||||
},
|
||||
{
|
||||
"w": 1920,
|
||||
"h": 1080,
|
||||
"availW": 1920,
|
||||
"availH": 1040,
|
||||
"dpr": 1,
|
||||
"prob": 0.004031
|
||||
},
|
||||
{
|
||||
"w": 1920,
|
||||
"h": 1080,
|
||||
"availW": 1920,
|
||||
"availH": 1050,
|
||||
"dpr": 1,
|
||||
"prob": 0.003847
|
||||
},
|
||||
{
|
||||
"w": 2560,
|
||||
"h": 1440,
|
||||
"availW": 2560,
|
||||
"availH": 1400,
|
||||
"dpr": 1,
|
||||
"prob": 0.003847
|
||||
},
|
||||
{
|
||||
"w": 1366,
|
||||
"h": 768,
|
||||
"availW": 1366,
|
||||
"availH": 728,
|
||||
"dpr": 1,
|
||||
"prob": 0.003664
|
||||
},
|
||||
{
|
||||
"w": 1920,
|
||||
"h": 1080,
|
||||
"availW": 1920,
|
||||
"availH": 1032,
|
||||
"dpr": 1,
|
||||
"prob": 0.003664
|
||||
},
|
||||
{
|
||||
"w": 1600,
|
||||
"h": 900,
|
||||
"availW": 1600,
|
||||
"availH": 852,
|
||||
"dpr": 1,
|
||||
"prob": 0.003664
|
||||
}
|
||||
],
|
||||
"cores": [
|
||||
{
|
||||
"value": 8,
|
||||
"prob": 0.271311
|
||||
},
|
||||
{
|
||||
"value": 12,
|
||||
"prob": 0.21567
|
||||
},
|
||||
{
|
||||
"value": 4,
|
||||
"prob": 0.157888
|
||||
},
|
||||
{
|
||||
"value": 16,
|
||||
"prob": 0.136607
|
||||
},
|
||||
{
|
||||
"value": 20,
|
||||
"prob": 0.03971
|
||||
},
|
||||
{
|
||||
"value": 2,
|
||||
"prob": 0.025086
|
||||
},
|
||||
{
|
||||
"value": 24,
|
||||
"prob": 0.023184
|
||||
},
|
||||
{
|
||||
"value": 6,
|
||||
"prob": 0.022233
|
||||
},
|
||||
{
|
||||
"value": 32,
|
||||
"prob": 0.021876
|
||||
},
|
||||
{
|
||||
"value": 14,
|
||||
"prob": 0.015575
|
||||
},
|
||||
{
|
||||
"value": 3,
|
||||
"prob": 0.011651
|
||||
},
|
||||
{
|
||||
"value": 28,
|
||||
"prob": 0.010462
|
||||
},
|
||||
{
|
||||
"value": 22,
|
||||
"prob": 0.010225
|
||||
},
|
||||
{
|
||||
"value": 5,
|
||||
"prob": 0.008441
|
||||
},
|
||||
{
|
||||
"value": 10,
|
||||
"prob": 0.005588
|
||||
},
|
||||
{
|
||||
"value": 7,
|
||||
"prob": 0.005469
|
||||
},
|
||||
{
|
||||
"value": 9,
|
||||
"prob": 0.004042
|
||||
},
|
||||
{
|
||||
"value": 11,
|
||||
"prob": 0.003923
|
||||
},
|
||||
{
|
||||
"value": 18,
|
||||
"prob": 0.003567
|
||||
},
|
||||
{
|
||||
"value": 15,
|
||||
"prob": 0.001189
|
||||
},
|
||||
{
|
||||
"value": 13,
|
||||
"prob": 0.000951
|
||||
},
|
||||
{
|
||||
"value": 36,
|
||||
"prob": 0.000713
|
||||
},
|
||||
{
|
||||
"value": 64,
|
||||
"prob": 0.000594
|
||||
},
|
||||
{
|
||||
"value": 44,
|
||||
"prob": 0.000594
|
||||
},
|
||||
{
|
||||
"value": 19,
|
||||
"prob": 0.000476
|
||||
},
|
||||
{
|
||||
"value": 17,
|
||||
"prob": 0.000357
|
||||
},
|
||||
{
|
||||
"value": 23,
|
||||
"prob": 0.000357
|
||||
},
|
||||
{
|
||||
"value": 40,
|
||||
"prob": 0.000357
|
||||
},
|
||||
{
|
||||
"value": 21,
|
||||
"prob": 0.000357
|
||||
},
|
||||
{
|
||||
"value": 48,
|
||||
"prob": 0.000357
|
||||
},
|
||||
{
|
||||
"value": 640,
|
||||
"prob": 0.000357
|
||||
},
|
||||
{
|
||||
"value": 56,
|
||||
"prob": 0.000357
|
||||
},
|
||||
{
|
||||
"value": 26,
|
||||
"prob": 0.000238
|
||||
},
|
||||
{
|
||||
"value": 27,
|
||||
"prob": 0.000119
|
||||
},
|
||||
{
|
||||
"value": 25,
|
||||
"prob": 0.000119
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,273 +0,0 @@
|
|||
"""Public dataclass surface for fpforge."""
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field, replace as _dc_replace
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from ._sampler import sample as _sample_raw
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class GPUProfile:
|
||||
vendor: str
|
||||
renderer: str
|
||||
class_tier: str # "low_end" | "mid_range" | "high_end" | "integrated_old" | "integrated_modern"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ScreenProfile:
|
||||
width: int
|
||||
height: int
|
||||
avail_width: int
|
||||
avail_height: int
|
||||
dpr: float
|
||||
tier: str
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class HardwareProfile:
|
||||
concurrency: int
|
||||
storage_quota_mb: int
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class AudioProfile:
|
||||
sample_rate: int
|
||||
output_latency_ms: int
|
||||
max_channel_count: int
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class CodecProfile:
|
||||
av1_enabled: bool
|
||||
webm_encoder_enabled: bool
|
||||
mediasource_webm: bool
|
||||
mediasource_mp4: bool
|
||||
webspeech_synth: bool
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class WebGLProfile:
|
||||
msaa_samples: int
|
||||
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────
|
||||
# Pin map: flat dotted-path -> value. Set via `pin=` on generate_profile.
|
||||
#
|
||||
# Supported keys:
|
||||
# "gpu.vendor", "gpu.renderer", "gpu.class_tier"
|
||||
# "screen.width", "screen.height", "screen.avail_width",
|
||||
# "screen.avail_height", "screen.dpr", "screen.tier"
|
||||
# "hardware.concurrency", "hardware.storage_quota_mb"
|
||||
# "audio.sample_rate", "audio.output_latency_ms",
|
||||
# "audio.max_channel_count"
|
||||
# "codec.av1_enabled", "codec.webm_encoder_enabled",
|
||||
# "codec.mediasource_webm", "codec.mediasource_mp4",
|
||||
# "codec.webspeech_synth"
|
||||
# "webgl.msaa_samples"
|
||||
# "fonts" (replaces the whole list)
|
||||
# "dark_theme"
|
||||
# ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
_PIN_GROUPS = {
|
||||
"gpu": {"vendor", "renderer", "class_tier"},
|
||||
"screen": {"width", "height", "avail_width", "avail_height", "dpr", "tier"},
|
||||
"hardware": {"concurrency", "storage_quota_mb"},
|
||||
"audio": {"sample_rate", "output_latency_ms", "max_channel_count"},
|
||||
"codec": {
|
||||
"av1_enabled", "webm_encoder_enabled",
|
||||
"mediasource_webm", "mediasource_mp4", "webspeech_synth",
|
||||
},
|
||||
"webgl": {"msaa_samples"},
|
||||
}
|
||||
_PIN_TOP = {"fonts", "dark_theme"}
|
||||
|
||||
|
||||
def _validate_pin_key(key: str) -> None:
|
||||
if key in _PIN_TOP:
|
||||
return
|
||||
if "." not in key:
|
||||
raise ValueError(
|
||||
f"pin key {key!r} is not valid. "
|
||||
f"Use 'group.field' (e.g. 'screen.width') or one of {sorted(_PIN_TOP)}."
|
||||
)
|
||||
group, field_name = key.split(".", 1)
|
||||
if group not in _PIN_GROUPS:
|
||||
raise ValueError(
|
||||
f"pin key {key!r}: unknown group {group!r}. "
|
||||
f"Known groups: {sorted(_PIN_GROUPS)}."
|
||||
)
|
||||
if field_name not in _PIN_GROUPS[group]:
|
||||
raise ValueError(
|
||||
f"pin key {key!r}: unknown field {field_name!r} in group {group!r}. "
|
||||
f"Known fields: {sorted(_PIN_GROUPS[group])}."
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Profile:
|
||||
"""Coherent browser fingerprint profile sampled from a single integer seed.
|
||||
|
||||
Use `generate_profile(seed)` to build one. Pin specific values at build
|
||||
time with `generate_profile(seed, pin={"screen.width": 2560, ...})`.
|
||||
"""
|
||||
seed: int
|
||||
gpu: GPUProfile
|
||||
screen: ScreenProfile
|
||||
hardware: HardwareProfile
|
||||
audio: AudioProfile
|
||||
codec: CodecProfile
|
||||
webgl: WebGLProfile
|
||||
fonts: List[str]
|
||||
dark_theme: bool
|
||||
# Bayesian browsing-history: list of {name, category, cookie_profile}
|
||||
# dicts sampled from data/browsing_pool.json with per-class CPT. Used
|
||||
# by _recaptcha_seed.py to build a coherent cookie pre-seed when the
|
||||
# caller opts in via Stealthfox(prep_recaptcha=True).
|
||||
browsing_history: List[Dict[str, str]] = field(default_factory=list)
|
||||
_raw: Dict[str, Any] = field(default_factory=dict, repr=False, compare=False)
|
||||
|
||||
def to_prefs_dict(self) -> Dict[str, Any]:
|
||||
"""Return the flat dict of raw sampler fields, as produced by the
|
||||
underlying Bayesian sampler. Stable across releases for a given seed."""
|
||||
return dict(self._raw)
|
||||
|
||||
|
||||
# Mapping from flat pin key -> raw sampler dict key, so `to_prefs_dict()`
|
||||
# and `invisible_playwright.prefs.translate_profile_to_prefs` observe the pinned value.
|
||||
_PIN_TO_RAW = {
|
||||
"gpu.vendor": "webgl_vendor",
|
||||
"gpu.renderer": "webgl_renderer",
|
||||
"gpu.class_tier": "gpu_class",
|
||||
"screen.width": "screen_w",
|
||||
"screen.height": "screen_h",
|
||||
"screen.avail_width": "screen_avail_w",
|
||||
"screen.avail_height": "screen_avail_h",
|
||||
"screen.dpr": "dpr",
|
||||
"screen.tier": "screen_tier",
|
||||
"hardware.concurrency": "hw_concurrency",
|
||||
"hardware.storage_quota_mb": "storage_quota_mb",
|
||||
"audio.sample_rate": "audio_sample_rate",
|
||||
"audio.output_latency_ms": "audio_output_latency_ms",
|
||||
"audio.max_channel_count": "audio_max_channel_count",
|
||||
"codec.av1_enabled": "av1_enabled",
|
||||
"codec.webm_encoder_enabled": "webm_encoder_enabled",
|
||||
"codec.mediasource_webm": "mediasource_webm",
|
||||
"codec.mediasource_mp4": "mediasource_mp4",
|
||||
"codec.webspeech_synth": "webspeech_synth",
|
||||
"webgl.msaa_samples": "msaa_samples",
|
||||
"dark_theme": "dark_theme",
|
||||
# "fonts" is a list — handled specially (joined into font_whitelist).
|
||||
}
|
||||
|
||||
|
||||
def _apply_pins_to_raw(raw: Dict[str, Any], pin: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Return a copy of `raw` with the pinned sampler-level fields updated."""
|
||||
out = dict(raw)
|
||||
for key, value in pin.items():
|
||||
if key == "fonts":
|
||||
if not isinstance(value, (list, tuple)):
|
||||
raise TypeError("pin 'fonts' must be a list/tuple of strings")
|
||||
out["font_whitelist"] = ",".join(value)
|
||||
continue
|
||||
raw_key = _PIN_TO_RAW.get(key)
|
||||
if raw_key is None:
|
||||
# Shouldn't happen after validation, but guard anyway.
|
||||
continue
|
||||
out[raw_key] = value
|
||||
return out
|
||||
|
||||
|
||||
def generate_profile(
|
||||
seed: int,
|
||||
pin: Optional[Dict[str, Any]] = None,
|
||||
fixed_gpu_class: Optional[str] = None,
|
||||
) -> Profile:
|
||||
"""Return a deterministic Profile for the given integer seed.
|
||||
|
||||
pin: optional dict of dotted-path keys (e.g. "screen.width", "gpu.renderer")
|
||||
to values that are FORCED in the resulting profile. All other fields
|
||||
are still sampled from the Bayesian network based on `seed`, so the
|
||||
same seed + same pin map always yields the same profile.
|
||||
|
||||
Example — force a specific GPU and screen while letting everything
|
||||
else vary with the seed (via the public invisible_playwright API):
|
||||
|
||||
from invisible_playwright import InvisiblePlaywright
|
||||
|
||||
with InvisiblePlaywright(
|
||||
seed=42,
|
||||
pin={
|
||||
"gpu.renderer": "ANGLE (NVIDIA, NVIDIA GeForce RTX 4090 Direct3D11)",
|
||||
"gpu.vendor": "Google Inc. (NVIDIA)",
|
||||
"gpu.class_tier": "high_end",
|
||||
"screen.width": 2560,
|
||||
"screen.height": 1440,
|
||||
},
|
||||
) as browser:
|
||||
...
|
||||
|
||||
Warning: pinning breaks Bayesian coherence across the pinned fields
|
||||
(if you pin a high-end GPU but leave screen unpinned, you may get a
|
||||
1080p screen that would be unusual for that GPU class). Pin related
|
||||
fields together when coherence matters.
|
||||
|
||||
Supported keys: see the module-level _PIN_GROUPS / _PIN_TOP tables
|
||||
or run `help(generate_profile)` after import.
|
||||
"""
|
||||
if pin:
|
||||
for key in pin:
|
||||
_validate_pin_key(key)
|
||||
|
||||
# fixed_gpu_class re-conditions the whole bundle on a chosen class (used so the
|
||||
# bundle stays coherent with the validated WebGL persona we expose on Windows/mac).
|
||||
# An explicit gpu.class_tier pin still wins.
|
||||
eff_class = (pin or {}).get("gpu.class_tier") or fixed_gpu_class
|
||||
raw = _sample_raw(int(seed), fixed_gpu_class=eff_class)
|
||||
if pin:
|
||||
raw = _apply_pins_to_raw(raw, pin)
|
||||
|
||||
# Font whitelist is stored as a comma-separated string in raw; split it.
|
||||
font_wl = raw.get("font_whitelist", "")
|
||||
if isinstance(font_wl, str):
|
||||
fonts = [f.strip() for f in font_wl.split(",") if f.strip()]
|
||||
else:
|
||||
fonts = list(font_wl) if font_wl else []
|
||||
|
||||
return Profile(
|
||||
seed=int(raw["stealth_seed"]),
|
||||
gpu=GPUProfile(
|
||||
vendor=raw["webgl_vendor"],
|
||||
renderer=raw["webgl_renderer"],
|
||||
class_tier=raw["gpu_class"],
|
||||
),
|
||||
screen=ScreenProfile(
|
||||
width=int(raw["screen_w"]),
|
||||
height=int(raw["screen_h"]),
|
||||
avail_width=int(raw["screen_avail_w"]),
|
||||
avail_height=int(raw["screen_avail_h"]),
|
||||
dpr=float(raw["dpr"]),
|
||||
tier=str(raw.get("screen_tier", "")),
|
||||
),
|
||||
hardware=HardwareProfile(
|
||||
concurrency=int(raw["hw_concurrency"]),
|
||||
storage_quota_mb=int(raw["storage_quota_mb"]),
|
||||
),
|
||||
audio=AudioProfile(
|
||||
sample_rate=int(raw["audio_sample_rate"]),
|
||||
output_latency_ms=int(raw["audio_output_latency_ms"]),
|
||||
max_channel_count=int(raw["audio_max_channel_count"]),
|
||||
),
|
||||
codec=CodecProfile(
|
||||
av1_enabled=bool(raw["av1_enabled"]),
|
||||
webm_encoder_enabled=bool(raw["webm_encoder_enabled"]),
|
||||
mediasource_webm=bool(raw["mediasource_webm"]),
|
||||
mediasource_mp4=bool(raw["mediasource_mp4"]),
|
||||
webspeech_synth=bool(raw["webspeech_synth"]),
|
||||
),
|
||||
webgl=WebGLProfile(msaa_samples=int(raw["msaa_samples"])),
|
||||
fonts=fonts,
|
||||
dark_theme=bool(raw["dark_theme"]),
|
||||
browsing_history=list(raw.get("browsing_history") or []),
|
||||
_raw=raw,
|
||||
)
|
||||
|
|
@ -1,268 +1,4 @@
|
|||
"""Resolve the session timezone from the egress IP (``timezone="auto"``).
|
||||
|
||||
Approach B: discover the egress IP with one HTTP request — routed *through the
|
||||
proxy* when one is set, otherwise a direct request that sees the host's own
|
||||
public IP — then map IP → IANA timezone with an offline mmdb
|
||||
(``daijro/geoip-all-in-one``, downloaded + cached by ``download.py``).
|
||||
|
||||
Precedence (see ``resolve_session_timezone``):
|
||||
|
||||
explicit IANA → unchanged explicit always wins
|
||||
"" / "auto" → egress ALWAYS resolve. With a proxy, from the proxy
|
||||
egress IP; without a proxy, from the host's
|
||||
own public IP. This is the default.
|
||||
|
||||
On failure:
|
||||
with a proxy → raise a foreign proxy paired with the host TZ is
|
||||
the precise ``timezone_mismatch`` signal, so
|
||||
we fail loudly rather than fall back silently.
|
||||
without a proxy → "" (host) the host TZ is a safe default, so a transient
|
||||
lookup failure must not break the launch.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import ipaddress
|
||||
from typing import Any, Dict, NamedTuple, Optional
|
||||
from urllib.parse import quote
|
||||
|
||||
import requests
|
||||
|
||||
|
||||
class GeoTimezoneError(RuntimeError):
|
||||
"""Raised when ``timezone="auto"`` cannot resolve a valid IANA zone."""
|
||||
|
||||
|
||||
# Plain-text IP echo endpoints (each returns just the caller's public IP).
|
||||
_IP_ECHO_ENDPOINTS = (
|
||||
"https://api.ipify.org",
|
||||
"https://icanhazip.com",
|
||||
"https://checkip.amazonaws.com",
|
||||
)
|
||||
|
||||
_SOCKS_SCHEMES = ("socks5://", "socks4://", "socks://")
|
||||
|
||||
|
||||
def _proxy_is_set(proxy: Optional[Dict[str, str]]) -> bool:
|
||||
if not proxy:
|
||||
return False
|
||||
server = (proxy.get("server") or "").strip()
|
||||
return bool(server) and server.lower() != "direct://"
|
||||
|
||||
|
||||
def _proxies_for_requests(proxy: Dict[str, str]) -> Dict[str, str]:
|
||||
"""Translate our proxy dict into a ``requests`` proxies mapping.
|
||||
|
||||
SOCKS5 uses the ``socks5h`` scheme so DNS is resolved proxy-side (matches
|
||||
``network.proxy.socks_remote_dns=True`` in the Firefox path). HTTP/HTTPS
|
||||
pass through unchanged. Credentials are URL-encoded.
|
||||
"""
|
||||
server = (proxy.get("server") or "").strip()
|
||||
low = server.lower()
|
||||
if low.startswith("socks5://") or low.startswith("socks://"):
|
||||
scheme = "socks5h"
|
||||
elif low.startswith("socks4://"):
|
||||
scheme = "socks4"
|
||||
elif low.startswith("https://"):
|
||||
scheme = "https"
|
||||
else:
|
||||
scheme = "http"
|
||||
|
||||
host_port = server.split("://", 1)[1] if "://" in server else server
|
||||
user = proxy.get("username") or ""
|
||||
pwd = proxy.get("password") or ""
|
||||
if user:
|
||||
auth = f"{quote(user, safe='')}:{quote(pwd, safe='')}@"
|
||||
else:
|
||||
auth = ""
|
||||
url = f"{scheme}://{auth}{host_port}"
|
||||
return {"http": url, "https": url}
|
||||
|
||||
|
||||
def discover_egress_ip(
|
||||
proxy: Optional[Dict[str, str]] = None, *, timeout: float = 10.0
|
||||
) -> str:
|
||||
"""Return the public egress IP.
|
||||
|
||||
Routes the request through ``proxy`` when given (SOCKS support requires
|
||||
``requests[socks]`` / PySocks); with ``proxy=None`` it makes a direct
|
||||
request that sees the host's own public IP. Tries each echo endpoint in
|
||||
turn; raises :class:`GeoTimezoneError` if none return a valid IP.
|
||||
"""
|
||||
proxies = _proxies_for_requests(proxy) if proxy else None
|
||||
last_err: Optional[Exception] = None
|
||||
for url in _IP_ECHO_ENDPOINTS:
|
||||
try:
|
||||
resp = requests.get(url, proxies=proxies, timeout=timeout)
|
||||
resp.raise_for_status()
|
||||
ip = resp.text.strip()
|
||||
ipaddress.ip_address(ip) # validate (raises ValueError if not an IP)
|
||||
return ip
|
||||
except Exception as exc: # noqa: BLE001 - try the next endpoint
|
||||
last_err = exc
|
||||
continue
|
||||
raise GeoTimezoneError(
|
||||
f"could not discover the proxy egress IP via {len(_IP_ECHO_ENDPOINTS)} "
|
||||
f"endpoints (last error: {last_err!r}). For SOCKS proxies make sure "
|
||||
f"requests[socks] / PySocks is installed."
|
||||
)
|
||||
|
||||
|
||||
def ip_to_timezone(ip: str, mmdb_path: Any) -> str:
|
||||
"""Map ``ip`` to its IANA timezone using the offline mmdb.
|
||||
|
||||
Reads the standard MaxMind ``location.time_zone`` field and validates it
|
||||
against the system tz database. Raises :class:`GeoTimezoneError` if the IP
|
||||
is absent from the DB or the zone is missing / not a valid IANA name.
|
||||
"""
|
||||
import maxminddb
|
||||
|
||||
with maxminddb.open_database(str(mmdb_path)) as reader:
|
||||
record = reader.get(ip)
|
||||
if not record:
|
||||
raise GeoTimezoneError(f"egress IP {ip} not present in the geoip database")
|
||||
tz = ((record.get("location") or {}) if isinstance(record, dict) else {}).get(
|
||||
"time_zone"
|
||||
)
|
||||
if not tz:
|
||||
raise GeoTimezoneError(f"no timezone for egress IP {ip} in the geoip database")
|
||||
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
|
||||
|
||||
try:
|
||||
ZoneInfo(tz)
|
||||
except (ZoneInfoNotFoundError, ValueError) as exc:
|
||||
raise GeoTimezoneError(
|
||||
f"geoip returned an invalid IANA zone {tz!r} for {ip}: {exc}"
|
||||
) from exc
|
||||
return tz
|
||||
|
||||
|
||||
# ISO 3166 country code -> the primary BCP-47 locale a real Windows machine in that
|
||||
# country most commonly runs. Multi-language countries use the majority language; the
|
||||
# user can always force a specific locale instead of "auto". Unknown -> en-US.
|
||||
_COUNTRY_LOCALE = {
|
||||
"US": "en-US", "GB": "en-GB", "CA": "en-CA", "AU": "en-AU", "NZ": "en-NZ", "IE": "en-IE",
|
||||
"ZA": "en-ZA", "IN": "en-IN", "SG": "en-SG", "PH": "en-PH",
|
||||
"FR": "fr-FR", "BE": "fr-BE", "LU": "fr-LU",
|
||||
"DE": "de-DE", "AT": "de-AT", "CH": "de-CH",
|
||||
"IT": "it-IT", "ES": "es-ES", "PT": "pt-PT", "NL": "nl-NL",
|
||||
"SE": "sv-SE", "NO": "nb-NO", "DK": "da-DK", "FI": "fi-FI", "IS": "is-IS",
|
||||
"PL": "pl-PL", "CZ": "cs-CZ", "SK": "sk-SK", "HU": "hu-HU", "RO": "ro-RO",
|
||||
"GR": "el-GR", "BG": "bg-BG", "HR": "hr-HR", "RS": "sr-RS", "SI": "sl-SI",
|
||||
"RU": "ru-RU", "UA": "uk-UA", "TR": "tr-TR", "IL": "he-IL",
|
||||
"BR": "pt-BR", "MX": "es-MX", "AR": "es-AR", "CL": "es-CL", "CO": "es-CO", "PE": "es-PE",
|
||||
"JP": "ja-JP", "KR": "ko-KR", "CN": "zh-CN", "TW": "zh-TW", "HK": "zh-HK",
|
||||
"ID": "id-ID", "TH": "th-TH", "VN": "vi-VN", "MY": "ms-MY",
|
||||
"SA": "ar-SA", "AE": "ar-AE", "EG": "ar-EG",
|
||||
}
|
||||
|
||||
|
||||
def ip_to_locale(ip: str, mmdb_path: Any) -> str:
|
||||
"""Map ``ip`` -> a BCP-47 locale via the MaxMind ``country.iso_code`` field, so the
|
||||
browser language stays consistent with the proxy egress country. Falls back to
|
||||
``en-US`` for IPs absent from the DB or countries we don't map."""
|
||||
import maxminddb
|
||||
|
||||
with maxminddb.open_database(str(mmdb_path)) as reader:
|
||||
record = reader.get(ip)
|
||||
cc = ""
|
||||
if isinstance(record, dict):
|
||||
cc = ((record.get("country") or {}).get("iso_code") or "")
|
||||
return _COUNTRY_LOCALE.get(cc.upper(), "en-US")
|
||||
|
||||
|
||||
def resolve_session_locale(egress_ip: Optional[str], proxy: Optional[Dict[str, str]]) -> str:
|
||||
"""Resolve ``locale="auto"`` to a BCP-47 locale from the egress country. Behind a proxy
|
||||
it reuses the already-discovered ``egress_ip`` (no extra round-trip); without a proxy it
|
||||
discovers the host's public IP. On any failure it returns ``en-US`` (never breaks launch
|
||||
— locale is cosmetic, unlike timezone which traps a foreign-proxy mismatch)."""
|
||||
from .download import ensure_geoip_mmdb
|
||||
|
||||
try:
|
||||
ip = egress_ip if _proxy_is_set(proxy) else discover_egress_ip(None)
|
||||
if ip is None:
|
||||
return "en-US"
|
||||
return ip_to_locale(ip, ensure_geoip_mmdb())
|
||||
except Exception: # noqa: BLE001
|
||||
return "en-US"
|
||||
|
||||
|
||||
class SessionGeo(NamedTuple):
|
||||
"""Geo facts resolved once per session from a single egress round-trip.
|
||||
|
||||
``timezone`` follows the precedence in the module docstring.
|
||||
``egress_ip`` is the proxy egress IP (the IP the *outside world* sees) when
|
||||
a proxy is set, else ``None`` — it feeds the WebRTC srflx override, which is
|
||||
only meaningful behind a proxy (a direct connection's real STUN already
|
||||
reports the truthful public IP, so we leave it alone).
|
||||
"""
|
||||
|
||||
timezone: str
|
||||
egress_ip: Optional[str]
|
||||
|
||||
|
||||
def prepare_session_geo(
|
||||
timezone: str, proxy: Optional[Dict[str, str]]
|
||||
) -> SessionGeo:
|
||||
"""Resolve the session timezone AND the proxy egress IP in ONE round-trip.
|
||||
|
||||
The egress IP is discovered once and reused for both the timezone mapping
|
||||
(when ``timezone`` is ``""``/``"auto"``) and the WebRTC public-IP override.
|
||||
Timezone precedence is identical to :func:`resolve_session_timezone`; the
|
||||
egress IP is best-effort for the WebRTC side (a discovery failure that the
|
||||
timezone path doesn't need won't break the launch — but if the timezone
|
||||
path *does* need it behind a proxy, that path still fails loudly).
|
||||
"""
|
||||
from .download import ensure_geoip_mmdb
|
||||
|
||||
tz = (timezone or "").strip()
|
||||
proxy_set = _proxy_is_set(proxy)
|
||||
|
||||
# One discovery, reused below. Behind a proxy we always want the egress IP
|
||||
# (for WebRTC) regardless of the timezone setting.
|
||||
egress_ip: Optional[str] = None
|
||||
egress_err: Optional[Exception] = None
|
||||
if proxy_set:
|
||||
try:
|
||||
egress_ip = discover_egress_ip(proxy)
|
||||
except Exception as exc: # noqa: BLE001
|
||||
egress_err = exc
|
||||
|
||||
# Timezone resolution — same precedence as resolve_session_timezone.
|
||||
if tz and tz.lower() != "auto":
|
||||
return SessionGeo(tz, egress_ip) # explicit IANA wins
|
||||
try:
|
||||
ip = egress_ip if proxy_set else discover_egress_ip(None)
|
||||
if ip is None: # proxy set but discovery failed above
|
||||
raise egress_err or GeoTimezoneError("egress IP discovery failed")
|
||||
return SessionGeo(ip_to_timezone(ip, ensure_geoip_mmdb()), egress_ip)
|
||||
except Exception:
|
||||
if proxy_set:
|
||||
raise # fail-early behind a proxy (timezone_mismatch trap)
|
||||
return SessionGeo("", None) # no proxy: host TZ is a safe fallback
|
||||
|
||||
|
||||
def resolve_session_timezone(
|
||||
timezone: str, proxy: Optional[Dict[str, str]]
|
||||
) -> str:
|
||||
"""Map the user's ``timezone`` setting to a concrete IANA zone (or ``""``).
|
||||
|
||||
Timezone-only path (no WebRTC side effects): an explicit IANA zone wins and
|
||||
triggers NO network call; ``""``/``"auto"`` resolve from the egress IP. The
|
||||
launch path uses :func:`prepare_session_geo` instead (which additionally
|
||||
returns the egress IP for WebRTC); this standalone resolver is kept for
|
||||
third-party integrations that only want the zone. See the module docstring
|
||||
for the precedence table.
|
||||
"""
|
||||
tz = (timezone or "").strip()
|
||||
if tz and tz.lower() != "auto":
|
||||
return tz # explicit IANA wins — no egress lookup
|
||||
from .download import ensure_geoip_mmdb
|
||||
|
||||
proxy_set = _proxy_is_set(proxy)
|
||||
try:
|
||||
ip = discover_egress_ip(proxy if proxy_set else None)
|
||||
return ip_to_timezone(ip, ensure_geoip_mmdb())
|
||||
except Exception:
|
||||
if proxy_set:
|
||||
raise # fail-early behind a proxy (timezone_mismatch trap)
|
||||
return "" # no proxy: host TZ is a safe fallback
|
||||
"""Backward-compat shim — spostato in invisible_core._geo (alias completo)."""
|
||||
import sys as _sys
|
||||
from invisible_core import _geo as _mod
|
||||
_sys.modules[__name__] = _mod
|
||||
|
|
|
|||
|
|
@ -1,178 +1,4 @@
|
|||
"""Invisible-but-headed browser windows.
|
||||
|
||||
Playwright's ``headless=True`` flips Firefox onto a different code path —
|
||||
no widget tree, software-only rendering, distinct timing — and anti-bot
|
||||
systems can spot the divergence. Running the browser *headed* but hidden
|
||||
gives us the real rendering pipeline while keeping the windows off screen.
|
||||
|
||||
Two mechanisms, by platform:
|
||||
|
||||
- **Windows & macOS**: the patched binary cloaks its OWN chrome windows
|
||||
when ``zoom.stealth.cloak_windows`` is set — ``DWMWA_CLOAK`` (Windows)
|
||||
/ ``NSWindow`` alpha-0 + pinned occlusion-ignore (macOS). The window
|
||||
renders on the real GPU but never appears on screen, in the taskbar or
|
||||
the Dock. The launcher injects the pref; nothing host-side is spawned.
|
||||
|
||||
- **Linux**: spawns its own ``Xvfb`` instance and points ``DISPLAY`` at
|
||||
it (X11/Wayland have no per-window cloak that keeps the GPU rendering).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from typing import Optional
|
||||
|
||||
|
||||
# Inherited from WSLg / GNOME / etc. these env vars make Firefox prefer a
|
||||
# Wayland compositor over the X11 DISPLAY we set, so the window leaks onto
|
||||
# the real desktop. Strip them all before starting.
|
||||
_WAYLAND_LEAK_VARS = (
|
||||
"WAYLAND_DISPLAY",
|
||||
"XDG_RUNTIME_DIR",
|
||||
"XDG_SESSION_TYPE",
|
||||
"PULSE_SERVER",
|
||||
"WSL2_GUI_APPS_ENABLED",
|
||||
)
|
||||
|
||||
|
||||
class _LinuxVirtualDisplay:
|
||||
"""Standalone Xvfb instance owned by this InvisiblePlaywright session."""
|
||||
|
||||
def __init__(self, width: int = 1920, height: int = 1080) -> None:
|
||||
self._geometry = f"{width}x{height}x24"
|
||||
self._proc: Optional[subprocess.Popen] = None
|
||||
self._display: Optional[str] = None
|
||||
self._saved_env: dict[str, Optional[str]] = {}
|
||||
|
||||
def start(self) -> None:
|
||||
if not _binary_on_path("Xvfb"):
|
||||
raise RuntimeError(
|
||||
"invisible_playwright headless=True requires Xvfb. "
|
||||
"Install it: sudo apt install xvfb"
|
||||
)
|
||||
# Retry: when many workers start in parallel they can pick the same
|
||||
# display number before any has created its lockfile. Xvfb on the
|
||||
# losing side exits immediately — try again with a fresh number.
|
||||
last_err: Optional[Exception] = None
|
||||
for _ in range(10):
|
||||
display = self._pick_display()
|
||||
try:
|
||||
self._spawn(display)
|
||||
self._wait_until_ready(display)
|
||||
self._display = display
|
||||
self._apply_env(display)
|
||||
return
|
||||
except RuntimeError as e:
|
||||
last_err = e
|
||||
if self._proc is not None and self._proc.poll() is None:
|
||||
self._proc.kill()
|
||||
self._proc = None
|
||||
raise RuntimeError(f"Xvfb failed to start after 10 attempts: {last_err}")
|
||||
|
||||
def _spawn(self, display: str) -> None:
|
||||
self._proc = subprocess.Popen(
|
||||
[
|
||||
"Xvfb", display,
|
||||
"-screen", "0", self._geometry,
|
||||
"+extension", "GLX",
|
||||
"+extension", "RENDER",
|
||||
"-nolisten", "unix",
|
||||
"-listen", "tcp",
|
||||
"-ac",
|
||||
],
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
start_new_session=True,
|
||||
)
|
||||
|
||||
def _pick_display(self) -> str:
|
||||
for n in range(99, 400):
|
||||
if not os.path.exists(f"/tmp/.X{n}-lock"):
|
||||
return f":{n}"
|
||||
raise RuntimeError("no free X display number in :99–:399")
|
||||
|
||||
def _wait_until_ready(self, display: str) -> None:
|
||||
# We start Xvfb with -nolisten unix → no /tmp/.X11-unix socket appears.
|
||||
# Xvfb creates /tmp/.X{n}-lock immediately though — wait for that.
|
||||
lockfile = f"/tmp/.X{display[1:]}-lock"
|
||||
deadline = time.monotonic() + 3.0
|
||||
assert self._proc is not None
|
||||
while time.monotonic() < deadline:
|
||||
if self._proc.poll() is not None:
|
||||
raise RuntimeError(f"Xvfb {display} exited immediately")
|
||||
if os.path.exists(lockfile):
|
||||
return
|
||||
time.sleep(0.02)
|
||||
raise RuntimeError(f"Xvfb {display} did not become ready in 3s")
|
||||
|
||||
def _apply_env(self, display: str) -> None:
|
||||
keys = ("DISPLAY", "MOZ_ENABLE_WAYLAND", "GDK_BACKEND") + _WAYLAND_LEAK_VARS
|
||||
for k in keys:
|
||||
self._saved_env[k] = os.environ.get(k)
|
||||
for k in _WAYLAND_LEAK_VARS:
|
||||
os.environ.pop(k, None)
|
||||
os.environ["DISPLAY"] = display
|
||||
os.environ["MOZ_ENABLE_WAYLAND"] = "0"
|
||||
os.environ["GDK_BACKEND"] = "x11"
|
||||
|
||||
def stop(self) -> None:
|
||||
for k, v in self._saved_env.items():
|
||||
if v is None:
|
||||
os.environ.pop(k, None)
|
||||
else:
|
||||
os.environ[k] = v
|
||||
self._saved_env.clear()
|
||||
|
||||
if self._proc is not None and self._proc.poll() is None:
|
||||
self._proc.terminate()
|
||||
try:
|
||||
self._proc.wait(timeout=3)
|
||||
except subprocess.TimeoutExpired:
|
||||
self._proc.kill()
|
||||
self._proc.wait(timeout=2)
|
||||
self._proc = None
|
||||
self._display = None
|
||||
|
||||
|
||||
# Windows & macOS: the patched Firefox cloaks its own chrome windows when this
|
||||
# pref is set (DWMWA_CLOAK / NSWindow alpha-0 + pinned occlusion-ignore), so the
|
||||
# window renders on the real GPU but never shows on screen / in the taskbar or
|
||||
# Dock. window_occlusion_tracking is disabled so a hidden window keeps painting.
|
||||
CLOAK_PREFS = {
|
||||
"zoom.stealth.cloak_windows": True,
|
||||
"widget.windows.window_occlusion_tracking.enabled": False,
|
||||
}
|
||||
|
||||
|
||||
def cloak_prefs() -> dict:
|
||||
"""Prefs that make the patched binary self-cloak its chrome windows.
|
||||
|
||||
Used on Windows & macOS, where hiding is done inside the binary rather than
|
||||
with a host-side virtual display.
|
||||
"""
|
||||
return dict(CLOAK_PREFS)
|
||||
|
||||
|
||||
def make_virtual_display():
|
||||
"""Return a start()/stop()-able virtual display, or ``None`` when the
|
||||
platform hides windows via the in-binary cloak pref instead.
|
||||
|
||||
- Linux: a fresh ``Xvfb`` (the launcher start()s/stop()s it).
|
||||
- Windows / macOS: ``None`` — the binary self-cloaks via ``cloak_prefs()``,
|
||||
injected by the launcher; nothing host-side needs spawning.
|
||||
"""
|
||||
if sys.platform.startswith("linux"):
|
||||
return _LinuxVirtualDisplay()
|
||||
if sys.platform in ("win32", "darwin"):
|
||||
return None
|
||||
raise RuntimeError(
|
||||
f"invisible_playwright supports Windows, macOS and Linux "
|
||||
f"(got {sys.platform!r})"
|
||||
)
|
||||
|
||||
|
||||
def _binary_on_path(name: str) -> bool:
|
||||
import shutil
|
||||
return shutil.which(name) is not None
|
||||
"""Backward-compat shim — spostato in invisible_core._headless (alias completo)."""
|
||||
import sys as _sys
|
||||
from invisible_core import _headless as _mod
|
||||
_sys.modules[__name__] = _mod
|
||||
|
|
|
|||
|
|
@ -1,56 +1,4 @@
|
|||
"""Proxy translation shared by sync and async launchers.
|
||||
|
||||
SOCKS proxies are driven entirely by the patched Firefox prefs (the
|
||||
``nsProtocolProxyService`` patch reads ``network.proxy.socks_username``
|
||||
and ``socks_password``). HTTP/HTTPS proxies go through Playwright's own
|
||||
``proxy=`` kwarg so it can negotiate Basic auth.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
|
||||
_SOCKS_SCHEMES = ("socks5://", "socks4://", "socks://")
|
||||
|
||||
|
||||
def configure_proxy(
|
||||
proxy: Optional[Dict[str, str]],
|
||||
prefs: Dict[str, Any],
|
||||
) -> Optional[Dict[str, str]]:
|
||||
"""Mutate ``prefs`` for SOCKS auth; return what to pass to Playwright.
|
||||
|
||||
* ``None`` proxy → returns ``None``.
|
||||
* SOCKS proxy → writes the auth prefs and returns ``None`` (Playwright
|
||||
gets nothing; Firefox does the rest).
|
||||
* HTTP / HTTPS proxy → returns the dict unchanged for Playwright.
|
||||
"""
|
||||
if not proxy:
|
||||
return None
|
||||
|
||||
server = (proxy.get("server") or "").strip()
|
||||
if not server or server.lower() == "direct://":
|
||||
return None
|
||||
if not _is_socks_scheme(server):
|
||||
return proxy
|
||||
|
||||
host_port = _strip_scheme(server)
|
||||
if ":" not in host_port:
|
||||
return None # malformed, drop silently
|
||||
|
||||
host, port_str = host_port.rsplit(":", 1)
|
||||
prefs["network.proxy.type"] = 1
|
||||
prefs["network.proxy.socks"] = host
|
||||
prefs["network.proxy.socks_port"] = int(port_str)
|
||||
prefs["network.proxy.socks_version"] = 4 if server.lower().startswith("socks4://") else 5
|
||||
prefs["network.proxy.socks_username"] = proxy.get("username") or ""
|
||||
prefs["network.proxy.socks_password"] = proxy.get("password") or ""
|
||||
prefs["network.proxy.socks_remote_dns"] = True
|
||||
return None
|
||||
|
||||
|
||||
def _is_socks_scheme(server: str) -> bool:
|
||||
return server.lower().startswith(_SOCKS_SCHEMES)
|
||||
|
||||
|
||||
def _strip_scheme(server: str) -> str:
|
||||
return server.split("://", 1)[1] if "://" in server else server
|
||||
"""Backward-compat shim — spostato in invisible_core._proxy (alias completo)."""
|
||||
import sys as _sys
|
||||
from invisible_core import _proxy as _mod
|
||||
_sys.modules[__name__] = _mod
|
||||
|
|
|
|||
|
|
@ -1,202 +1,4 @@
|
|||
"""Empirically-calibrated WebGL GPU personas for Windows ANGLE D3D11.
|
||||
|
||||
We expose a FALSE GPU (this is a multi-user tool — never leak each host's real GPU),
|
||||
chosen deterministically per seed from a small set of renderer-string "buckets" that
|
||||
Firefox's SanitizeRenderer emits and that FP Pro's tampering_ml scores as CLEAN.
|
||||
|
||||
## What actually gates a persona (calibrated 2026-06-14, supersedes the old theory)
|
||||
|
||||
The blocker is NOT anti_detect and NOT a "render-vs-renderer" check. It is FP Pro's
|
||||
**tampering_ml** (gate <=0.5), a holistic ML coherence score. We reverse-engineered its
|
||||
GPU sensitivity with single-variable A/Bs on demo.fingerprint.com (deterministic per
|
||||
(seed, renderer, IP); tools in tests/_gpu_isolate.py / _gpu_landscape.py / _gpu_sweep.py /
|
||||
_gpu_sweep2.py / _gpu_persona_pure.py). Findings:
|
||||
|
||||
1. tampering_ml = f(renderer STRING, seed baseline = canvas/audio). The renderer string
|
||||
carries a STABLE per-bucket penalty; the seed sets the floor it adds to.
|
||||
2. gpu_class is IRRELEVANT to tampering_ml (nv_980 scored identically on mid_range /
|
||||
high_end / premium / workstation). So pairing a fake GPU with a "matching" hardware
|
||||
tier does NOT help the score (we still set a coherent class — see gpu_class below —
|
||||
for OTHER detectors that cross-check cores/screen, just not for this).
|
||||
3. It is NOT render-consistency: a cross-vendor AMD string is CLEAN on our Intel-Arc
|
||||
host. So the real silicon's pixels are not the dominant signal; falsifying to a
|
||||
different vendor works — IF the string is one FP Pro scores low.
|
||||
|
||||
Sweep over all 10 Windows SanitizeRenderer buckets x 10 seeds (clean = tml<=0.5 AND not
|
||||
anti_detect), on our Intel Arc A750 host:
|
||||
- amd_r9 (Radeon R9 200 Series) ...... 10/10 clean, max tml 0.346 <- SHIP
|
||||
- intel_arc (Arc A750) ............... 10/10 clean, max tml 0.377 <- SHIP
|
||||
- amd_hd5850 ......................... 9/10 (fails the hardest seed)
|
||||
- amd_hd3200 / intel_hd .............. 6/10 (seed-dependent, risky)
|
||||
- intel_hd400 ........................ 3/10
|
||||
- ALL NVIDIA (8800/480/980) .......... 0/10 (penalized everywhere, ~0.7-0.99)
|
||||
- intel_945 (ancient Intel) .......... 0/10
|
||||
So only TWO buckets are robustly clean across profiles. We ship exactly those, weighted
|
||||
to real-world prevalence ("Radeon R9 200 Series" is the bucket for ALL modern AMD = a big
|
||||
real slice; "Arc A750" covers Intel discrete = rarer). Cross-vendor, so the fleet is not a
|
||||
single-GPU cluster. More names require lowering the seed floor first (see CAVEAT 2).
|
||||
|
||||
## ⚠️ CAVEATS
|
||||
1. HOST-INDEPENDENCE NOT PROVEN. Everything above was measured on ONE host (Intel Arc
|
||||
A750). The host's real render is embedded in the seed baseline, so the clean-bucket set
|
||||
*might* be host-dependent (on a real NVIDIA host, maybe nv_980 is clean and amd_r9 is
|
||||
not). This MUST be validated on a non-Arc machine before trusting it fleet-wide; if it
|
||||
turns out host-dependent, add a pre-launch host-GPU-class probe and pick a bucket per
|
||||
detected class. Until then: safe for Arc hosts (incl. the dev's), unvalidated elsewhere.
|
||||
2. DIVERSITY CEILING = 2 names because "hard" seeds (high canvas/audio floor, e.g. seed 4
|
||||
~0.35) only stay clean on the 2 best buckets. Lowering that floor (an fpforge CPT fix —
|
||||
candidate: 8-channel audio + 1TB storage emitted on a mid_range profile) would unlock
|
||||
amd_hd5850 / intel_hd for more seeds => up to ~5 names. Follow-up, not done yet.
|
||||
|
||||
## Load-bearing format requirements (unchanged, still true)
|
||||
- renderer MUST end ", D3D11)" (full ANGLE wire format) or SanitizeRenderer returns
|
||||
"Generic Renderer" (a tell). The C++ passes our string through SanitizeRenderer, which
|
||||
buckets "AMD Radeon R9 200 Series" -> "Radeon R9 200 Series" and "Arc A750" -> itself.
|
||||
- the forced extension list MUST be the EXACT NATIVE ORDER getSupportedExtensions returns.
|
||||
The set+order is fixed by Firefox+ANGLE on D3D11 FL11_0 (VENDOR-INDEPENDENT — verified
|
||||
via 20-agent source study), so ONE list is correct for both personas. A reorder is caught
|
||||
(tampering_ml 0.34 -> 0.84). The lists below are the verbatim native-order Arc capture.
|
||||
|
||||
Calibration data + sweep tooling live in the local workbench (not shipped).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
# Vendor-independent ext lists (native order, Arc host capture). Identical for every persona
|
||||
# because the set+order is fixed by Firefox+ANGLE on D3D11 FL11_0, not by the GPU vendor.
|
||||
_EXT1 = (
|
||||
"ANGLE_instanced_arrays,EXT_blend_minmax,EXT_color_buffer_half_float,EXT_float_blend,"
|
||||
"EXT_frag_depth,EXT_shader_texture_lod,EXT_sRGB,EXT_texture_compression_bptc,"
|
||||
"EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_element_index_uint,"
|
||||
"OES_fbo_render_mipmap,OES_standard_derivatives,OES_texture_float,OES_texture_float_linear,"
|
||||
"OES_texture_half_float,OES_texture_half_float_linear,OES_vertex_array_object,"
|
||||
"WEBGL_color_buffer_float,WEBGL_compressed_texture_s3tc,WEBGL_compressed_texture_s3tc_srgb,"
|
||||
"WEBGL_debug_renderer_info,WEBGL_debug_shaders,WEBGL_depth_texture,WEBGL_draw_buffers,"
|
||||
"WEBGL_lose_context,WEBGL_provoking_vertex"
|
||||
)
|
||||
_EXT2 = (
|
||||
"EXT_color_buffer_float,EXT_float_blend,EXT_texture_compression_bptc,"
|
||||
"EXT_texture_compression_rgtc,EXT_texture_filter_anisotropic,OES_draw_buffers_indexed,"
|
||||
"OES_texture_float_linear,OVR_multiview2,WEBGL_compressed_texture_s3tc,"
|
||||
"WEBGL_compressed_texture_s3tc_srgb,WEBGL_debug_renderer_info,WEBGL_debug_shaders,"
|
||||
"WEBGL_lose_context,WEBGL_provoking_vertex"
|
||||
)
|
||||
|
||||
|
||||
# ── Real-Firefox GPU pool (2026-06-18, supersedes the 2-bucket sweep above) ───────────────
|
||||
# The personas are now sourced from `_fpforge/data/webgl_gpu_pool.json` — an OFFLINE extract
|
||||
# of camoufox's real-Firefox WebGL telemetry DB (17 Windows GPUs with their REAL per-OS
|
||||
# prevalence AND the full coherent WebGL fingerprint: renderer + vendor + extensions +
|
||||
# ~100 getParameter values + shader-precision formats). prefs.py applies ALL of these, not
|
||||
# just the renderer string. The linchpin A/B (2026-06-18) proved that the OLD "NVIDIA 0/10"
|
||||
# verdict was an artifact of spoofing the renderer string over the host's REAL (Arc) params:
|
||||
# FP Pro cross-checks renderer<->params, so a GTX 980 string over Arc params mismatched
|
||||
# (~0.7-0.85). Injecting camoufox's REAL GTX 980 params makes it coherent (tml median 0.333,
|
||||
# flags clean). So the params are NOT vendor-independent (the old assumption) and per-GPU
|
||||
# real data is what unlocks the full real GPU mix — including NVIDIA (~47% of real FF-Win),
|
||||
# which we no longer gate.
|
||||
_ENABLED = True
|
||||
_POOL_PATH = __import__("pathlib").Path(__file__).parent / "_fpforge" / "data" / "webgl_gpu_pool.json"
|
||||
_GPU_POOL_CACHE: Optional[List[Dict]] = None
|
||||
|
||||
|
||||
def _gpu_pool() -> List[Dict]:
|
||||
"""Lazy-load the Windows GPU pool (we always claim Windows). Each entry:
|
||||
{key, renderer (input form), vendor, gpu_class (via classify_gpu), prefs (full
|
||||
zoom.stealth.webgl.* override dict), weight (real per-OS prevalence)}."""
|
||||
global _GPU_POOL_CACHE
|
||||
if _GPU_POOL_CACHE is not None:
|
||||
return _GPU_POOL_CACHE
|
||||
import json
|
||||
from ._fpforge._sampler import classify_gpu # lazy import → no module cycle
|
||||
raw = json.loads(_POOL_PATH.read_text(encoding="utf-8"))
|
||||
pool: List[Dict] = []
|
||||
for e in raw.get("win", []):
|
||||
prefs = e["prefs"]
|
||||
rend_in = prefs["zoom.stealth.webgl.renderer"]
|
||||
cls = classify_gpu({"renderer": rend_in,
|
||||
"vendor": prefs.get("zoom.stealth.webgl.vendor", "")})
|
||||
pool.append({
|
||||
"key": e.get("renderer_out", rend_in)[:48],
|
||||
"renderer": rend_in,
|
||||
"vendor": prefs["zoom.stealth.webgl.vendor"],
|
||||
"gpu_class": cls,
|
||||
"prefs": prefs,
|
||||
"weight": float(e["prob"]),
|
||||
})
|
||||
_GPU_POOL_CACHE = pool
|
||||
return pool
|
||||
|
||||
|
||||
def select_persona(seed: int) -> Optional[Dict]:
|
||||
"""Deterministic, prevalence-weighted GPU persona for this seed — on EVERY host.
|
||||
|
||||
Same seed -> same persona (fppro_consistency: identity stable per seed). Different seeds
|
||||
spread across the REAL Windows GPU mix by prevalence. Returns the Windows-ANGLE persona on
|
||||
Linux/Mac too: we must always look Windows, and the C++ WebGL override (SanitizeRenderer +
|
||||
pref-driven params/extensions) is platform-independent, so the same Windows GPU is presented
|
||||
on any host without consulting the real GL backend (no more Linux "Generic Renderer")."""
|
||||
if not _ENABLED:
|
||||
return None
|
||||
pool = _gpu_pool()
|
||||
if not pool:
|
||||
return None
|
||||
total = sum(p["weight"] for p in pool) or 1.0
|
||||
h = ((int(seed) * 2654435761) % 1_000_003) / 1_000_003.0 * total
|
||||
cum = 0.0
|
||||
for p in pool:
|
||||
cum += p["weight"]
|
||||
if h < cum:
|
||||
return p
|
||||
return pool[-1]
|
||||
|
||||
|
||||
def forced_gpu_class(seed: int) -> Optional[str]:
|
||||
"""The gpu_class the forge conditions the bundle on (== the selected GPU's class via
|
||||
classify_gpu), so cores/screen/fonts stay coherent with the GPU we expose. Does NOT
|
||||
affect FP Pro tampering_ml (proven) but matters for detectors that cross-check hardware
|
||||
tier. None on Linux."""
|
||||
p = select_persona(seed)
|
||||
return p["gpu_class"] if p else None
|
||||
|
||||
|
||||
# ── Render-noise seed pool (canvas/WebGL gamma) ──────────────────────────────
|
||||
# zoom.stealth.fpp.hw_seed drives the per-seed canvas2D + WebGL readPixels gamma
|
||||
# LUT in C++. The render-image HASH it produces is the DOMINANT FP Pro tampering_ml
|
||||
# driver (proven 2026-06-14: holding a fixed profile and varying ONLY hw_seed moved
|
||||
# tml 0.25->0.75). The monotonic gamma preserves the GPU's render structure, so some
|
||||
# hw_seeds yield a "suspicious" render hash. We therefore DECOUPLE the render-noise
|
||||
# seed from the identity seed and pick from a calibrated pool of hw_seeds that score
|
||||
# CLEAN even on the hardest attribute profile (sweep 1..30 vs the worst seed: these
|
||||
# 14 all gave tml<=0.285). Diversity is preserved (14 distinct render hashes spread
|
||||
# across the population — real GPUs cluster to few canvas hashes anyway); identity
|
||||
# stays per-seed (the rest of the fingerprint differs). Same seed -> same render seed
|
||||
# (fppro_consistency holds).
|
||||
# CAVEAT: the render hash = f(host GPU render, gamma), so this pool is calibrated on
|
||||
# the Intel-Arc host. On other GPUs the clean set may differ (host-independence open,
|
||||
# same as the personas) — Option B (substitution = GPU-independent render hash) would
|
||||
# remove that dependency. Validate per-host or move to B before trusting fleet-wide.
|
||||
# RECALIBRATED 2026-06-18 for the real-Firefox GPU mix (incl NVIDIA, which is more
|
||||
# consistency-sensitive than the old amd/arc personas). Swept hw_seed 0..30 on the hottest
|
||||
# persona (NVIDIA GTX 980) through a residential exit: these 9 stayed well within the clean
|
||||
# band with a wide margin to the rest. The old pool's picks scored dirty on NVIDIA (clean
|
||||
# only on the retired amd/arc mix) → dropped. NVIDIA is the worst case, so these are clean on
|
||||
# amd/intel too. hw_seed = the canvas/WebGL gamma render hash (the dominant consistency-score
|
||||
# driver); host-calibrated.
|
||||
# 2026-06-21: with WebGL Option B (zoom.stealth.webgl.substitute_pixels, ON in prefs.py) the WebGL
|
||||
# render hash is hash(seed,idx) = HOST-INDEPENDENT, so this list NO LONGER needs per-host calibration
|
||||
# — it only supplies per-session diversity. A 2026-06-21 attempt to re-calibrate it per-host FAILED
|
||||
# cross-OS: hw_seed clean on Windows went dirty on the Linux GL backend (b008 0.034->0.839; Win-dirty
|
||||
# {7,11,20,27} = Linux-clean and vice-versa; + identity×hw_seed interaction on Linux). That proved
|
||||
# calibration can't work cross-host → substitution replaces it. Kept the original diverse 9-set.
|
||||
CLEAN_RENDER_SEEDS = [0, 5, 6, 9, 11, 16, 19, 20, 28]
|
||||
|
||||
|
||||
def render_noise_seed(seed: int) -> int:
|
||||
"""Deterministic clean render-noise seed for hw_seed (decoupled from identity).
|
||||
|
||||
Maps the identity seed into CLEAN_RENDER_SEEDS so every session gets a calibrated
|
||||
clean canvas/WebGL render hash while keeping per-user diversity. Stable per seed."""
|
||||
return CLEAN_RENDER_SEEDS[(int(seed) * 2654435761) % len(CLEAN_RENDER_SEEDS)]
|
||||
"""Backward-compat shim — spostato in invisible_core._webgl_personas (alias completo)."""
|
||||
import sys as _sys
|
||||
from invisible_core import _webgl_personas as _mod
|
||||
_sys.modules[__name__] = _mod
|
||||
|
|
|
|||
|
|
@ -1,112 +1,4 @@
|
|||
"""Public helpers for building Firefox launch config without using ``InvisiblePlaywright``.
|
||||
|
||||
Use these when you need to call ``playwright.firefox.launch()`` (or
|
||||
``firefox.launch_persistent_context()``) directly with our patched binary
|
||||
and stealth prefs, instead of using the ``InvisiblePlaywright`` context
|
||||
manager.
|
||||
|
||||
Typical caller is an external integration that owns its own browser
|
||||
lifecycle (a Crawlee/Skyvern/changedetection-style fetcher, a Playwright
|
||||
Server wrapper, a multi-language harness) and just wants the building
|
||||
blocks::
|
||||
|
||||
from playwright.async_api import async_playwright
|
||||
from invisible_playwright import ensure_binary, get_default_stealth_prefs
|
||||
|
||||
async with async_playwright() as p:
|
||||
browser = await p.firefox.launch(
|
||||
executable_path=str(ensure_binary()),
|
||||
firefox_user_prefs=get_default_stealth_prefs(seed=42),
|
||||
)
|
||||
|
||||
For everyday Python usage the ``InvisiblePlaywright`` context manager is
|
||||
still the recommended entry point; these helpers expose the same internals
|
||||
without the lifecycle ownership.
|
||||
|
||||
.. note::
|
||||
When calling ``firefox.launch()`` yourself, pass ``headless=False`` and
|
||||
manage the display hiding (Xvfb on Linux, hidden desktop on Windows)
|
||||
externally. Passing ``headless=True`` directly to Playwright puts
|
||||
Firefox in true headless mode, which skips the real rendering pipeline
|
||||
and breaks canvas / audio / WebGL fingerprint coherence. The
|
||||
``InvisiblePlaywright`` context manager does this translation
|
||||
automatically; the public helpers leave it to the caller.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import secrets
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
|
||||
from ._fpforge import generate_profile
|
||||
from ._webgl_personas import forced_gpu_class
|
||||
from .prefs import translate_profile_to_prefs
|
||||
|
||||
|
||||
def get_default_stealth_prefs(
|
||||
seed: Optional[int] = None,
|
||||
*,
|
||||
pin: Optional[Dict[str, Any]] = None,
|
||||
locale: str = "en-US",
|
||||
timezone: str = "",
|
||||
extra_prefs: Optional[Dict[str, Any]] = None,
|
||||
humanize: Union[bool, float] = True,
|
||||
virtual_display: bool = False,
|
||||
) -> Dict[str, Any]:
|
||||
"""Build a complete ``firefox_user_prefs`` dict for ``firefox.launch()``.
|
||||
|
||||
Same prefs that ``InvisiblePlaywright(seed=..., locale=..., timezone=...,
|
||||
extra_prefs=..., humanize=...)`` would inject. Use this when you need to
|
||||
drive ``playwright.firefox.launch()`` yourself.
|
||||
|
||||
Args:
|
||||
seed: Integer seed for the Bayesian fingerprint sampler. Same seed
|
||||
produces the same fingerprint. ``None`` generates a fresh
|
||||
random int31 (matches ``InvisiblePlaywright`` default).
|
||||
pin: Optional dict forcing specific fingerprint fields while the
|
||||
rest stays seed-derived. See ``docs/pinning.md``.
|
||||
locale: BCP-47 tag (e.g. ``"en-US"``). Drives ``Accept-Language``
|
||||
and ``navigator.language``.
|
||||
timezone: IANA timezone (e.g. ``"America/New_York"``). Empty means
|
||||
use the host TZ. This pure pref builder does NOT resolve
|
||||
``"auto"`` (that needs the proxy + a network lookup at launch
|
||||
time) — pass a concrete zone here, or use ``InvisiblePlaywright``
|
||||
/ ``resolve_session_timezone(timezone, proxy)`` for ``"auto"``.
|
||||
extra_prefs: Optional dict overlaid LAST onto the generated prefs.
|
||||
humanize: When True (default), every mouse move is expanded into
|
||||
a Bezier trajectory by the patched Juggler. A float caps the
|
||||
motion in seconds. False disables the behavior.
|
||||
virtual_display: When True on Windows, apply GPU-disabling prefs
|
||||
to prevent GPU process crashes on virtual desktops without
|
||||
D3D11 backend.
|
||||
|
||||
Returns:
|
||||
Dict ready to pass as ``firefox_user_prefs=`` to
|
||||
``playwright.firefox.launch()`` or ``launch_persistent_context()``.
|
||||
"""
|
||||
resolved_seed = int(seed) if seed is not None else secrets.randbits(31)
|
||||
profile = generate_profile(resolved_seed, pin=pin, fixed_gpu_class=forced_gpu_class(resolved_seed))
|
||||
prefs = translate_profile_to_prefs(
|
||||
profile,
|
||||
locale=locale,
|
||||
timezone=timezone,
|
||||
extra_prefs=extra_prefs,
|
||||
virtual_display=virtual_display,
|
||||
)
|
||||
# stealthfox.* is the namespace the binary's Juggler reads (see launcher.py note).
|
||||
prefs["stealthfox.humanize"] = bool(humanize)
|
||||
if humanize:
|
||||
max_seconds = float(humanize) if not isinstance(humanize, bool) else 1.5
|
||||
prefs["stealthfox.humanize.maxTime"] = str(max_seconds)
|
||||
return prefs
|
||||
|
||||
|
||||
def get_default_args() -> List[str]:
|
||||
"""Return the default Firefox CLI args to pass via ``args=``.
|
||||
|
||||
Currently empty list, since all our stealth configuration is delivered
|
||||
via ``firefox_user_prefs`` rather than CLI flags. Exposed for parity
|
||||
with the ``cloakbrowser.config.get_default_stealth_args`` pattern and
|
||||
to future-proof integrations that already wire ``args=[*existing,
|
||||
*get_default_args()]``.
|
||||
"""
|
||||
return []
|
||||
"""Backward-compat shim — spostato in invisible_core.config (alias completo)."""
|
||||
import sys as _sys
|
||||
from invisible_core import config as _mod
|
||||
_sys.modules[__name__] = _mod
|
||||
|
|
|
|||
|
|
@ -1,80 +1,4 @@
|
|||
"""Compile-time constants that pin the wrapper to a specific Firefox build.
|
||||
|
||||
BINARY_VERSION is bumped every time new Firefox patches are released. It is
|
||||
deliberately decoupled from the Python package version so that pure-Python
|
||||
bugfixes don't force a multi-hour Firefox rebuild.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
# Bump this when a new patched Firefox build is released on GitHub.
|
||||
BINARY_VERSION: str = "firefox-13"
|
||||
|
||||
# Releases known to be broken — ensure_binary() refuses them with a clear error
|
||||
# instead of handing the user an unusable binary. firefox-8 was packaged without
|
||||
# the juggler automation layer, so Playwright cannot drive it (TargetClosedError);
|
||||
# fixed in firefox-9 (package-manifest.in now ships chrome/juggler). A cached
|
||||
# firefox-8 from before the bump would otherwise keep being used silently.
|
||||
BROKEN_VERSIONS: frozenset[str] = frozenset({"firefox-8"})
|
||||
|
||||
# Underlying Firefox version (for display only; does not drive downloads).
|
||||
FIREFOX_UPSTREAM_VERSION: str = "150.0.1"
|
||||
|
||||
# The base filename prefix used inside archives.
|
||||
BINARY_BASENAME: str = f"firefox-{FIREFOX_UPSTREAM_VERSION}-stealth"
|
||||
|
||||
|
||||
def ARCHIVE_NAME(platform_key: str, machine: str) -> str:
|
||||
"""Return the platform-specific archive filename.
|
||||
|
||||
platform_key: sys.platform ("win32", "linux", "darwin")
|
||||
machine: platform.machine() ("AMD64", "x86_64", "arm64", "aarch64", ...)
|
||||
"""
|
||||
pk = platform_key.lower()
|
||||
m = machine.lower()
|
||||
if m in {"amd64", "x86_64"}:
|
||||
arch = "x86_64"
|
||||
elif m in {"arm64", "aarch64"}:
|
||||
arch = "arm64"
|
||||
else:
|
||||
raise NotImplementedError(f"unsupported arch: {machine}")
|
||||
|
||||
if pk == "win32":
|
||||
return f"{BINARY_BASENAME}-win-{arch}.zip"
|
||||
if pk == "linux":
|
||||
return f"{BINARY_BASENAME}-linux-{arch}.tar.gz"
|
||||
if pk == "darwin":
|
||||
return f"{BINARY_BASENAME}-macos-{arch}.tar.gz"
|
||||
raise NotImplementedError(f"unsupported platform: {platform_key}")
|
||||
|
||||
|
||||
# Binary entry point relative path inside the extracted archive root.
|
||||
# macOS ships the .app bundle (renamed to a stable "Firefox.app" by release.yml);
|
||||
# the wrapper execs the inner binary directly, which sidesteps Gatekeeper.
|
||||
BINARY_ENTRY_REL = {
|
||||
"win32": "firefox.exe",
|
||||
"linux": "firefox",
|
||||
"darwin": "Firefox.app/Contents/MacOS/firefox",
|
||||
}
|
||||
|
||||
# GitHub release URL template. The "TODO" owner is resolved at publication time.
|
||||
RELEASE_URL_TEMPLATE = (
|
||||
"https://github.com/feder-cr/invisible_playwright/releases/download/{tag}/{asset}"
|
||||
)
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────
|
||||
# GeoIP database (timezone="auto" → resolve IANA zone from proxy egress IP)
|
||||
# ─────────────────────────────────────────────────────────────────────────
|
||||
# daijro/geoip-all-in-one merges IP2Location LITE + GeoLite2 + DB-IP into a
|
||||
# single mmdb (country ISO + coordinates + IANA timezone via tzfpy), rebuilt
|
||||
# weekly. GPL-3.0, so we DOWNLOAD it at runtime into the user cache (like the
|
||||
# Firefox binary) rather than bundling it into this MIT package. The `-all`
|
||||
# variant covers IPv4+IPv6. download.py NEVER pins a tag (daijro prunes old
|
||||
# releases, so a pinned tag eventually 404s): on every launch it resolves the
|
||||
# CURRENT latest tag from the `releases/latest/download` permalink (no GitHub
|
||||
# API, no rate limit) and pulls it if newer than the cache.
|
||||
GEOIP_REPO: str = "daijro/geoip-all-in-one"
|
||||
GEOIP_ASSET: str = "geoip-aio-all.mmdb.zip"
|
||||
GEOIP_MMDB_NAME: str = "geoip-aio-all.mmdb"
|
||||
GEOIP_RELEASE_URL_TEMPLATE: str = (
|
||||
"https://github.com/daijro/geoip-all-in-one/releases/download/{tag}/{asset}"
|
||||
)
|
||||
"""Backward-compat shim — spostato in invisible_core.constants (alias completo)."""
|
||||
import sys as _sys
|
||||
from invisible_core import constants as _mod
|
||||
_sys.modules[__name__] = _mod
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,343 +1,4 @@
|
|||
"""Download and cache the patched Firefox binary from GitHub Releases."""
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tarfile
|
||||
import tempfile
|
||||
import time
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
import platformdirs
|
||||
import requests
|
||||
|
||||
from .constants import (
|
||||
ARCHIVE_NAME,
|
||||
BINARY_ENTRY_REL,
|
||||
BINARY_VERSION,
|
||||
BROKEN_VERSIONS,
|
||||
GEOIP_ASSET,
|
||||
GEOIP_MMDB_NAME,
|
||||
GEOIP_REPO,
|
||||
GEOIP_RELEASE_URL_TEMPLATE,
|
||||
RELEASE_URL_TEMPLATE,
|
||||
)
|
||||
|
||||
|
||||
def _github_token() -> str | None:
|
||||
return os.environ.get("STEALTHFOX_GITHUB_TOKEN") or os.environ.get("GITHUB_TOKEN")
|
||||
|
||||
|
||||
def _parse_owner_repo(template: str) -> tuple[str, str]:
|
||||
"""Extract (owner, repo) from RELEASE_URL_TEMPLATE."""
|
||||
m = re.match(r"https://github\.com/([^/]+)/([^/]+)/releases/", template)
|
||||
if not m:
|
||||
raise RuntimeError(f"cannot parse owner/repo from {template!r}")
|
||||
return m.group(1), m.group(2)
|
||||
|
||||
|
||||
def cache_root() -> Path:
|
||||
"""Directory where all cached binaries live."""
|
||||
return Path(platformdirs.user_cache_dir("invisible-playwright"))
|
||||
|
||||
|
||||
def cache_dir_for_version(version: str = BINARY_VERSION) -> Path:
|
||||
return cache_root() / version
|
||||
|
||||
|
||||
def _resolve_asset_url(tag: str, asset_name: str) -> str:
|
||||
"""Return a downloadable URL for the asset.
|
||||
|
||||
For private repos the direct `releases/download/<tag>/<asset>` URL returns
|
||||
404 even with a token, so we resolve via the API: list assets for the
|
||||
release tag, find the one matching `asset_name`, and use its API URL with
|
||||
`Accept: application/octet-stream` (which 302-redirects to a signed URL).
|
||||
For public repos the direct URL still works without a token.
|
||||
"""
|
||||
token = _github_token()
|
||||
if not token:
|
||||
return RELEASE_URL_TEMPLATE.format(tag=tag, asset=asset_name)
|
||||
owner, repo = _parse_owner_repo(RELEASE_URL_TEMPLATE)
|
||||
api = f"https://api.github.com/repos/{owner}/{repo}/releases/tags/{tag}"
|
||||
r = requests.get(api, headers={"Authorization": f"token {token}"}, timeout=30)
|
||||
r.raise_for_status()
|
||||
for a in r.json().get("assets", []):
|
||||
if a.get("name") == asset_name:
|
||||
return a["url"]
|
||||
raise RuntimeError(f"asset {asset_name!r} not found in release {tag!r}")
|
||||
|
||||
|
||||
def _download_file(url: str, dst: Path, chunk_size: int = 1 << 16) -> None:
|
||||
dst.parent.mkdir(parents=True, exist_ok=True)
|
||||
headers: dict[str, str] = {}
|
||||
token = _github_token()
|
||||
if token and url.startswith("https://api.github.com/"):
|
||||
headers["Authorization"] = f"token {token}"
|
||||
headers["Accept"] = "application/octet-stream"
|
||||
with requests.get(url, stream=True, timeout=60, headers=headers) as r:
|
||||
r.raise_for_status()
|
||||
with open(dst, "wb") as f:
|
||||
for chunk in r.iter_content(chunk_size):
|
||||
if chunk:
|
||||
f.write(chunk)
|
||||
|
||||
|
||||
def _sha256_file(path: Path) -> str:
|
||||
h = hashlib.sha256()
|
||||
with open(path, "rb") as f:
|
||||
for chunk in iter(lambda: f.read(1 << 16), b""):
|
||||
h.update(chunk)
|
||||
return h.hexdigest()
|
||||
|
||||
|
||||
def _parse_checksums(text: str) -> dict[str, str]:
|
||||
out: dict[str, str] = {}
|
||||
for line in text.splitlines():
|
||||
line = line.strip()
|
||||
if not line or line.startswith("#"):
|
||||
continue
|
||||
parts = line.split()
|
||||
if len(parts) >= 2:
|
||||
# sha256sum uses ' *' or ' ' prefix for binary vs text mode
|
||||
key = parts[-1].lstrip("*")
|
||||
out[key] = parts[0]
|
||||
return out
|
||||
|
||||
|
||||
def _extract(archive: Path, dst: Path) -> None:
|
||||
dst.mkdir(parents=True, exist_ok=True)
|
||||
if archive.suffix == ".zip":
|
||||
with zipfile.ZipFile(archive) as zf:
|
||||
zf.extractall(dst)
|
||||
elif archive.name.endswith(".tar.gz") or archive.suffix in {".tgz", ".gz"}:
|
||||
with tarfile.open(archive, "r:gz") as tf:
|
||||
tf.extractall(dst)
|
||||
else:
|
||||
raise RuntimeError(f"unknown archive format: {archive}")
|
||||
|
||||
|
||||
def _post_extract_darwin(app_root: Path, entry: Path) -> None:
|
||||
"""Make an ad-hoc-signed .app launchable on macOS.
|
||||
|
||||
The .app is downloaded via requests (no Finder quarantine attached), but we
|
||||
strip com.apple.quarantine defensively and ensure the inner binary is
|
||||
executable. We exec the inner binary directly (not via LaunchServices), so
|
||||
Gatekeeper's first-launch prompt does not apply; the ad-hoc signature
|
||||
(applied in release.yml) is what lets the arm64 Mach-O run at all.
|
||||
"""
|
||||
app = app_root
|
||||
# walk up to the .app bundle dir if entry points inside it
|
||||
for parent in entry.parents:
|
||||
if parent.name.endswith(".app"):
|
||||
app = parent
|
||||
break
|
||||
try:
|
||||
subprocess.run(["xattr", "-dr", "com.apple.quarantine", str(app)], check=False)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
try:
|
||||
entry.chmod(0o755)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
def ensure_binary(version: str = BINARY_VERSION) -> Path:
|
||||
"""Return a path to a runnable Firefox executable. Download if needed."""
|
||||
if version in BROKEN_VERSIONS:
|
||||
raise RuntimeError(
|
||||
f"{version} is a known-broken release (the juggler automation layer is "
|
||||
f"missing, so Playwright cannot drive it). Upgrade invisible_playwright "
|
||||
f"(current BINARY_VERSION={BINARY_VERSION}) or pass a newer version."
|
||||
)
|
||||
plat = sys.platform
|
||||
mach = platform.machine()
|
||||
asset = ARCHIVE_NAME(plat, mach)
|
||||
entry_rel = BINARY_ENTRY_REL.get(plat)
|
||||
if entry_rel is None:
|
||||
raise NotImplementedError(f"no binary entry for platform {plat}")
|
||||
|
||||
version_dir = cache_dir_for_version(version)
|
||||
entry = version_dir / entry_rel
|
||||
if entry.exists():
|
||||
return entry
|
||||
|
||||
url_archive = _resolve_asset_url(version, asset)
|
||||
url_sums = _resolve_asset_url(version, "checksums.txt")
|
||||
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
tmp = Path(td)
|
||||
archive_path = tmp / asset
|
||||
_download_file(url_archive, archive_path)
|
||||
sums_path = tmp / "checksums.txt"
|
||||
_download_file(url_sums, sums_path)
|
||||
sums = _parse_checksums(sums_path.read_text())
|
||||
expected = sums.get(asset)
|
||||
if expected is None:
|
||||
raise RuntimeError(f"no SHA256 for {asset} in checksums.txt")
|
||||
actual = _sha256_file(archive_path)
|
||||
if actual.lower() != expected.lower():
|
||||
raise RuntimeError(
|
||||
f"SHA256 mismatch for {asset}: got {actual}, expected {expected}"
|
||||
)
|
||||
_extract(archive_path, version_dir)
|
||||
|
||||
if plat == "darwin":
|
||||
_post_extract_darwin(version_dir, entry)
|
||||
|
||||
if not entry.exists():
|
||||
raise RuntimeError(f"binary not found after extraction: {entry}")
|
||||
return entry
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────
|
||||
# GeoIP mmdb (timezone="auto" → map egress IP → IANA zone)
|
||||
#
|
||||
# daijro/geoip-all-in-one is rebuilt weekly and KEEPS ONLY the latest ~2
|
||||
# releases — older tags are pruned and 404. So we NEVER pin a tag: on every
|
||||
# launch we resolve the CURRENT latest tag from the `releases/latest/download`
|
||||
# permalink (its 302 Location carries the tag — a plain CDN request, NOT the
|
||||
# rate-limited GitHub API) and download it if it differs from the cached one.
|
||||
# Offline → reuse the cached mmdb; cold cache + offline → raise (the caller can
|
||||
# then fall back off timezone="auto"). No stale pinned tag to rot.
|
||||
# ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
def _geoip_root() -> Path:
|
||||
return cache_root() / "geoip"
|
||||
|
||||
|
||||
def _cached_geoip_mmdb() -> Path | None:
|
||||
"""Newest cached mmdb across tag dirs, or None. Tag dirs are date strings
|
||||
(e.g. ``2026.06.17``) so a lexical sort is chronological."""
|
||||
root = _geoip_root()
|
||||
if not root.exists():
|
||||
return None
|
||||
cands = sorted(root.glob("*/*.mmdb"))
|
||||
return cands[-1] if cands else None
|
||||
|
||||
|
||||
def _geoip_latest_url() -> str:
|
||||
return f"https://github.com/{GEOIP_REPO}/releases/latest/download/{GEOIP_ASSET}"
|
||||
|
||||
|
||||
def _latest_geoip_tag_api() -> str:
|
||||
"""Latest ``daijro/geoip-all-in-one`` release tag via the GitHub API
|
||||
(fallback for :func:`_resolve_latest_geoip_tag` when the permalink HEAD
|
||||
can't be parsed)."""
|
||||
headers = {"Accept": "application/vnd.github+json"}
|
||||
token = _github_token()
|
||||
if token:
|
||||
headers["Authorization"] = f"token {token}"
|
||||
r = requests.get(
|
||||
f"https://api.github.com/repos/{GEOIP_REPO}/releases/latest",
|
||||
headers=headers, timeout=15,
|
||||
)
|
||||
r.raise_for_status()
|
||||
tag = r.json().get("tag_name")
|
||||
if not tag:
|
||||
raise RuntimeError("no tag_name in geoip-all-in-one latest release")
|
||||
return tag
|
||||
|
||||
|
||||
def _resolve_latest_geoip_tag() -> str | None:
|
||||
"""Current latest release tag WITHOUT the rate-limited API: HEAD the
|
||||
``releases/latest/download`` permalink — GitHub answers 302 with
|
||||
``Location: …/releases/download/<tag>/<asset>``. Falls back to the API,
|
||||
then to ``None`` (offline / unparseable)."""
|
||||
try:
|
||||
r = requests.head(_geoip_latest_url(), allow_redirects=False, timeout=10)
|
||||
loc = r.headers.get("Location") or r.headers.get("location") or ""
|
||||
m = re.search(r"/releases/download/([^/]+)/", loc)
|
||||
if m:
|
||||
return m.group(1)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
return _latest_geoip_tag_api()
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def _download_geoip_tag(tag: str) -> Path:
|
||||
"""Download + extract a specific tag's mmdb if not already cached."""
|
||||
dst_dir = _geoip_root() / tag
|
||||
target = dst_dir / GEOIP_MMDB_NAME
|
||||
if not target.exists():
|
||||
url = GEOIP_RELEASE_URL_TEMPLATE.format(tag=tag, asset=GEOIP_ASSET)
|
||||
dst_dir.mkdir(parents=True, exist_ok=True)
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
archive = Path(td) / GEOIP_ASSET
|
||||
_download_file(url, archive)
|
||||
_extract(archive, dst_dir)
|
||||
if target.exists():
|
||||
return target
|
||||
# asset name inside the zip may differ from GEOIP_MMDB_NAME
|
||||
found = sorted(dst_dir.glob("*.mmdb"))
|
||||
if found:
|
||||
return found[0]
|
||||
raise RuntimeError(f"geoip mmdb not found after extraction in {dst_dir}")
|
||||
|
||||
|
||||
def _prune_old_geoip_tags(keep: str) -> None:
|
||||
"""Drop every cached tag dir except ``keep`` to bound disk usage."""
|
||||
root = _geoip_root()
|
||||
if not root.exists():
|
||||
return
|
||||
for d in root.iterdir():
|
||||
if d.is_dir() and d.name != keep:
|
||||
shutil.rmtree(d, ignore_errors=True)
|
||||
|
||||
|
||||
def geoip_mmdb_path() -> Path | None:
|
||||
"""Path to the currently-cached mmdb (newest tag), or None if none cached."""
|
||||
return _cached_geoip_mmdb()
|
||||
|
||||
|
||||
def ensure_geoip_mmdb() -> Path:
|
||||
"""Return the geoip mmdb, always the latest daijro build. Checked on EVERY
|
||||
call — a single cheap permalink HEAD (no GitHub API, so no rate limit).
|
||||
|
||||
Resolution order:
|
||||
1. ``STEALTHFOX_GEOIP_MMDB`` env → use that file (user-supplied / test).
|
||||
2. Resolve the CURRENT latest tag. If it differs from the newest cached
|
||||
tag (or nothing is cached) → download it, prune older tags, return it.
|
||||
3. Latest tag == newest cached tag → use the cache (no download).
|
||||
4. Couldn't resolve the tag (offline / unparseable): cached mmdb → use it;
|
||||
cold cache → raise (caller can then drop timezone="auto").
|
||||
"""
|
||||
override = os.environ.get("STEALTHFOX_GEOIP_MMDB")
|
||||
if override:
|
||||
p = Path(override)
|
||||
if not p.exists():
|
||||
raise RuntimeError(f"STEALTHFOX_GEOIP_MMDB points to a missing file: {p}")
|
||||
return p
|
||||
|
||||
cached = _cached_geoip_mmdb()
|
||||
cached_tag = cached.parent.name if cached else None
|
||||
|
||||
latest = _resolve_latest_geoip_tag()
|
||||
if latest and latest != cached_tag:
|
||||
# newer build available (or nothing cached) → fetch it
|
||||
try:
|
||||
mmdb = _download_geoip_tag(latest)
|
||||
_prune_old_geoip_tags(mmdb.parent.name)
|
||||
return mmdb
|
||||
except Exception:
|
||||
if cached:
|
||||
return cached # transient download failure → keep using the cache
|
||||
raise
|
||||
|
||||
if cached:
|
||||
return cached # cache is already the latest, or we're offline
|
||||
|
||||
raise RuntimeError(
|
||||
"geoip mmdb unavailable: no cached copy and GitHub is unreachable. "
|
||||
"Connect once to download it, or set STEALTHFOX_GEOIP_MMDB to a local "
|
||||
"geoip-aio-all.mmdb file."
|
||||
)
|
||||
"""Backward-compat shim — spostato in invisible_core.download (alias completo)."""
|
||||
import sys as _sys
|
||||
from invisible_core import download as _mod
|
||||
_sys.modules[__name__] = _mod
|
||||
|
|
|
|||
|
|
@ -1,673 +1,4 @@
|
|||
"""Translate an internal Profile into the Firefox prefs dict that the
|
||||
patched Firefox binary expects.
|
||||
|
||||
The output dict keys map 1:1 to ``user.js`` preferences. Playwright passes
|
||||
them via ``firefox_user_prefs=``. The patched binary propagates them to all
|
||||
content processes over IPC; C++ patches read the ``zoom.stealth.*``
|
||||
namespace.
|
||||
|
||||
The translation is split into:
|
||||
|
||||
* ``_BASELINE`` — global stealth policy (RFP off, WebRTC leaks blocked,
|
||||
safebrowsing disabled, debugger detach, …) plus Windows-canonical
|
||||
constants that don't depend on the Profile (system colors palette,
|
||||
WebGL extensions whitelist, speech voices, navigator identity).
|
||||
* ``translate_profile_to_prefs`` — overlays the Profile fields plus the
|
||||
user-supplied ``locale`` and ``timezone``.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from ._fpforge import Profile
|
||||
from ._webgl_personas import render_noise_seed, select_persona
|
||||
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────
|
||||
# Navigator identity — locked to Firefox 150 Windows so the binary
|
||||
# reports the same UA / platform / oscpu regardless of the host OS.
|
||||
# ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
_NAVIGATOR_OVERRIDES: Dict[str, str] = {
|
||||
"general.useragent.override":
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:150.0) "
|
||||
"Gecko/20100101 Firefox/150.0.1",
|
||||
"general.platform.override": "Win32",
|
||||
"general.oscpu.override": "Windows NT 10.0; Win64; x64",
|
||||
# general.buildID.override removed 2026-04-28: the previous value
|
||||
# "20181001000000" was a 2018 buildID stuck on a 2026-built Firefox 150
|
||||
# binary (real BuildID=20260426192818 from application.ini). The 7.5-yr
|
||||
# discrepancy is the kind of internal-consistency check Google reCAPTCHA
|
||||
# can use to flag bot/spoofed browsers. Deleting the override lets
|
||||
# Firefox emit its compiled-in buildID, which auto-tracks the binary.
|
||||
# A/B knockout 2026-04-28 (n=30): F2 delete +0.083 RC vs BASE; n=100
|
||||
# confirm: +0.021; overnight isolated: +0.155 single-variant. Variable
|
||||
# signal, but the underlying data error is unambiguous.
|
||||
"general.appversion.override": "5.0 (Windows)",
|
||||
}
|
||||
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────
|
||||
# System colors — FP Pro probes getComputedStyle(div) with CSS system
|
||||
# keywords (ButtonFace, Menu, Highlight, …) and hashes the result into
|
||||
# signal s142. On Linux, Firefox resolves these via GTK theme → GTK
|
||||
# RGB values diverge from Windows Win32 palette → server-side anomaly
|
||||
# even with Windows UA. Pinning the palette to Win10 default closes
|
||||
# the gap (see project_css_system_colors.md memory).
|
||||
# ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
_WIN_LIGHT_COLORS: Dict[str, str] = {
|
||||
"ui.activeborder": "#B4B4B4",
|
||||
"ui.activecaption": "#99B4D1",
|
||||
"ui.appworkspace": "#ABABAB",
|
||||
"ui.background": "#000000",
|
||||
"ui.buttonface": "#F0F0F0",
|
||||
"ui.buttonhighlight": "#FFFFFF",
|
||||
"ui.buttonshadow": "#A0A0A0",
|
||||
"ui.buttontext": "#000000",
|
||||
"ui.buttonborder": "#000000",
|
||||
"ui.captiontext": "#000000",
|
||||
"ui.graytext": "#6D6D6D",
|
||||
"ui.highlight": "#0078D7",
|
||||
"ui.highlighttext": "#FFFFFF",
|
||||
"ui.inactiveborder": "#F4F7FC",
|
||||
"ui.inactivecaption": "#BFCDDB",
|
||||
"ui.inactivecaptiontext": "#434E54",
|
||||
"ui.infobackground": "#FFFFE1",
|
||||
"ui.infotext": "#000000",
|
||||
"ui.menu": "#F9F9FB",
|
||||
"ui.menutext": "#000000",
|
||||
"ui.scrollbar": "#C8C8C8",
|
||||
"ui.threeddarkshadow": "#696969",
|
||||
"ui.threedface": "#F0F0F0",
|
||||
"ui.threedhighlight": "#FFFFFF",
|
||||
"ui.threedlightshadow": "#E3E3E3",
|
||||
"ui.threedshadow": "#A0A0A0",
|
||||
"ui.window": "#FFFFFF",
|
||||
"ui.windowframe": "#646464",
|
||||
"ui.windowtext": "#000000",
|
||||
"ui.mark": "#FFFF00",
|
||||
"ui.marktext": "#000000",
|
||||
"ui.accentcolor": "#0078D4",
|
||||
"ui.accentcolortext": "#FFFFFF",
|
||||
"ui.selecteditem": "#0078D7",
|
||||
"ui.selecteditemtext": "#FFFFFF",
|
||||
"ui.-moz-hyperlinktext": "#0066CC",
|
||||
"ui.-moz-activehyperlinktext": "#EE0000",
|
||||
"ui.-moz-visitedhyperlinktext": "#551A8B",
|
||||
}
|
||||
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────
|
||||
# WebGL extensions — Windows ANGLE canonical lists. Empty string =
|
||||
# fall back to native Mesa/ANGLE; non-empty = `getSupportedExtensions`
|
||||
# returns this list verbatim and `IsSupported()` rejects anything else.
|
||||
# ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
_WEBGL1_EXTENSIONS = ",".join([
|
||||
"ANGLE_instanced_arrays",
|
||||
"EXT_blend_minmax",
|
||||
"EXT_color_buffer_half_float",
|
||||
"EXT_float_blend",
|
||||
"EXT_frag_depth",
|
||||
"EXT_sRGB",
|
||||
"EXT_shader_texture_lod",
|
||||
"EXT_texture_compression_bptc",
|
||||
"EXT_texture_compression_rgtc",
|
||||
"EXT_texture_filter_anisotropic",
|
||||
"OES_element_index_uint",
|
||||
"OES_fbo_render_mipmap",
|
||||
"OES_standard_derivatives",
|
||||
"OES_texture_float",
|
||||
"OES_texture_float_linear",
|
||||
"OES_texture_half_float",
|
||||
"OES_texture_half_float_linear",
|
||||
"OES_vertex_array_object",
|
||||
"WEBGL_color_buffer_float",
|
||||
"WEBGL_compressed_texture_s3tc",
|
||||
"WEBGL_compressed_texture_s3tc_srgb",
|
||||
"WEBGL_debug_renderer_info",
|
||||
"WEBGL_debug_shaders",
|
||||
"WEBGL_depth_texture",
|
||||
"WEBGL_draw_buffers",
|
||||
"WEBGL_lose_context",
|
||||
"WEBGL_provoking_vertex",
|
||||
])
|
||||
|
||||
_WEBGL2_EXTENSIONS = ",".join([
|
||||
"EXT_color_buffer_float",
|
||||
"EXT_color_buffer_half_float",
|
||||
"EXT_float_blend",
|
||||
"EXT_texture_compression_bptc",
|
||||
"EXT_texture_compression_rgtc",
|
||||
"EXT_texture_filter_anisotropic",
|
||||
"OES_draw_buffers_indexed",
|
||||
"OES_texture_float_linear",
|
||||
"OES_texture_half_float_linear",
|
||||
"OVR_multiview2",
|
||||
"WEBGL_compressed_texture_s3tc",
|
||||
"WEBGL_compressed_texture_s3tc_srgb",
|
||||
"WEBGL_debug_renderer_info",
|
||||
"WEBGL_debug_shaders",
|
||||
"WEBGL_lose_context",
|
||||
"WEBGL_provoking_vertex",
|
||||
])
|
||||
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────
|
||||
# Speech voices — Windows canonical "Microsoft *" set. Format:
|
||||
# "NAME|LANG|DEFAULT|LOCAL,...". Non-empty value drives the
|
||||
# speechSynthesis.getVoices() patch; empty disables it.
|
||||
# ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
_WIN_VOICES = ",".join([
|
||||
"Microsoft David - English (United States)|en-US|1|1",
|
||||
"Microsoft Zira - English (United States)|en-US|0|1",
|
||||
"Microsoft Mark - English (United States)|en-US|0|1",
|
||||
"Microsoft David Desktop - English (United States)|en-US|0|1",
|
||||
"Microsoft Zira Desktop - English (United States)|en-US|0|1",
|
||||
])
|
||||
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────
|
||||
# Baseline — applied to every session regardless of Profile.
|
||||
# ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
_BASELINE: Dict[str, Any] = {
|
||||
# Turn off Firefox's own resistFingerprinting; we do our own via patches.
|
||||
"privacy.resistFingerprinting": False,
|
||||
"privacy.resistFingerprinting.letterboxing": False,
|
||||
|
||||
# FF150 fingerprintingProtection — enabled by default (or remotely via
|
||||
# Mozilla webcompat overrides). FP Pro detects the side-effects and
|
||||
# flips `privacy_settings: true`. On FF146 these were all off → False.
|
||||
# Force off so FP Pro reports privacy_settings:false (matches FF146).
|
||||
"privacy.fingerprintingProtection": False,
|
||||
"privacy.fingerprintingProtection.pbmode": False,
|
||||
"privacy.fingerprintingProtection.remoteOverrides.enabled": False,
|
||||
|
||||
# WebRTC: enabled, looks like a real Firefox behind NAT, no real-IP leak.
|
||||
# obfuscate_host_addresses=true → host candidate is `<uuid>.local` mDNS,
|
||||
# exactly like vanilla Firefox (BrowserLeaks "No Leak", Local IP "-").
|
||||
# The mDNS-IPC hang feared on older builds does NOT reproduce on FF150.
|
||||
# The proxy-egress srflx is injected by our C++ (srflx swap §17 + fallback
|
||||
# §17.B), fed the egress IP via STEALTHFOX_WEBRTC_PUBLIC_IP from
|
||||
# launcher._build_env (auto-discovered from the proxy).
|
||||
# IPv6: media.peerconnection.ice.disableIPv6 is DEAD on FF150 (read by no
|
||||
# ICE-gathering code). The real switch is our zoom.stealth.webrtc.disable_ipv6
|
||||
# (nICEr addrs.cpp filter) + the STEALTHFOX_WEBRTC_DISABLE_IPV6 env.
|
||||
"media.peerconnection.enabled": True,
|
||||
"media.peerconnection.ice.no_host": False,
|
||||
"media.peerconnection.ice.default_address_only": False,
|
||||
"media.peerconnection.ice.obfuscate_host_addresses": True,
|
||||
"zoom.stealth.webrtc.disable_ipv6": True,
|
||||
"media.peerconnection.ice.proxy_only": False,
|
||||
"media.peerconnection.ice.relay_only": False,
|
||||
"media.peerconnection.use_document_iceservers": True,
|
||||
|
||||
# Proxy — route DNS through SOCKS proxies to avoid local DNS leaks.
|
||||
"network.proxy.socks_remote_dns": True,
|
||||
"network.proxy.failover_direct": False,
|
||||
|
||||
# TLS ClientHello fingerprint — match stock Firefox byte-for-byte.
|
||||
# The Playwright/Juggler Firefox build this binary derives from re-enables
|
||||
# cipher 0xC009 (TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA), which retail Firefox
|
||||
# 150 does NOT offer. That extra (17th) cipher shifts our JA3/JA4 away from
|
||||
# any real Firefox (ja4 t13d1717h2 vs stock t13d1617h2). A ClientHello that
|
||||
# matches no real browser is itself a consistency tell. Disabling it makes
|
||||
# JA3/JA4/peetprint byte-identical to retail FF150 (verified on tls.peet.ws).
|
||||
# Stock Firefox ships without 0xC009 and works on the whole web, so this only
|
||||
# improves fingerprint consistency — it cannot break connectivity.
|
||||
"security.ssl3.ecdhe_ecdsa_aes_128_sha": False,
|
||||
|
||||
# Safebrowsing — chatty and fingerprintable.
|
||||
"browser.safebrowsing.malware.enabled": False,
|
||||
"browser.safebrowsing.phishing.enabled": False,
|
||||
"browser.safebrowsing.downloads.enabled": False,
|
||||
"browser.safebrowsing.downloads.remote.enabled": False,
|
||||
|
||||
# First-run / welcome UI noise.
|
||||
"browser.startup.page": 0,
|
||||
"browser.shell.checkDefaultBrowser": False,
|
||||
"browser.aboutwelcome.enabled": False,
|
||||
"browser.startup.upgradeDialog.enabled": False,
|
||||
"termsofuse.acceptedVersion": 999,
|
||||
|
||||
# Disable about:newtab auto-load — TopSitesFeed.sys.mjs auto-fetches when
|
||||
# a tab opens, triggering a cross-process BC swap that hijacks the first
|
||||
# page.goto() (NS_BINDING_ABORTED on creepjs/peet/sannysoft/fppro).
|
||||
"browser.newtabpage.enabled": False,
|
||||
"browser.newtab.preload": False,
|
||||
"browser.newtabpage.activity-stream.feeds.topsites": False,
|
||||
"browser.newtabpage.activity-stream.feeds.section.topstories": False,
|
||||
"browser.newtabpage.activity-stream.enabled": False,
|
||||
|
||||
# Disable Firefox internal services that hit the network on startup.
|
||||
# Through a residential SOCKS5 proxy these compete with the test
|
||||
# navigation and trigger NS_BINDING_FAILED (server-side rate-limit /
|
||||
# connection drops). Domains observed in MOZ_LOG: push.services,
|
||||
# firefox.settings.services, detectportal, ohttp-gateway, location.
|
||||
"browser.aboutConfig.showWarning": False,
|
||||
"network.captive-portal-service.enabled": False,
|
||||
"network.connectivity-service.enabled": False,
|
||||
"dom.push.enabled": False,
|
||||
"dom.push.connection.enabled": False,
|
||||
"geo.enabled": False,
|
||||
"geo.provider.network.url": "",
|
||||
"browser.region.network.url": "",
|
||||
"browser.region.update.enabled": False,
|
||||
"services.settings.server": "",
|
||||
"browser.search.geoSpecificDefaults": False,
|
||||
"browser.contentblocking.report.lockwise.enabled": False,
|
||||
"browser.contentblocking.report.monitor.enabled": False,
|
||||
"extensions.systemAddon.update.enabled": False,
|
||||
"extensions.update.enabled": False,
|
||||
"extensions.getAddons.cache.enabled": False,
|
||||
"browser.discovery.enabled": False,
|
||||
"browser.ping-centre.telemetry": False,
|
||||
"app.normandy.enabled": False,
|
||||
"dom.private-attribution.submission.enabled": False,
|
||||
"browser.translations.enable": False,
|
||||
"browser.search.update": False,
|
||||
|
||||
# HTTP/3 + speculative + Alt-Svc disabled. SOCKS5 proxy doesn't
|
||||
# support UDP ASSOCIATE so HTTP/3 fails. Speculative connections
|
||||
# under load cause early channel cancel (NS_BINDING_FAILED).
|
||||
"network.http.http3.enable": False,
|
||||
"network.http.http3.enabled": False,
|
||||
"network.http.altsvc.enabled": False,
|
||||
"network.http.altsvc.oe": False,
|
||||
"network.http.speculative-parallel-limit": 0,
|
||||
"network.predictor.enabled": False,
|
||||
"network.dns.disablePrefetch": True,
|
||||
"network.dns.disablePrefetchFromHTTPS": True,
|
||||
"network.dns.echconfig.enabled": False,
|
||||
"network.dns.use_https_rr_as_altsvc": False,
|
||||
|
||||
# === Fission / site-isolation disabled (FF146 Playwright parity) ===
|
||||
# Force a single content-process model. Three knobs are required in FF150:
|
||||
# upstream Playwright Firefox (FF146-based) only needed fission.autostart=False
|
||||
# because FF146's default isolation strategy was looser. FF150 ships with
|
||||
# fission.webContentIsolationStrategy=1 (IsolateEverything) which still
|
||||
# site-isolates cross-origin iframes into separate `webIsolated` content
|
||||
# processes EVEN WHEN fission.autostart is False. From the parent process's
|
||||
# point of view, those iframes get a Juggler Frame placeholder with no
|
||||
# docShell, no URL, and an execution context that wraps the wrong global,
|
||||
# so frame.evaluate() fails with cross-origin SOP errors and
|
||||
# element_handle.content_frame() returns None.
|
||||
#
|
||||
# Pinning the strategy to 0 keeps every cross-origin web iframe in the
|
||||
# parent's content process, where the Juggler code paths from the FF146
|
||||
# era expect them. processCount.webIsolated=1 is kept as belt-and-suspenders
|
||||
# in case some path still classifies an origin as webIsolated despite the
|
||||
# strategy change. It costs nothing to leave.
|
||||
#
|
||||
# See issue #20 + tests/test_cross_origin_iframe.py for the regression
|
||||
# sentinel that catches a future A/B flipping these back.
|
||||
"fission.autostart": False,
|
||||
"fission.autostart.session": False,
|
||||
"fission.webContentIsolationStrategy": 0, # IsolateNothing
|
||||
"dom.ipc.processCount.webIsolated": 1,
|
||||
|
||||
|
||||
# Telemetry & data reporting.
|
||||
"datareporting.healthreport.uploadEnabled": False,
|
||||
"datareporting.policy.dataSubmissionEnabled": False,
|
||||
"toolkit.telemetry.enabled": False,
|
||||
"toolkit.telemetry.unified": False,
|
||||
"app.shield.optoutstudies.enabled": False,
|
||||
|
||||
# Update channels.
|
||||
"app.update.enabled": False,
|
||||
"app.update.auto": False,
|
||||
|
||||
# Speech synth: enabled (the C++ patch fabricates voices from the
|
||||
# comma list above) regardless of the host OS.
|
||||
"media.webspeech.synth.enabled": True,
|
||||
"zoom.stealth.voices.list": _WIN_VOICES,
|
||||
|
||||
# WebGL extensions whitelist — non-empty pre-empts native enumeration.
|
||||
"zoom.stealth.webgl.extensions": _WEBGL1_EXTENSIONS,
|
||||
"zoom.stealth.webgl2.extensions": _WEBGL2_EXTENSIONS,
|
||||
# WebGL numeric param overrides — kept empty (A/B test 2026-04-22 showed
|
||||
# mismatches between the values we shipped and ANGLE's real envelope
|
||||
# raised FP Pro's ML tampering score). Slot kept for future experiments.
|
||||
"zoom.stealth.webgl.int_params": "",
|
||||
"zoom.stealth.webgl.int2_params": "",
|
||||
"zoom.stealth.webgl.shader_precisions": "",
|
||||
"zoom.stealth.webgl.float_params": "",
|
||||
|
||||
# DevTools anti-detection.
|
||||
"zoom.stealth.debugger.force_detach": True,
|
||||
|
||||
# Canvas substitution (Option B for canvas) — replace pixels with hash(seed,idx),
|
||||
# uniform-skip (red-box exact, masking-safe) + full overwrite. Makes the canvas
|
||||
# render a pure function of (seed) = HOST-INDEPENDENT (kills the DWrite-vs-FreeType
|
||||
# text-raster leak: Canvas Hash + Font hash were the residual Win!=Linux signals).
|
||||
# ON by default (paired with webgl.substitute_pixels).
|
||||
"zoom.stealth.canvas.substitute_pixels": True,
|
||||
|
||||
# WebGL substitution (Option B) — replace readback/snapshot RGB with
|
||||
# hash(seed,idx), endpoint-preserving. Makes the WebGL render hash a pure
|
||||
# function of (seed, dims) = HOST-INDEPENDENT, so no per-host hw_seed
|
||||
# calibration is needed (the gamma path was per-host: NVIDIA/Arc-on-Win clean
|
||||
# seeds went dirty on the Linux GL backend). ON by default.
|
||||
"zoom.stealth.webgl.substitute_pixels": True,
|
||||
|
||||
# WebGPU presence consistency. Firefox enables dom.webgpu.enabled by default on
|
||||
# Windows/Mac-ARM but NOT on Linux/Mac-x64. We ALWAYS claim Windows, so force it ON
|
||||
# on every host: a Windows FF MUST expose navigator.gpu (object); a Linux host leaving
|
||||
# it undefined while the UA says Windows is an inconsistency tell (RE 2026-06-22:
|
||||
# has_gpu was object on Win, undefined on WSL). adapter.info is empty (FF privacy
|
||||
# default) so no GPU-name leak; requestAdapter may be null on a GPU-less host, which
|
||||
# is itself plausible for a real Windows machine.
|
||||
"dom.webgpu.enabled": True,
|
||||
|
||||
# Audio fingerprint noise OFF. RE 2026-06-22: the per-session OfflineAudioContext
|
||||
# noise (gated by hw_seed) was THE dominant driver of FP Pro tampering_ml on Windows
|
||||
# — b005 Win dropped 0.4349 -> 0.0564 with audio noise alone disabled (canvas_text/
|
||||
# emoji unchanged, so they were a red-herring). The audio value is already host-indep
|
||||
# AND identical to a real FF's canonical OfflineAudioContext sum, so a fixed (un-noised)
|
||||
# audio is NOT a linking signal (every real FF has the same value) — removing the noise
|
||||
# matches real Firefox and clears the tampering flag.
|
||||
"zoom.stealth.audio.fp_noise": False,
|
||||
|
||||
# Navigator identity (locked to Windows Firefox 150).
|
||||
**_NAVIGATOR_OVERRIDES,
|
||||
}
|
||||
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────
|
||||
# Linux-only Xvfb workarounds — the Linux Firefox build under Xvfb
|
||||
# cannot run WebRender (`ConnectToCompositor` retries forever). We
|
||||
# disable WebRender + force WebGL through the GL software path so
|
||||
# webgl_basics / webgl_extensions still report.
|
||||
# ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
_LINUX_XVFB_WORKAROUNDS: Dict[str, Any] = {
|
||||
"gfx.webrender.all": False,
|
||||
"gfx.webrender.force-disabled": True,
|
||||
"webgl.force-enabled": True,
|
||||
# webgl.software-rendering-enabled / webgl.force-layers-readback removed in FF150.
|
||||
}
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────
|
||||
# Windows virtual-desktop workarounds — when headless=True on Windows,
|
||||
# Firefox runs on a CreateDesktop virtual desktop. The hardware GPU is
|
||||
# inaccessible from the virtual desktop, so the GPU process crashes when
|
||||
# it tries to initialize the D3D11 compositor with hardware acceleration.
|
||||
#
|
||||
# Approach: force D3D11 WARP (CPU software renderer) for the GPU process.
|
||||
# layers.d3d11.force-warp=True → compositor uses WARP → GPU process stable.
|
||||
# webgl.angle.force-warp=True → ANGLE uses WARP → WebGL context creates.
|
||||
#
|
||||
# CRITICAL: do NOT set webgl.out-of-process=False. That moves WebGL from the
|
||||
# GPU process to the sandboxed content process. The content process sandbox
|
||||
# blocks D3D11 access entirely → ANGLE crashes the content process →
|
||||
# canvas.getContext('webgl') throws instead of returning null.
|
||||
#
|
||||
# gfx.canvas.accelerated=False: default is true, disabling avoids any
|
||||
# hardware GPU dependency for 2D canvas in the content process.
|
||||
# ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
_WIN_VIRT_DESKTOP_WORKAROUNDS: Dict[str, Any] = {
|
||||
# FF150 regression vs FF146 on CreateDesktop alt-desktop:
|
||||
# The GPU process sandbox (level=1, default since FF110) tries to parent
|
||||
# its compositor window to the parent process's window. Our worker spawns
|
||||
# Firefox on a CreateDesktop-created alt desktop — parent and GPU process
|
||||
# do not share the same desktop/HWND namespace, so window parenting fails
|
||||
# silently. WebRender falls back to "Software D3D11" and OOP-WebGL never
|
||||
# publishes a hardware ANGLE renderer → getContext('webgl') returns a
|
||||
# context but extensions/parameters/$hash all come back null/empty (FF146
|
||||
# had a more permissive sandbox, so the same setup worked there).
|
||||
# Bugzilla refs: 1798091, 1524591, 1229829. Lowering the GPU sandbox to 0
|
||||
# restores hardware compositor + functional WebGL on alt desktops.
|
||||
"security.sandbox.gpu.level": 0,
|
||||
# Same root cause as above, content process side. Wrapper repo issue #18
|
||||
# (tab crash on cross-process navigation under headless=True). Sandbox
|
||||
# content level > 4 puts content processes on the sandbox's own
|
||||
# kAlternateWinstation (see security/sandbox/win/src/sandboxbroker/
|
||||
# sandboxBroker.cpp line 1113-1114:
|
||||
# `if (aSandboxLevel > 4) config->SetDesktop(kAlternateWinstation)`).
|
||||
# Combined with our CreateDesktop alt-desktop, that puts browser process
|
||||
# and content processes on DIFFERENT desktops. Cross-process navigation
|
||||
# then fails window parenting between parent and child, the content
|
||||
# process exits cleanly (exitCode=0, signal=null) and Playwright fires
|
||||
# page.on('crash') ~10s after page load. Lowering content sandbox to 4
|
||||
# keeps content processes on the same desktop as the browser process,
|
||||
# which is what we want here (still tight enough — level 4 blocks
|
||||
# file/registry write, network calls, hardware access).
|
||||
"security.sandbox.content.level": 4,
|
||||
}
|
||||
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────
|
||||
# Public helpers
|
||||
# ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
def _accept_language(locale: str) -> str:
|
||||
# "<locale>, <base>" — the desktop-default shape (e.g. "en-US, en"). Firefox expands it
|
||||
# to navigator.languages=["en-US","en"] AND (via the patched binary) the q-valued header
|
||||
# "en-US,en;q=0.5". The patched nsHttpHandler (STEALTHFOX, RE 2026-06-23) builds the
|
||||
# Accept-Language header from THIS pref even when juggler sets a per-context locale
|
||||
# override, so header and navigator.languages stay consistent 2/2 — the most authentic
|
||||
# (real desktop) form. Supersedes the 2026-06-22 single-tag workaround.
|
||||
lang = locale.replace("_", "-")
|
||||
base = lang.split("-")[0]
|
||||
return f"{lang}, {base}" if base != lang else lang
|
||||
|
||||
|
||||
def translate_profile_to_prefs(
|
||||
profile: Profile,
|
||||
*,
|
||||
locale: str = "en-US",
|
||||
timezone: str = "",
|
||||
extra_prefs: Optional[Dict[str, Any]] = None,
|
||||
virtual_display: bool = False,
|
||||
) -> Dict[str, Any]:
|
||||
"""Return a complete prefs dict ready for Playwright's firefox_user_prefs=.
|
||||
|
||||
Args:
|
||||
profile: Bayesian-sampled fingerprint (from ``generate_profile``).
|
||||
locale: BCP-47 tag, e.g. ``"en-US"``.
|
||||
timezone: IANA timezone name, e.g. ``"America/New_York"``.
|
||||
extra_prefs: Optional overlay applied LAST.
|
||||
virtual_display: When True on Windows, apply GPU-disabling workarounds
|
||||
to prevent the GPU process from crashing on virtual
|
||||
desktops that have no D3D11 backend.
|
||||
"""
|
||||
prefs: Dict[str, Any] = dict(_BASELINE)
|
||||
|
||||
# GPU / WebGL renderer/vendor.
|
||||
# On Linux we spoof to a Windows ANGLE renderer string (profile.gpu.renderer)
|
||||
# so cross-platform sessions report a consistent Windows GPU identity.
|
||||
# On Windows/mac, spoofing a renderer string ALONE is unsafe — the ~81
|
||||
# getParameter values stay real, so a name↔params hash mismatch FP Pro flags
|
||||
# (setting GTX 980 over real Arc A750 params scored ~0.70). Instead we apply a
|
||||
# VALIDATED PERSONA (see _webgl_personas): a {renderer, vendor} whose params are
|
||||
# the shared ANGLE D3D11 caps (vendor-independent — identical on any host, per the
|
||||
# ANGLE source) and whose extension list is FORCED below. That is a coherent fake
|
||||
# GPU that passes FP Pro host-independently (the host's real GPU never leaks). If no
|
||||
# validated persona exists for the sampled gpu_class yet, fall back to the host-real
|
||||
# renderer (empty → native ANGLE; SanitizeRenderer at ClientWebGLContext.cpp:2592).
|
||||
# Apply the camoufox-derived real-Firefox GPU persona on EVERY host (Win/Linux/Mac).
|
||||
# We must ALWAYS look Windows (rule), and the WebGL override is platform-independent:
|
||||
# SanitizeRenderer (ClientWebGLContext.cpp) is pure string regex, and the param/extension
|
||||
# overrides are pref-driven, so the C++ presents the SAME Windows ANGLE GPU regardless of
|
||||
# the host's real GL backend (it never consults it when the pref is set). This is why a
|
||||
# Windows GPU shows correctly even on a Linux/Mesa host (no more "Generic Renderer").
|
||||
_persona = select_persona(profile.seed)
|
||||
if _persona:
|
||||
# Apply the FULL coherent WebGL override (renderer + vendor + webgl1/webgl2 extensions
|
||||
# + ~100 getParameter values + shader-precision formats). Setting ALL of them — not just
|
||||
# the renderer string — keeps renderer<->params coherent (FP Pro cross-checks them); a
|
||||
# string-only spoof over the host's real params is the old ~0.85 mismatch.
|
||||
for _k, _v in _persona["prefs"].items():
|
||||
if _k == "zoom.stealth.webgl2.enabled":
|
||||
prefs["webgl.enable-webgl2"] = bool(_v)
|
||||
else:
|
||||
prefs[_k] = _v
|
||||
else:
|
||||
prefs["zoom.stealth.webgl.renderer"] = ""
|
||||
prefs["zoom.stealth.webgl.vendor"] = ""
|
||||
# Canvas-noise mask is calibrated to the REAL host GPU's rendering variance — the canvas is
|
||||
# drawn by real hardware, NOT the persona's claimed GPU, so it must NOT follow the persona
|
||||
# (a non-Intel persona on an Intel host would over-noise). Dev/deployment host is Intel.
|
||||
_renderer_lo = "intel"
|
||||
|
||||
# MSAA: on Windows, pin to 4 (Firefox default for ANGLE) so gl.SAMPLES is
|
||||
# constant across all sessions. Different MSAA values cause different CN-set
|
||||
# parameters hashes even with the same renderer → detectable variation.
|
||||
# Vanilla Intel Arc A750 parameters hash (66544db8) verified at msaa=4.
|
||||
_msaa = profile.webgl.msaa_samples if sys.platform.startswith("linux") else 4
|
||||
prefs["zoom.stealth.webgl.msaa"] = _msaa
|
||||
prefs["webgl.msaa-samples"] = _msaa
|
||||
prefs["webgl.msaa-force"] = _msaa > 0
|
||||
|
||||
# Canvas pixel-noise density per vendor. Intel has lower natural
|
||||
# rendering variance than NVIDIA/AMD, so the default 1/8 noise rate
|
||||
# over-amplifies the FP Pro tampering ML signal. Drop to 1/16 for Intel
|
||||
# to keep tampering_ml below the detection threshold while still
|
||||
# breaking the canvas geometry hash.
|
||||
if "intel" in _renderer_lo:
|
||||
prefs["zoom.stealth.canvas.noise_skip_mask"] = 15 # 1/16, ~6.25%
|
||||
else:
|
||||
prefs["zoom.stealth.canvas.noise_skip_mask"] = 7 # 1/8, ~12.5%
|
||||
|
||||
# Screen
|
||||
prefs["zoom.stealth.screen.width"] = profile.screen.width
|
||||
prefs["zoom.stealth.screen.height"] = profile.screen.height
|
||||
prefs["zoom.stealth.screen.avail_width"] = profile.screen.avail_width
|
||||
prefs["zoom.stealth.screen.avail_height"] = profile.screen.avail_height
|
||||
prefs["zoom.stealth.screen.dpr"] = profile.screen.dpr
|
||||
prefs["layout.css.devPixelsPerPx"] = str(profile.screen.dpr)
|
||||
|
||||
# Hardware — coherent with the sampled gpu_class by construction (the forge
|
||||
# draws hw_concurrency conditioned on the GPU class).
|
||||
prefs["zoom.stealth.hw_concurrency"] = profile.hardware.concurrency
|
||||
prefs["zoom.stealth.storage.quota_mb"] = profile.hardware.storage_quota_mb
|
||||
|
||||
# Audio
|
||||
prefs["zoom.stealth.audio.sample_rate"] = profile.audio.sample_rate
|
||||
prefs["zoom.stealth.audio.output_latency_ms"] = profile.audio.output_latency_ms
|
||||
prefs["zoom.stealth.audio.max_channel_count"] = profile.audio.max_channel_count
|
||||
|
||||
# Codec
|
||||
prefs["media.av1.enabled"] = profile.codec.av1_enabled
|
||||
prefs["media.encoder.webm.enabled"] = profile.codec.webm_encoder_enabled
|
||||
prefs["media.mediasource.webm.enabled"] = profile.codec.mediasource_webm
|
||||
prefs["media.mediasource.mp4.enabled"] = profile.codec.mediasource_mp4
|
||||
|
||||
# Fonts — real bundled fonts (no collapse). The binary ships the real
|
||||
# Windows font files in <GRE>/fonts and loads them via MOZ_BUNDLED_FONTS, so
|
||||
# glyphs are genuine on every host. We expose this profile's family set via
|
||||
# the per-profile fontlist, which the binary applies to the native system
|
||||
# font allow-list AT CONSTRUCTION (no runtime rebuild → no scan stall), and
|
||||
# force the system-ui generic to Segoe UI. profile.fonts is the _fpforge
|
||||
# sample (core always + a conditioned optional subset). Per-profile metric
|
||||
# uniqueness comes from the shared fpp.hw_seed jitter in the HarfBuzz shaper
|
||||
# (set with the other fpp prefs), not from fabricated widths.
|
||||
prefs["zoom.stealth.font.fontlist"] = ",".join(profile.fonts)
|
||||
prefs["zoom.stealth.font.system_ui"] = "Segoe UI"
|
||||
|
||||
# Activate the bundled real-Windows fonts (MOZ_BUNDLED_FONTS / <GRE>/fonts).
|
||||
prefs["gfx.bundled-fonts.activate"] = 1
|
||||
# Point the CSS generics at Windows defaults. On a Windows HOST Firefox's
|
||||
# built-in name-lists already resolve to these (and they're in the fontlist
|
||||
# so they survive the allow-list); but on a non-Windows host (Linux/Mac) the
|
||||
# built-in defaults name host fonts (DejaVu/Liberation/…) which the allow-list
|
||||
# hides — so the generics would collapse. Setting them explicitly keeps the
|
||||
# generics resolving to the bundled Windows families on EVERY host (system-ui
|
||||
# is forced to Segoe UI by the C++ hook above, so it is not listed here).
|
||||
prefs["font.name-list.serif.x-western"] = "Times New Roman"
|
||||
prefs["font.name-list.sans-serif.x-western"] = "Arial"
|
||||
prefs["font.name-list.monospace.x-western"] = "Consolas"
|
||||
prefs["font.name-list.sans-serif.ja"] = "Yu Gothic UI"
|
||||
prefs["font.name-list.serif.ja"] = "Yu Gothic UI"
|
||||
prefs["font.name-list.sans-serif.ko"] = "Malgun Gothic"
|
||||
prefs["font.name-list.serif.ko"] = "Malgun Gothic"
|
||||
prefs["font.name-list.sans-serif.zh-CN"] = "Microsoft YaHei UI"
|
||||
prefs["font.name-list.sans-serif.zh-TW"] = "Microsoft JhengHei UI"
|
||||
prefs["font.name-list.sans-serif.zh-HK"] = "Microsoft JhengHei UI"
|
||||
|
||||
# UI / dark mode + Windows colors palette (only when light theme).
|
||||
prefs["ui.systemUsesDarkTheme"] = int(profile.dark_theme)
|
||||
if not profile.dark_theme:
|
||||
prefs.update(_WIN_LIGHT_COLORS)
|
||||
|
||||
# Locale prefs.
|
||||
locale = locale or "en-US"
|
||||
lang = locale.replace("_", "-")
|
||||
prefs["intl.accept_languages"] = _accept_language(locale)
|
||||
prefs["general.useragent.locale"] = lang
|
||||
prefs["intl.locale.requested"] = lang
|
||||
prefs["privacy.spoof_english"] = 0
|
||||
# juggler.locale.override seeds the BrowsingContext LanguageOverride FIELD in
|
||||
# the parent process (BrowsingContext::Attach), whose DidSet drives BOTH
|
||||
# navigator.languages (the full list) AND the realm Intl default locale (the
|
||||
# primary tag it extracts) — so Intl.DateTimeFormat / NumberFormat /
|
||||
# toLocaleString follow the locale, not just the Accept-Language header. Seed
|
||||
# it with the full Accept-Language list so navigator.languages stays the
|
||||
# desktop-default 2 elements (["fr-FR","fr"]); the C++ DidSet takes "fr-FR"
|
||||
# for Intl. Mirrors juggler.timezone.override; the SOLE source of truth.
|
||||
prefs["juggler.locale.override"] = _accept_language(locale)
|
||||
|
||||
if timezone:
|
||||
# juggler.timezone.override is the SOLE source of truth read by the C++
|
||||
# timezone chain (BrowsingContext::Attach/DidSet, ContentChild). The old
|
||||
# zoom.stealth.timezone pref was declared in the yaml but read by NO
|
||||
# code — dropped here on 2026-06-10 (see 20-our-patches.md §8).
|
||||
prefs["juggler.timezone.override"] = timezone
|
||||
|
||||
# Cross-process seed (canvas noise + DWrite gamma share this). Only
|
||||
# zoom.stealth.fpp.hw_seed is read by the C++; the old zoom.stealth.seed
|
||||
# alias was never declared in the yaml and read by nothing — dropped
|
||||
# 2026-06-10. The render-noise seed is DECOUPLED from the identity seed and
|
||||
# drawn from a calibrated CLEAN pool: the canvas/WebGL render HASH it drives
|
||||
# is the dominant FP Pro tampering_ml signal, and some hw_seeds yield a
|
||||
# "suspicious" render hash. render_noise_seed() maps to the clean pool while
|
||||
# keeping per-seed determinism + diversity. See _webgl_personas.
|
||||
prefs["zoom.stealth.fpp.hw_seed"] = render_noise_seed(profile.seed)
|
||||
|
||||
# Synthetic host ICE candidate — injected by C++ when addr_ct==0 (SOCKS5
|
||||
# proxy suppresses all local addresses so Firefox can't gather host cands).
|
||||
# LAN IP is seed-derived so it's consistent per session and looks like a
|
||||
# real home router assignment (192.168.x.x range).
|
||||
_s = profile.seed
|
||||
_lan_ip = f"192.168.{(_s >> 8) % 254 + 1}.{_s % 254 + 1}"
|
||||
prefs["zoom.stealth.webrtc.host_ip"] = _lan_ip
|
||||
|
||||
# Windows/mac extension list:
|
||||
# - persona active → the coherent webgl1/webgl2 extension lists (in the GPU's real
|
||||
# native order) were ALREADY applied above from the GPU pool's `prefs`, alongside the
|
||||
# matching renderer + params + shader-precisions. Nothing to do here.
|
||||
# - no persona → clear so the host-real renderer reports its native extension set
|
||||
# (matches real vanilla captures for that host's GPU).
|
||||
if not sys.platform.startswith("linux") and not _persona:
|
||||
prefs["zoom.stealth.webgl.extensions"] = ""
|
||||
prefs["zoom.stealth.webgl2.extensions"] = ""
|
||||
|
||||
# Linux Xvfb workarounds (no-op on Windows).
|
||||
if sys.platform.startswith("linux"):
|
||||
for k, v in _LINUX_XVFB_WORKAROUNDS.items():
|
||||
prefs.setdefault(k, v)
|
||||
|
||||
# Windows virtual-desktop workarounds (headless=True on Windows).
|
||||
if virtual_display and sys.platform == "win32":
|
||||
for k, v in _WIN_VIRT_DESKTOP_WORKAROUNDS.items():
|
||||
prefs.setdefault(k, v)
|
||||
|
||||
# Caller overlay LAST so users can override anything we set. A value of
|
||||
# None is treated as a sentinel meaning "delete this pref entirely from
|
||||
# the final dict" — useful for A/B harnesses that need to test what
|
||||
# happens when an override is unset (vs set to empty string, which for
|
||||
# some prefs like general.useragent.override means literally empty UA).
|
||||
if extra_prefs:
|
||||
for k, v in extra_prefs.items():
|
||||
if v is None:
|
||||
prefs.pop(k, None)
|
||||
else:
|
||||
prefs[k] = v
|
||||
|
||||
return prefs
|
||||
"""Backward-compat shim — spostato in invisible_core.prefs (alias completo)."""
|
||||
import sys as _sys
|
||||
from invisible_core import prefs as _mod
|
||||
_sys.modules[__name__] = _mod
|
||||
|
|
|
|||
84
tests/test_backcompat.py
Normal file
84
tests/test_backcompat.py
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
"""Backward-compatibility guard for the invisible_core / invisible_playwright split.
|
||||
|
||||
After extracting the pure config into ``invisible_core`` and turning the moved
|
||||
modules into aliasing shims, EVERY import path that external users (and our own
|
||||
outreach PRs) rely on must keep working — same objects, same behavior. This test
|
||||
is the contract: if it breaks, we broke someone's install.
|
||||
"""
|
||||
import importlib
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_top_level_public_api_imports():
|
||||
import invisible_playwright as ip
|
||||
for name in [
|
||||
"InvisiblePlaywright", "ensure_binary", "ensure_geoip_mmdb",
|
||||
"get_default_stealth_prefs", "get_default_args",
|
||||
"resolve_session_timezone", "GeoTimezoneError",
|
||||
"BINARY_VERSION", "FIREFOX_UPSTREAM_VERSION", "__version__",
|
||||
]:
|
||||
assert hasattr(ip, name), f"invisible_playwright.{name} missing"
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_submodule_imports_external_users_rely_on():
|
||||
# These are the exact import shapes used in the wild + in our Skyvern/onyx/etc PRs.
|
||||
from invisible_playwright._fpforge import generate_profile, Profile # noqa: F401
|
||||
from invisible_playwright._webgl_personas import forced_gpu_class # noqa: F401
|
||||
from invisible_playwright.prefs import translate_profile_to_prefs # noqa: F401
|
||||
from invisible_playwright.download import ensure_binary # noqa: F401
|
||||
from invisible_playwright.constants import BINARY_VERSION # noqa: F401
|
||||
from invisible_playwright.config import get_default_stealth_prefs # noqa: F401
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_deep_submodule_and_private_names_still_import():
|
||||
from invisible_playwright._fpforge.profile import Profile # noqa: F401
|
||||
from invisible_playwright._fpforge import _sampler # noqa: F401
|
||||
from invisible_playwright.prefs import _BASELINE # noqa: F401
|
||||
assert isinstance(_BASELINE, dict)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_class_identity_is_shared_with_core():
|
||||
# isinstance must hold: the object returned by generate_profile must be an
|
||||
# instance of BOTH the invisible_playwright-path Profile and the core Profile
|
||||
# (they must be the SAME class object, not duplicates).
|
||||
from invisible_playwright._fpforge import generate_profile
|
||||
from invisible_playwright._fpforge import Profile as PW_Profile
|
||||
from invisible_playwright._fpforge.profile import Profile as PW_Profile2
|
||||
import invisible_core
|
||||
from invisible_core import Profile as Core_Profile
|
||||
|
||||
p = generate_profile(seed=42)
|
||||
assert isinstance(p, PW_Profile)
|
||||
assert isinstance(p, PW_Profile2)
|
||||
assert isinstance(p, Core_Profile)
|
||||
assert PW_Profile is Core_Profile
|
||||
assert PW_Profile2 is Core_Profile
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_shim_output_matches_core_output():
|
||||
from invisible_playwright._fpforge import generate_profile as gp_pw
|
||||
from invisible_playwright._webgl_personas import forced_gpu_class as fgc_pw
|
||||
from invisible_playwright.prefs import translate_profile_to_prefs as tp_pw
|
||||
import invisible_core as ic
|
||||
|
||||
a = tp_pw(gp_pw(42, fixed_gpu_class=fgc_pw(42)))
|
||||
b = ic.translate_profile_to_prefs(ic.generate_profile(42, fixed_gpu_class=ic.forced_gpu_class(42)))
|
||||
assert a == b
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_core_imports_without_playwright(monkeypatch):
|
||||
# invisible_core must be importable even if playwright is absent. We simulate
|
||||
# by asserting the core modules never pulled playwright into sys.modules as a
|
||||
# side effect of their own import (the wrapper may have, but core must not need it).
|
||||
import sys
|
||||
import invisible_core # noqa: F401
|
||||
# invisible_core itself must not hard-depend on playwright at import time.
|
||||
core_file = importlib.import_module("invisible_core").__file__
|
||||
assert "invisible_core" in core_file
|
||||
Loading…
Add table
Add a link
Reference in a new issue