mirror of
https://github.com/daijro/camoufox.git
synced 2026-10-04 08:00:19 +00:00
WebGL vendor, renderer, context attributes, extensions, parameters and shader precisions, for WebGL1 and WebGL2, now come from fpgen's recorded Firefox devices instead of webgl_data.db, which is deleted with the camoufox/webgl/ package. camoufox/webgl.py: - webgl_for_gpu() traces `webgl` given Firefox, the OS and the GPU, then `webgl2` given the chosen `webgl` too, and draws each with one seeded random.Random. The GPU and the webgl value are pinned by their fpgen lookup index: a dict condition is flattened into leaves that overwrite each other, so only the renderer applied and Linux "Mesa" and "AMD" Radeon HD 3200 devices came back mixed. - sample_webgl_for_screen() draws the GPU of a generated identity from fpgen's per-OS weights, filtering out software rasterisers, GPUs the OS cannot report, discrete GPUs behind a netbook screen and the resistFingerprinting "Mozilla" mask before the weighted choice, so there is no rejection loop. An empty pool raises. - The draft/host-dependent extension filter moves over unchanged. A preset's GPU and a caller's webgl_config pair are looked up as given; a pair fpgen has never seen from Firefox on that OS raises instead of falling back to another GPU. generate_context_fingerprint no longer falls back to the host GPU when the draw fails. For 10 of the 15 (GPU, OS) pairs the two sources share, one of fpgen's records converts to exactly the database row on every value the browser reads. The other five rows (Linux R9 200 and Radeon HD 3200, macOS Intel HD, and two software rasterisers) are devices fpgen does not carry; those GPUs now present fpgen's recorded devices instead. The Linux GTX 980 row is kept as a test fixture. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
83 lines
2.9 KiB
Python
83 lines
2.9 KiB
Python
"""Every place an identity falls back to a substitute value says so.
|
|
|
|
A substitute is a value the rest of the identity was not drawn to match, which
|
|
a page can see. Each site warns with a report block the user can paste into a
|
|
GitHub issue, so the failure reaches us instead of shipping silently.
|
|
"""
|
|
|
|
import pytest
|
|
from test_identity_salt import launch
|
|
|
|
from camoufox import fingerprints as fp
|
|
from camoufox import utils
|
|
from camoufox._warnings import FallbackWarning
|
|
|
|
REPORT = r"Please report this at https://github\.com/daijro/camoufox/issues/new"
|
|
|
|
|
|
def _fail(error):
|
|
def raiser(*_args, **_kwargs):
|
|
raise error
|
|
|
|
return raiser
|
|
|
|
|
|
def _preset():
|
|
preset = fp.load_presets("152")["presets"]["windows"][0]
|
|
return {**preset, "fonts": ["Arial"]}
|
|
|
|
|
|
def _report(record):
|
|
(warning,) = [w for w in record if w.category is FallbackWarning]
|
|
text = str(warning.message)
|
|
assert "camoufox:" in text and "python:" in text and "os:" in text
|
|
return text
|
|
|
|
|
|
def test_preset_font_draw(monkeypatch):
|
|
monkeypatch.setattr(fp, "_generate_random_font_subset", _fail(OSError("fonts.json missing")))
|
|
with pytest.warns(FallbackWarning, match=REPORT) as record:
|
|
config = fp.from_preset(_preset(), "152")
|
|
assert "OSError: fonts.json missing" in _report(record)
|
|
assert "Arial" in config["fonts"]
|
|
|
|
|
|
def test_preset_voice_draw(monkeypatch):
|
|
monkeypatch.setattr(fp, "_generate_random_voice_subset", _fail(ValueError("bad manifest")))
|
|
with pytest.warns(FallbackWarning, match=REPORT) as record:
|
|
fp.from_preset(_preset(), "152")
|
|
assert "ValueError: bad manifest" in _report(record)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"target, error, key",
|
|
[
|
|
("_generate_random_font_subset", OSError("fonts.json missing"), "fonts"),
|
|
("_generate_random_voice_subset", ValueError("bad manifest"), "voices"),
|
|
],
|
|
)
|
|
def test_context_draws(monkeypatch, target, error, key):
|
|
monkeypatch.setattr(fp, target, _fail(error))
|
|
with pytest.warns(FallbackWarning, match=REPORT) as record:
|
|
context = fp.generate_context_fingerprint(os="linux")
|
|
assert f"{type(error).__name__}: {error}" in _report(record)
|
|
assert key not in context["config"]
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"loader, cache", [("_load_font_groups", "_FONT_GROUPS_CACHE"), ("_load_font_bases", "_FONT_BASES_CACHE")]
|
|
)
|
|
def test_font_data_loaders(monkeypatch, tmp_path, loader, cache):
|
|
monkeypatch.setattr(fp, cache, None)
|
|
monkeypatch.setattr(fp, "__file__", str(tmp_path / "fingerprints.py"))
|
|
with pytest.warns(FallbackWarning, match=REPORT) as record:
|
|
assert getattr(fp, loader)() == {}
|
|
assert "FileNotFoundError" in _report(record)
|
|
|
|
|
|
def test_launch_font_draw(monkeypatch):
|
|
monkeypatch.setattr(utils, "_generate_random_font_subset", _fail(OSError("font-bases.json missing")))
|
|
with pytest.warns(FallbackWarning, match=REPORT):
|
|
config = launch()
|
|
assert config["fonts"]
|