mirror of
https://github.com/daijro/camoufox.git
synced 2026-10-04 08:00:19 +00:00
e425dddcfdd764957dee5e2f58d95ceedbcd257d
64
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e425dddcfd |
chore(python): remove dead helpers and a stale dependency
None of these had a caller: - pkgman: is_supported_path, extract_zip, cleanup and set_version, left over from the single-directory install. cleanup() would have deleted every installed browser version. - multiversion.get_cached_repo_names, CONSTRAINTS.as_range, fingerprints._load_os_voices, utils._clean_locals, and unused imports. Also: - The "Apify Fingerprints" row in `camoufox version`, which has read "?" since fpgen replaced BrowserForge. - lxml is no longer a dependency; nothing imports it. - The geoip extra now names maxminddb, the module geolocation.py actually imports, rather than getting it transitively through geoip2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
6daae88dbc |
Stock-Firefox parity for native identities: input, fonts, locale, WebGL, WebRTC, media, timing, launcher (#779)
* feat(humanize): replay recorded human mouse movements (Cursory) humanize=True used to walk a Bezier curve through two random knots and emit a point every 10 ms. Both halves are tells: an analytic curve sampled at a fixed rate has velocity and jerk profiles that separate cleanly from a hand's, and every movement accelerated through the same easing function. Juggler now picks one of Cursory's 2357 recorded human movements whose direction, distance and wander suit the move, morphs it onto the requested endpoints and replays it with the recording's own timing. The generator is cursory-js (a bit-exact TypeScript port of Vinyzu/cursory) vendored under additions/juggler/input/cursory/; it is LGPLv3-or-later, not MPL-2.0, and ships its LICENSE and NOTICE inside juggler.jar. MouseTrajectories.hpp and ChromeUtils.camouGetMouseTrajectory are removed. sendTrajectoryAcked takes per-step pauses, drops points on the pixel the last dispatch left the cursor on (a zero-displacement move is never acked), and the humanize guards are updated for the new path shape. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(patches): shared helpers for binary resolution, a private Xvfb and Marionette resolve_binary() honours the runner's CAMOUFOX_EXECUTABLE_PATH before falling back to a Linux objdir (search-service-init and touchscreen-digitizer ignored it and ran the newest objdir, which after a macOS cross build is an arm64 Mach-O), hidden_display() gives a guard its own Xvfb so nothing ever opens on the user's display, and a minimal chrome-context Marionette client lets guards inspect browser UI state. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(juggler): synthesized input carries what real mouse and keyboard input carries - pointerType was "" for every Playwright mouse event: juggler dispatched with MOZ_SOURCE_UNKNOWN. It now passes MOZ_SOURCE_MOUSE (#776). - keyboard.type() never pressed Shift: a shifted character now arrives bracketed by ShiftLeft keydown/keyup (location 1) with shiftKey set. - After the pointer was parked off content, pointerover/enter re-entered with buttons=1 and pressure 0.5; the tracked position is now forgotten on park. - A Windows identity gets contextmenu after mouseup with buttons=0, as Windows does; GTK/macOS keep it on press. - Wheel events are sent as line deltas (DOMMouseScroll.detail 3 per notch instead of the pixel count). - The browser rect is measured after the APZ flush await, so a chrome height change during the wait cannot put a y==0 dispatch one row above content. - ci/run_sundial.py moves and clicks the mouse so input vectors have data. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(juggler): evaluate() no longer grants user activation Upstream Playwright runs every evaluate() as handling user input and notifies a user-gesture activation. Init scripts go through that path at load, so every page started with navigator.userActivation.hasBeenActive === true, autoplay allowed and popups permitted before any input. Activation now only comes from juggler's trusted input events, as in a stock browser. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(juggler): stop hiding scrollbars in headless The headless agent sheet set scrollbar-width: none !important, which a page reads back from getComputedStyle and from overflow:scroll gutters. Scrollbar appearance is left to the platform look-and-feel (the launcher sets ui.useOverlayScrollbars per claimed OS). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(ui): no visible automation cues in the browser window - Every Playwright context was a public container, so the URL bar showed a "JUGGLER <id>" label and a container colour. Contexts are now non-public identities (tabbrowser renders public identities only); startup cleanup still removes persisted leftovers. - showcursor defaulted to true, drawing a red dot that followed the mouse. It is now opt-in. tests/patches/visible-automation-cues.py checks both on a private Xvfb. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(fonts): web fonts, local(), per-character fallback and native bundles - FontFace / @font-face were answered from the font allowlist by the FontFace's own family name, so every url() web font failed with NS_ERROR_FAILURE and never rendered, local() of an allowed font failed, and a miss rejected with an XPCOM code instead of NetworkError (#759). Stock FontFace/FontFaceImpl are restored; local() is filtered by the RESOLVED family in gfxUserFontSet. - GlobalFontFallback forced the cmap scan, which skips families whose charmap is not loaded yet, so any character outside Gecko's script-based common-fallback table rendered as the primary family's .notdef (U+1E9E on macOS). The platform fallback chooses again, and its choice is held to the mask. - For a native macOS/Windows identity the bundled font sets are not activated: a bundled face of a family the system also has (Papyrus, Helvetica) won the lookup with different metrics. On Windows the enumerator still keeps Twemoji Mozilla, the emoji font stock Firefox ships (flag emoji drew nothing). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(fonts): CSS2 system fonts and system-ui follow the claimed OS - The host's own OS gets no system-ui override (macOS resolved system-ui to Helvetica instead of -apple-system). - CSS2 system font keywords use per-keyword faces and sizes; a Linux identity reports the Ubuntu desktop font; Windows form controls (-moz-button/field/list) answer "MS Shell Dlg 2" as Windows does, not Segoe UI. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat(fonts): per-OS font model and fontconfig parity fonts.json is now generated from the bundle by scripts/gen-fonts-json.py (fc-scan + aliases + scan-time families, intersected with the per-OS manifest in scripts/data/font-manifests.json) so every reportable family is renderable; scripts/verify-fonts.py checks that invariant, the generics and the reject globs. font-groups.json lets the draw keep co-shipped families together. Linux fontconfig: stock metric aliases (Arial -> Liberation Sans, ...), 49-sansserif, urw-base35 and the non-Latin rule files in stock conf.d order, generics resolving like a stock Ubuntu (Noto Sans / Noto Serif / DejaVu Sans Mono / Z003), hintslight so advances are not pinned to whole pixels, and weak <prefer> lists instead of strongly-bound generic pins so lang can promote a script face. Windows fontconfig: GDI substitution aliases, MS Shell Dlg 2, cursive/fantasy generics, duplicate-face rejects and Sitka / Segoe UI Variable optical-size families. NOTE: generated against a ~3.9 GB target font bundle that is not part of this change (one file is over GitHub's 100 MB limit); see docs/FONTS.md. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(locale): localize browser strings with the spoofed locale; stop rewriting explicit locales - With locale="fr-FR", Intl/number/date went French but input.validationMessage and XML parse errors stayed English, a mix no real Firefox produces. Official language packs are now baked in as packaged locales (scripts/fetch-langpacks.py, scripts/inject-locales.py, called by package.py, fetched on demand) and the launcher selects the UI locale through intl.locale.requested. A langpack add-on cannot do this: the parent pre-creates those string bundles first. - locale-spoofing.patch overrode Language/Script/Region on every intl::Locale, so new Intl.DisplayNames(['en'],{type:'region'}).of('DE') returned the spoofed region's name and Intl.Locale('ja-Jpan-JP').minimize() returned the spoofed tag. Only the OS/default locale is spoofed now. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(media): enumerate, capture and label the identity's media devices coherently The fake media engine now enumerates the identity's microphones, cameras and speakers (labels and group ids from new mediaDevices:*Labels/*Groups config keys), MediaManager uses it whenever mediaDevices:enabled, and stock exposure rules apply: before a grant one device per input kind, no outputs, no labels; after a grant OS-style labels, distinct deviceIds, shared groupIds. So enumerateDevices(), getUserMedia() tracks and getSettings() ids agree, and a claimed camera captures instead of throwing NotFoundError. Fixes the content-process crash on an identity with a camera and no microphone (InsertElementAt on an empty array). docs/MEDIA-DEVICES.md; guard tests/patches/media-devices-coherence.py. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(navigator): globalPrivacyControl agrees between window and workers (#760) The main-thread Navigator getter ignored the config key that WorkerNavigator::GlobalPrivacyControl honours, so a page read false in the window and true in a worker. Both read the key the same way now; the launcher also mirrors it into privacy.globalprivacycontrol.enabled so the Sec-GPC header agrees. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(timezone): apply the launch-level timezone from the first read (#773) The timezone config key was only applied lazily from a navigator getter, so Intl and Date reported the host zone until a page happened to touch navigator. It is now applied eagerly in every process (nsJSContext::EnsureStatics) and per realm when a new inner window is created, entering that window's realm rather than whichever one triggered the navigation. window.setTimezone() still takes precedence per context. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(screen): the CSS color media feature follows the spoofed colorDepth screen.colorDepth was spoofed at the WebIDL level only, so on a 10-bit panel a 24-bit identity reported 24 with (color: 10), a pair Gecko cannot produce. Gecko_MediaFeatures_GetColorDepth now resolves the depth in the same order as nsScreen::PixelDepth. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(webgl): pass live state through instead of answering it from the table getParameter answered everything from the sampled table, so state a page had just set read back wrong (lineWidth(5) read 1, VIEWPORT/SCISSOR_BOX stayed 300x150 on a 64x64 canvas), extension parameters were null (anisotropy, draw buffers), COMPRESSED_TEXTURE_FORMATS was null instead of [], and getContextAttributes() ignored the attributes requested ({antialias:false} still reported 4 samples). Identity and limits still come from the table; live state and context attributes are real. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(webrtc): ICE gathering completes behind a proxy (#774) With Playwright's per-context proxy and media.peerconnection.ice.proxy_only_if_behind_proxy, ICE failed before gathering started and iceGatheringState stayed "new" forever, where stock Firefox completes with host candidates. When that happens around the fabricated candidates the new -> gathering -> complete state walk is replayed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore(patches): refresh window-setter-seal.patch offsets Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(windows): embed Firefox's application manifest in camoufox.exe config/rules.mk embeds <program>.manifest and browser/app only ships firefox.exe.manifest, so --with-app-name=camoufox produced an exe with no manifest. Without the Windows 10 supportedOS GUID the process and its children run as a pre-Windows-10 application and Gecko's Windows-10-gated paths switch off (MediaCapabilities.decodingInfo powerEfficient false for H.264/VP9 where stock is true). The new patch adds a byte-for-byte copy as camoufox.exe.manifest; the old rename hunk in windows-theming-bug-modified.patch is dropped. Guard: tests/patches/windows-exe-manifest.py. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(settings): stock values for page-observable prefs; launcher prefs at startup Page-observable defaults that no stock Firefox has, restored: - the forced built-in dark theme (it also removed the 1 px nav-bar separator, and was applied ~1 s after startup, resizing the viewport) and ui.systemUsesDarkTheme (prefers-color-scheme disagreed with the desktop); - focus rings off, autoplay allowed, popup blocker off; - gfx.color_management.mode=0 (Playwright's test pref: ICC-tagged images were drawn unconverted, readable from a canvas pixel); - ui.use_standins_for_native_colors (non-native system colours); - GMP updates off (Widevine/OpenH264 never available); - storage.estimate() quota derived from the raw disk instead of the stock cap. The HardwareAcceleration:false enterprise policy is removed: it locked software WebRender with no hardware video decoding on every OS (guard tests/patches/hardware-acceleration-policy.py). The minimal-theme chrome.css is emptied: its ~55 px chrome made outerHeight - innerHeight impossible. Playwright's non-persistent launch writes no user.js, so launcher prefs only arrived through juggler after startup and anything Gecko reads while starting raced (on Windows the UI locale lost 3 of 4 launches). camoufox.cfg now applies the launcher's CAMOU_PREFS_1..N env chunks as default prefs at startup (guard tests/patches/startup-prefs.py). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(branding): chrome://branding assets match stock Firefox chrome://branding/content/ is content-accessible. The wordmark SVGs had different intrinsic sizes (336x48 / 172x48 vs 300x67) and document.ico, document_pdf.svg and the private-browsing about logos were missing, all measurable from a page with an <img>. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(pythonlib): identity draws that match real machines and stay stable Launcher-side fixes found by comparing camoufox against stock Firefox 152.0.4 on Linux, Windows 11 and macOS hosts: - DNT / GPC: BrowserForge draws doNotTrack "1" on most Firefox samples, but a stock Firefox 152 reports "unspecified" and globalPrivacyControl false; the stock defaults are used unless the caller sets them, and both are applied as prefs so the API, the worker and the DNT / Sec-GPC headers agree (#760). - Timezone and geolocation: the timezone is passed to the browser, and a configured position sets permissions.default.geo so permissions.query agrees with the auto-grant (#769, #773). - hardwareConcurrency: the reported count is the fingerprint's and the browser is pinned to that many cores (cpu_affinity.py, Linux/Windows), so worker timing agrees with it; otherwise the host count snapped into the core counts real machines ship with (never 2, Firefox's resistFingerprinting value). - Fonts: the OS base is always present in full, OS-version variants are drawn all-or-nothing, co-shipped groups stay together, Cascadia is never claimed off Windows, a native macOS/Windows identity claims only the real OS base, and gfx.font_rendering.fallback.async is off on Linux so per-character fallback does not depend on cmap-load timing. - Speech voices: a per-OS installed-voice model (voice-manifests.json) with the voiceURI formats each backend really produces (voice-uris.json); no default voice where stock has none. - WebGL: extensions a release Firefox never exposes are filtered, but OVR_multiview2 stays for Windows D3D11 renderers, which expose it. - Media devices: a seeded draw of common per-OS devices with OS-style labels. - Windows scrollbars follow the drawn Windows version (overlay on 11). - Glyph-advance perturbation (fonts:spacing_seed) defaults to off: it moved every measureText width off the value the same font gives on a real machine. - Launcher prefs are also exported as CAMOU_PREFS_1..N so camoufox.cfg applies them at startup, and the browser UI locale follows the spoofed locale. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(pythonlib): per-identity salt for seeded draws; core pinning under concurrency Found in review of the previous commits: - identity_seed() hashed only the UA, platform, screen size and core count. Those take a handful of values per OS, so over 500 launches the seed took 12-30 distinct values and every install drew its fonts, voices, GPU, media devices and canvas/audio noise seeds from that same short list. The seed now mixes in identity_salt(): derived from what the caller pinned the identity with (a Fingerprint, a preset dict, a config naming the UA) so relaunching that identity reproduces every draw, and random otherwise. A pinned preset now reproduces its noise seeds too; seeds the caller sets are kept. - Concurrent AsyncNewBrowser launches on one driver interleaved pin/restore: one browser inherited the other's mask and the driver could stay pinned. pin -> launch -> restore is serialized per driver. - Every pinned browser landed on cores 0..N-1; pins now take N adjacent cores from a random start. - A pinnable host with 1-3 cores reported 1, 2 or 3 (2 is the resistFingerprinting value); the table floor of 4 applies as on other hosts. - launch_options() callers that launch the browser themselves (launch_server, direct use) kept the drawn core count although nothing pins the browser; only Camoufox/AsyncCamoufox pass pin_cpu_cores=True now, everyone else reports the host's snapped count. - PLAUSIBLE_CORE_COUNTS gains 18, 22, 28 and 32, all recorded in the -v150 corpus. - The Windows voice list was drawn before the locale was resolved, so an fr-FR identity got en-US voices; it is drawn after locale/geoip now. - macOS "Alex" gets its com.apple.speech.synthesis.voice identifier. - CAMOU_PREFS env chunks are ASCII-only JSON (Windows getenv goes through the ANSI code page). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(fonts): canvas accepts CSS2 system-font keywords; local() works on macOS - GetSpoofedSystemFontForRFP's per-OS branches returned before marking the result a system font. ComputeSystemFont copies that flag into FontFamilyList::is_system_font, and without it the canvas font setter could not serialize the value: ctx.font = 'caption' (or icon, menu, message-box, small-caption, status-bar) was silently ignored and read back '10px sans-serif' where stock reads back the keyword. - CoreTextFontList::LookupLocalFont builds a CTFontEntry with no family name, and local() sources are held to the spoofed font list by the resolved family, so on macOS every local() face (Helvetica, Menlo, Arial...) failed with NetworkError, installed and allowed or not. The entry now carries the family CoreText resolved. A blocked lookup's entry is released instead of leaked. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(webgl): only device limits come from the spoofed table getParameter still answered ~100 state pnames from the table, so state the page had just changed read back wrong: UNPACK_FLIP_Y_WEBGL / PREMULTIPLY_ALPHA / COLORSPACE_CONVERSION after pixelStorei, FRAGMENT_SHADER_DERIVATIVE_HINT after hint(), DRAW_BUFFERi after drawBuffers(), RED/ALPHA/DEPTH/STENCIL_BITS and IMPLEMENTATION_COLOR_READ_* for the bound framebuffer, and COMPRESSED_TEXTURE_ FORMATS after enabling an extension. UNMASKED_VENDOR/RENDERER_WEBGL came back without the extension enabled, where stock returns null with INVALID_ENUM. The table now answers only the MAX_*/ALIASED_*/SUBPIXEL_BITS limits, WebGL 2 limits on WebGL 2 contexts only, and extension limits (anisotropy, draw buffers, OVR multiview) only once that extension is enabled; everything else is the real context's answer. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(webrtc): fabricate candidates only where a real gather would have them With webrtc:ipv4/ipv6 set (every geoip launch), new RTCPeerConnection() with no iceServers produced a srflx carrying the spoofed IP, and after end-of-candidates a second host set with a different mDNS name. getStats() exposed that srflx as id 'camou-srflx' and rewrote every candidate address, including .local host names and the remote peer's candidates. A srflx is now fabricated only when the page configured an ICE server, host candidates only when none reached the page (sharing the real UDP host's port otherwise), the synthetic stats id has the shape real candidate ids have (8 hex digits, fixed per connection), and only this side's non-mDNS addresses are rewritten. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(media): honour mediaDevices:enabled=false; page fake:true behaves as stock - MaskConfig::GetBool returns std::optional<bool>, and the checks tested its presence: "mediaDevices:enabled": false still enabled the fake devices. - media.navigator.permission.fake=true was page-readable: a page's own getUserMedia({video: true, fake: true}) prompted and never resolved, where stock resolves at once with its generic fake device. The pref is off again; the identity's devices count as real hardware in the capturing checks instead (prompt, sharing indicator, post-grant labels), and a page's fake:true request gets stock's generic devices rather than the identity's. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(storage): per-context values are session state, and misses are cached - Values lived on the user pref branch, which a persistent profile writes to prefs.js: relaunching with a different timezone (or navigator values) kept reporting the previous session's in the page, iframes and workers. They now live on the default branch, which is never saved, and reads ignore user values an older build left behind. - A read of an unset key did a synchronous IPC to the parent every time, and in a launch without per-context values every read is unset: navigator.hardwareConcurrency, screen.* and (color) media queries measured ~20x slower than stock. A miss is now cached per key until a pref change or a local put clears it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(timezone): no per-realm override for the process-wide zone; cache DateTimeInfo With a launch-level timezone every new document and worker got a per-realm override of the zone the process already reported. Setting one releases all JIT code in the runtime (hot code after adding an iframe ran ~4x slower), and the realm rebuilt its DateTimeInfo on every call (getHours() ~40x slower than stock). The override is applied only when the zone differs from the one JS::SetTimeZoneOverride applied process-wide, and a realm keeps its DateTimeInfo until its override changes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(juggler): wheel scrolls in native notches; Shift leads the key it modifies - A wheel notch now reaches the page as its own 3-line event carrying one native tick (new WHEEL_EVENT_NATIVE_NOTCHES option in patches/wheel-native-ticks.patch), so wheelDelta is -120 per notch as with a physical wheel; it was -396, and a multi-notch scroll arrived as one event. Several notches are spaced a few tens of ms apart. - Auto-Shift pressed Shift 0 ms before the character's keydown and released it 0 ms after its keyup; it now leads and trails by a drawn human-scale delay, and a failing keydown no longer leaves Shift latched. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(settings): stock cookie partitioning, preconnect, popup notification; about dialog CSS - network.cookie.cookieBehavior 4 (Playwright's) -> Firefox's default 5. With 4 a cross-site iframe (captcha and anti-bot widgets are exactly that) sees document.hasStorageAccess() true and its first-party cookies and localStorage, where stock partitions them. Playwright set 4 so storageState need not carry thirdPartyCookie^ permissions. - network.http.speculative-parallel-limit 0 turned <link rel=preconnect> into a no-op, visible in Resource Timing. - privacy.popups.showBrowserMessage false: stock shows a notification bar for a blocked popup, which shrinks the viewport and fires resize. - chrome://branding/content/aboutDialog.css is page-loadable and was empty; it is the official branding's now. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(patches): stock-parity probes for the leaks found in review One launch (plus two persistent relaunches) checks page-observable invariants stock Firefox 152 holds: canvas CSS2 system-font keywords, WebGL state readback and UNMASKED_RENDERER without the extension, no srflx without ICE servers and no 'camou' stats id, getUserMedia({fake: true}), cross-site storage partitioning, wheel notches, per-read cost of (color)/hardwareConcurrency and of local Date getters under a launch timezone, and a persistent profile's timezone after relaunch (page and worker). Run against the build before these fixes it fails on every one of them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(humanize): judge cadence by distinct values and spread, not a share of the gap count The page clock is clamped to 1 ms and Cursory's recorded steps mostly sit between 12 and 20 ms, so the number of distinct gap values cannot grow with the number of gaps. Requiring len(gaps) // 4 made the guard fail on visibly uneven runs whenever event delivery was steady (3 of 4 runs once the per-read sync IPC jitter was gone). A fixed 10 ms cadence yields about three values within a few ms of each other, which the new rule (>= 6 values, >= 20 ms spread) still fails. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(juggler): restore the popup, wheel and history contracts The Playwright suite went red on this branch, and five of its six shards ran out their 40-minute budget before reporting, so ~470 sync tests were never run at all. Three causes, all ours: - window.open() from page.evaluate() returned null. The popup blocker being ON (Firefox's default) and evaluate() no longer granting user activation are each defensible alone; together they block every gesture-less popup. ~35 tests, each burning 30s x 4 attempts x 2 worlds, which is what exhausted the shards. The blocker goes back to Playwright's and geckodriver's value. Reading it costs a detector a popup window the user sees, so it is not a check an anti-bot script in the page runs -- unlike navigator.userActivation.hasBeenActive, which is one property read, and which is why the activation half stays. - mouse.wheel(0, 100) delivered deltaY 114 (or 132, depending on the host's font metrics) in deltaMode 1. Quantising into native wheel notches is what a physical wheel does, but it changes the number the caller asked for, so it now rides behind humanize= with the rest of the humanized input. Default is the exact requested delta in deltaMode 0. - page.go_back() did nothing after history.pushState(). canGoBack is the BACK BUTTON's answer: under browser.navigation.requireUserInteraction it reports false when every entry behind this one was pushed without the user touching the page, which is now every entry, because evaluate() grants no activation. goBack() itself does not skip those entries and neither does history.back(), so ask canGoBackIgnoringUserInteraction, as Marionette does. Two keyboard tests are skiplisted rather than fixed: auto-Shift means typing "!" emits the Shift a US keyboard requires, and upstream asserts the character's three events with shiftKey false throughout. The character's own key/code/keyCode are unchanged; what upstream asserts is the absence of a Shift no real typist could omit. Full suite against the fixed build: 2223 passed, 6 failed -- the two keyboard tests above, and four client-certificate tests that fail only on this machine (Node/OpenSSL rejects the fixture server) and pass in CI. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(pythonlib): stop overriding the corpus on core counts; pinning is opt-in Two findings from auditing the sweep's fixes against one bar: a difference is a leak only if a page's JavaScript can actually read it. hardwareConcurrency 2 was excluded from PLAUSIBLE_CORE_COUNTS because "2 is what Firefox reports under resistFingerprinting". That has not been true for years: RuntimeService::ClampedHardwareConcurrency hardcodes 4, and 8 on macOS, both of which are already in the table. The exclusion protected against nothing and cost every genuinely dual-core machine -- 20% of the macOS presets in the recorded corpus, 4.2% of Linux draws. It also made the small-host tail worse: a 3-core host reported 4, which cannot be pinned, so a page measured 3 while being told 4. At 2 the pin succeeds. pin_cpu_cores now defaults to False. What it buys is defence against a page timing N parallel workers; what it costs is a browser-wide CPU cap, a per-driver launch lock, and nothing at all on macOS. Unpinned, the host's own snapped count is reported, so reported and measurable still agree -- the identity just loses one drawn value. Callers who want the draw kept can still ask for it. The WebGL sampler keeps rejecting software rasterisers, and its docstring now says so: it described the opposite of what the code does. llvmpipe as the presented GPU is a live check on a string every fingerprint script reads, which is worth ~1.5% of corpus fidelity. 251 pythonlib tests pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(pythonlib): keep 2 out of the core table -- an Apple M1 is never dual-core Reverts the PLAUSIBLE_CORE_COUNTS half of |
||
|
|
eb5dc3bc5b |
chore(release): v152.0.4-beta.31, pythonlib 0.5.6
Browser: beta.31, cut from the integration merge in
|
||
|
|
b68a4fb940 |
chore(release): mark pythonlib 0.5.6 as a pre-release (0.5.6b1)
Pairs the library with the browser: v152.0.4-beta.30 is published as a GitHub
pre-release, so the library that requires it should be one too.
PEP 440 puts 0.5.6b1 after 0.5.5 and before 0.5.6, and pip skips pre-releases
by default -- so `pip install camoufox` still resolves 0.5.5, and only
`pip install --pre camoufox` or an explicit pin picks this up. That is what
makes the conditional browser floor safe to exercise in the wild: the users who
opt in are the ones who get moved to beta.30.
Verified against the live release: with Playwright 1.61 installed the effective
floor resolves to beta.30 and the fetcher selects the real published asset
(camoufox-152.0.4-beta.30-lin.x86_64.zip). 172 tests pass, the 3.8 vermin gate
holds, and the package builds as camoufox-0.5.6b1.
Known wrinkle, not introduced here: _parse_semver() does int("6b1"), fails, and
substitutes 0, so 0.5.6b1 parses to (0, 5, 0). The browser constraint still
resolves correctly because repos.yml's only entry is min 0.5.0 / max 1, but a
future entry gating on a patch version would silently miss a pre-release
install. Worth making that parser PEP 440-aware separately.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
df35ae79d2 |
chore(release): v152.0.4-beta.30, pythonlib 0.5.6
Raises the browser floor to beta.30 because 0.5.6 permits Playwright >= 1.61, which sends viewport isMobile/screenSize in Browser.setDefaultViewport. Only beta.30's Protocol.js schema accepts those; on beta.29 every new_context() fails with "Protocol error (Browser.setDefaultViewport)". Measured: 1.60 works on both builds, 1.61 and 1.62 fail on beta.29 and pass on beta.30. The floor means pythonlib 0.5.6 cannot run until the beta.30 release assets are published -- it must not reach PyPI first. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
17f873abc8 |
build(deps): raise the playwright cap to <1.63 instead of removing it
The cherry-picked #742 replaced `playwright = "<1.61"` with `"*"`. The breakage it fixes is real, but an unbounded range removes the tripwire rather than making the browser forward-compatible: `camoufox.server` imports `playwright._impl._driver.compute_driver_executable`, a private API with no compat guarantee, and `additions/juggler` is a fork of one Playwright vintage that every minor release is free to break again -- 1.61 sending `viewport.isMobile` is exactly that, and it will recur. So the cap moves up rather than away, to the tested-current 1.62.0. No lower bound is added: this package has never carried one, and the launch path was exercised against 1.53.0 as well as the 1.62.0 the Playwright suite runs on. build-tester/requirements.txt mirrors this pin by its own comment, so it moves with it. |
||
|
|
2b662a8fc3 |
Support Playwright 1.61+.
- Remove package pin to allow Playwright >=1.61. - Adds WebSocket frame timestamps to Juggler events. - Extends viewport and setViewportSize protocol data with screenSize, isMobile, and deviceScaleFactor. - Adds WebP screenshot support, including a default quality of 100. - Updates the protocol schemas to describe the new fields and screenshot format. Playwright 1.61 sends viewport.isMobile in Browser.setDefaultViewport, which the juggler's protocol schema rejected -- every context creation failed, so the whole Playwright suite errored out at fixture setup rather than reporting results. This fixes the schema rather than capping the version. Cherry-picked from daijro/camoufox#742 (closes #653). Co-authored-by: LamerLink <36551116+LamerLink@users.noreply.github.com> |
||
|
|
cd83f7fd2f | Bump python library to 0.5.5 | ||
|
|
479453d4e1 | Bump pythonlib to 0.5.4 | ||
|
|
022b7f1f8e | Bump pythonlib to 0.5.3 | ||
|
|
195298d004 |
Temporarily cap Playwright dependency #653
Restrict playwright version to less than 1.61 |
||
|
|
3911d11dbd |
Support for releases with the same version and build v0.5.2
Fix for JWriter20 repo having several releases with the same version and build name, which broke the manager - Installs browsers as version-build-sha256/ rather than version-build/ since assets can share a version-build combo - Doesn't break existing downloads - Show date column in gui and selector tui |
||
|
|
fdaffc03e9 | Bump pythonlib to 0.5.1 | ||
|
|
61a9e1f9bb | Update browserforge (remove manual model downloading) | ||
|
|
e0e2eeb2f3 | Add version manager in python library | ||
|
|
df51462c43 |
Set minimum Python to 3.10 to solve Python 3.14 compatibility
When installing package using Poetry on Python 3.14 we have compilation
errors:
```
copying src/greenlet/platform/setup_switch_x64_masm.cmd -> build/lib.linux-x86_64-cpython-314/greenlet/platform
| copying src/greenlet/platform/switch_aarch64_gcc.h -> build/lib.linux-x86_64-cpython-314/greenlet/platform
| copying src/greenlet/platform/switch_alpha_unix.h -> build/lib.linux-x86_64-cpython-314/greenlet/platform
| copying src/greenlet/platform/switch_amd64_unix.h -> build/lib.linux-x86_64-cpython-314/greenlet/platform
| copying src/greenlet/platform/switch_arm32_gcc.h -> build/lib.linux-x86_64-cpython-314/greenlet/platform
| copying src/greenlet/platform/switch_arm32_ios.h -> build/lib.linux-x86_64-cpython-314/greenlet/platform
| copying src/greenlet/platform/switch_arm64_masm.asm -> build/lib.linux-x86_64-cpython-314/greenlet/platform
| copying src/greenlet/platform/switch_arm64_masm.obj -> build/lib.linux-x86_64-cpython-314/greenlet/platform
| copying src/greenlet/platform/switch_arm64_msvc.h -> build/lib.linux-x86_64-cpython-314/greenlet/platform
| copying src/greenlet/platform/switch_csky_gcc.h -> build/lib.linux-x86_64-cpython-314/greenlet/platform
| copying src/greenlet/platform/switch_loongarch64_linux.h -> build/lib.linux-x86_64-cpython-314/greenlet/platform
| copying src/greenlet/platform/switch_m68k_gcc.h -> build/lib.linux-x86_64-cpython-314/greenlet/platform
| copying src/greenlet/platform/switch_mips_unix.h -> build/lib.linux-x86_64-cpython-314/greenlet/platform
| copying src/greenlet/platform/switch_ppc64_aix.h -> build/lib.linux-x86_64-cpython-314/greenlet/platform
| copying src/greenlet/platform/switch_ppc64_linux.h -> build/lib.linux-x86_64-cpython-314/greenlet/platform
| copying src/greenlet/platform/switch_ppc_aix.h -> build/lib.linux-x86_64-cpython-314/greenlet/platform
| copying src/greenlet/platform/switch_ppc_linux.h -> build/lib.linux-x86_64-cpython-314/greenlet/platform
| copying src/greenlet/platform/switch_ppc_macosx.h -> build/lib.linux-x86_64-cpython-314/greenlet/platform
| copying src/greenlet/platform/switch_ppc_unix.h -> build/lib.linux-x86_64-cpython-314/greenlet/platform
| copying src/greenlet/platform/switch_riscv_unix.h -> build/lib.linux-x86_64-cpython-314/greenlet/platform
| copying src/greenlet/platform/switch_s390_unix.h -> build/lib.linux-x86_64-cpython-314/greenlet/platform
| copying src/greenlet/platform/switch_sh_gcc.h -> build/lib.linux-x86_64-cpython-314/greenlet/platform
| copying src/greenlet/platform/switch_sparc_sun_gcc.h -> build/lib.linux-x86_64-cpython-314/greenlet/platform
| copying src/greenlet/platform/switch_x32_unix.h -> build/lib.linux-x86_64-cpython-314/greenlet/platform
| copying src/greenlet/platform/switch_x64_masm.asm -> build/lib.linux-x86_64-cpython-314/greenlet/platform
| copying src/greenlet/platform/switch_x64_masm.obj -> build/lib.linux-x86_64-cpython-314/greenlet/platform
| copying src/greenlet/platform/switch_x64_msvc.h -> build/lib.linux-x86_64-cpython-314/greenlet/platform
| copying src/greenlet/platform/switch_x86_msvc.h -> build/lib.linux-x86_64-cpython-314/greenlet/platform
| copying src/greenlet/platform/switch_x86_unix.h -> build/lib.linux-x86_64-cpython-314/greenlet/platform
| copying src/greenlet/tests/_test_extension.c -> build/lib.linux-x86_64-cpython-314/greenlet/tests
| copying src/greenlet/tests/_test_extension_cpp.cpp -> build/lib.linux-x86_64-cpython-314/greenlet/tests
| running build_ext
| building 'greenlet._greenlet' extension
| creating build/temp.linux-x86_64-cpython-314/src/greenlet
| c++ -pthread -fno-strict-overflow -Wsign-compare -Wunreachable-code -DNDEBUG -g -O3 -Wall -O3 -fPIC -fPIC -I/tmp/tmp55zvtgm3/.venv/include -I/home/jduponchelle/.local/share/uv/python/cpython-3.14.2-linux-x86_64-gnu/include/python3.14 -c src/greenlet/greenlet.cpp -o build/temp.linux-x86_64-cpython-314/src/greenlet/greenlet.o
| In file included from src/greenlet/greenlet.cpp:31:
| src/greenlet/TGreenlet.cpp: In member function ‘void greenlet::Greenlet::expose_frames()’:
| src/greenlet/TGreenlet.cpp:626:29: error: aggregate ‘_PyInterpreterFrame iframe_copy’ has incomplete type and cannot be defined
| 626 | _PyInterpreterFrame iframe_copy;
| | ^~~~~~~~~~~
| src/greenlet/TGreenlet.cpp:627:65: error: invalid application of ‘sizeof’ to incomplete type ‘_PyInterpreterFrame’
| 627 | this->stack_state.copy_from_stack(&iframe_copy, iframe, sizeof(*iframe));
| | ^~~~~~~~~~~~~~~
| src/greenlet/TGreenlet.cpp:628:14: error: ‘_PyFrame_IsIncomplete’ was not declared in this scope
| 628 | if (!_PyFrame_IsIncomplete(&iframe_copy)) {
| | ^~~~~~~~~~~~~~~~~~~~~
| src/greenlet/TGreenlet.cpp:654:24: error: invalid use of incomplete type ‘_PyInterpreterFrame’ {aka ‘struct _PyInterpreterFrame’}
| 654 | if (!iframe->frame_obj) {
| | ^~
| In file included from /home/jduponchelle/.local/share/uv/python/cpython-3.14.2-linux-x86_64-gnu/include/python3.14/pyframe.h:19,
| from /home/jduponchelle/.local/share/uv/python/cpython-3.14.2-linux-x86_64-gnu/include/python3.14/Python.h:113,
| from src/greenlet/greenlet.cpp:16:
| /home/jduponchelle/.local/share/uv/python/cpython-3.14.2-linux-x86_64-gnu/include/python3.14/cpython/pyframe.h:25:8: note: forward declaration of ‘_PyInterpreterFrame’ {aka ‘struct _PyInterpreterFrame’}
| 25 | struct _PyInterpreterFrame;
| | ^~~~~~~~~~~~~~~~~~~
| src/greenlet/TGreenlet.cpp:656:37: error: aggregate ‘_PyInterpreterFrame dummy_iframe’ has incomplete type and cannot be defined
| 656 | _PyInterpreterFrame dummy_iframe;
| | ^~~~~~~~~~~~
| src/greenlet/TGreenlet.cpp:661:38: error: ‘FRAME_OWNED_BY_GENERATOR’ was not declared in this scope
| 661 | dummy_iframe.owner = FRAME_OWNED_BY_GENERATOR;
| | ^~~~~~~~~~~~~~~~~~~~~~~~
| src/greenlet/TGreenlet.cpp:686:45: error: invalid use of incomplete type ‘_PyInterpreterFrame’ {aka ‘struct _PyInterpreterFrame’}
| 686 | memcpy(&last_complete_iframe->frame_obj->_f_frame_data[0],
| | ^~
| /home/jduponchelle/.local/share/uv/python/cpython-3.14.2-linux-x86_64-gnu/include/python3.14/cpython/pyframe.h:25:8: note: forward declaration of ‘_PyInterpreterFrame’ {aka ‘struct _PyInterpreterFrame’}
| 25 | struct _PyInterpreterFrame;
| | ^~~~~~~~~~~~~~~~~~~
| src/greenlet/TGreenlet.cpp:687:45: error: invalid use of incomplete type ‘_PyInterpreterFrame’ {aka ‘struct _PyInterpreterFrame’}
| 687 | &last_complete_iframe->previous, sizeof(void *));
| | ^~
| /home/jduponchelle/.local/share/uv/python/cpython-3.14.2-linux-x86_64-gnu/include/python3.14/cpython/pyframe.h:25:8: note: forward declaration of ‘_PyInterpreterFrame’ {aka ‘struct _PyInterpreterFrame’}
| 25 | struct _PyInterpreterFrame;
| | ^~~~~~~~~~~~~~~~~~~
| src/greenlet/TGreenlet.cpp:688:37: error: invalid use of incomplete type ‘_PyInterpreterFrame’ {aka ‘struct _PyInterpreterFrame’}
| 688 | last_complete_iframe->previous = iframe;
| | ^~
| /home/jduponchelle/.local/share/uv/python/cpython-3.14.2-linux-x86_64-gnu/include/python3.14/cpython/pyframe.h:25:8: note: forward declaration of ‘_PyInterpreterFrame’ {aka ‘struct _PyInterpreterFrame’}
| 25 | struct _PyInterpreterFrame;
| | ^~~~~~~~~~~~~~~~~~~
| src/greenlet/TGreenlet.cpp:705:37: error: invalid use of incomplete type ‘_PyInterpreterFrame’ {aka ‘struct _PyInterpreterFrame’}
| 705 | memcpy(&last_complete_iframe->frame_obj->_f_frame_data[0],
| | ^~
| /home/jduponchelle/.local/share/uv/python/cpython-3.14.2-linux-x86_64-gnu/include/python3.14/cpython/pyframe.h:25:8: note: forward declaration of ‘_PyInterpreterFrame’ {aka ‘struct _PyInterpreterFrame’}
| 25 | struct _PyInterpreterFrame;
| | ^~~~~~~~~~~~~~~~~~~
| src/greenlet/TGreenlet.cpp:706:37: error: invalid use of incomplete type ‘_PyInterpreterFrame’ {aka ‘struct _PyInterpreterFrame’}
| 706 | &last_complete_iframe->previous, sizeof(void *));
| | ^~
| /home/jduponchelle/.local/share/uv/python/cpython-3.14.2-linux-x86_64-gnu/include/python3.14/cpython/pyframe.h:25:8: note: forward declaration of ‘_PyInterpreterFrame’ {aka ‘struct _PyInterpreterFrame’}
| 25 | struct _PyInterpreterFrame;
| | ^~~~~~~~~~~~~~~~~~~
| src/greenlet/TGreenlet.cpp:707:29: error: invalid use of incomplete type ‘_PyInterpreterFrame’ {aka ‘struct _PyInterpreterFrame’}
| 707 | last_complete_iframe->previous = nullptr;
| | ^~
| /home/jduponchelle/.local/share/uv/python/cpython-3.14.2-linux-x86_64-gnu/include/python3.14/cpython/pyframe.h:25:8: note: forward declaration of ‘_PyInterpreterFrame’ {aka ‘struct _PyInterpreterFrame’}
| 25 | struct _PyInterpreterFrame;
| | ^~~~~~~~~~~~~~~~~~~
| In file included from src/greenlet/greenlet.cpp:36:
| src/greenlet/TPythonState.cpp: In member function ‘void greenlet::PythonState::operator<<(const PyThreadState*)’:
| src/greenlet/TPythonState.cpp:137:31: error: ‘Py_C_RECURSION_LIMIT’ was not declared in this scope
| 137 | this->c_recursion_depth = Py_C_RECURSION_LIMIT - tstate->c_recursion_remaining;
| | ^~~~~~~~~~~~~~~~~~~~
| src/greenlet/TPythonState.cpp:137:62: error: ‘const PyThreadState’ {aka ‘const struct _ts’} has no member named ‘c_recursion_remaining’; did you mean ‘py_recursion_remaining’?
| 137 | this->c_recursion_depth = Py_C_RECURSION_LIMIT - tstate->c_recursion_remaining;
| | ^~~~~~~~~~~~~~~~~~~~~
| | py_recursion_remaining
| src/greenlet/TPythonState.cpp: In member function ‘void greenlet::PythonState::unexpose_frames()’:
| src/greenlet/TPythonState.cpp:179:51: error: invalid use of incomplete type ‘_PyInterpreterFrame’ {aka ‘struct _PyInterpreterFrame’}
| 179 | _PyInterpreterFrame *prev_exposed = iframe->previous;
| | ^~
| /home/jduponchelle/.local/share/uv/python/cpython-3.14.2-linux-x86_64-gnu/include/python3.14/cpython/pyframe.h:25:8: note: forward declaration of ‘_PyInterpreterFrame’ {aka ‘struct _PyInterpreterFrame’}
| 25 | struct _PyInterpreterFrame;
| | ^~~~~~~~~~~~~~~~~~~
| src/greenlet/TPythonState.cpp:181:23: error: invalid use of incomplete type ‘_PyInterpreterFrame’ {aka ‘struct _PyInterpreterFrame’}
| 181 | memcpy(&iframe->previous, &iframe->frame_obj->_f_frame_data[0],
| | ^~
| /home/jduponchelle/.local/share/uv/python/cpython-3.14.2-linux-x86_64-gnu/include/python3.14/cpython/pyframe.h:25:8: note: forward declaration of ‘_PyInterpreterFrame’ {aka ‘struct _PyInterpreterFrame’}
| 25 | struct _PyInterpreterFrame;
| | ^~~~~~~~~~~~~~~~~~~
| src/greenlet/TPythonState.cpp:181:42: error: invalid use of incomplete type ‘_PyInterpreterFrame’ {aka ‘struct _PyInterpreterFrame’}
| 181 | memcpy(&iframe->previous, &iframe->frame_obj->_f_frame_data[0],
| | ^~
| /home/jduponchelle/.local/share/uv/python/cpython-3.14.2-linux-x86_64-gnu/include/python3.14/cpython/pyframe.h:25:8: note: forward declaration of ‘_PyInterpreterFrame’ {aka ‘struct _PyInterpreterFrame’}
| 25 | struct _PyInterpreterFrame;
| | ^~~~~~~~~~~~~~~~~~~
| src/greenlet/TPythonState.cpp: In member function ‘void greenlet::PythonState::operator>>(PyThreadState*)’:
| src/greenlet/TPythonState.cpp:212:13: error: ‘PyThreadState’ {aka ‘struct _ts’} has no member named ‘c_recursion_remaining’; did you mean ‘py_recursion_remaining’?
| 212 | tstate->c_recursion_remaining = Py_C_RECURSION_LIMIT - this->c_recursion_depth;
| | ^~~~~~~~~~~~~~~~~~~~~
| | py_recursion_remaining
| src/greenlet/TPythonState.cpp:212:37: error: ‘Py_C_RECURSION_LIMIT’ was not declared in this scope
| 212 | tstate->c_recursion_remaining = Py_C_RECURSION_LIMIT - this->c_recursion_depth;
| | ^~~~~~~~~~~~~~~~~~~~
| error: command '/usr/bin/c++' failed with exit code 1
```
This due to the fact we are not installing a recent greenlet version
(3.1.1) due to the Python restriction.
By upgrading the minium Python version to 3.10 (minimum version
supported by the PSF https://devguide.python.org/versions/) the greenlet
module is now 3.3.1, it's the last available release.
|
||
|
|
0dabcb726d | Add leak warning for block_images 0.4.11 #170 | ||
|
|
a9b1934790 | pythonlib: Add COOP toggle (workaround for #150) 0.4.10 | ||
|
|
e3d3dcd6e7 | pythonlib: Look for assets in earlier releases #134 0.4.9 | ||
|
|
9a9e61f660 |
pythonlib: Force Browserforge 1.2.1+
Apify's models have been contaminated in recent releases. Browserforge 1.2.1 reverts to a earlier model. https://github.com/daijro/browserforge/issues/17 |
||
|
|
2422d622eb | pythonlib: Auto offset Canvas anti-aliasing | ||
|
|
2b7334c24f |
pythonlib: Bump to 0.4.7
Bugfix release including the fix for #114 by iSuslov. |
||
|
|
4c52518dd2 |
pythonlib: Cleanup & bump to 0.4.6
- `camoufox test` will no longer highlight the cursor by default - Fixed launch_options blocking async - WebGL database cleanup & added ability to query all possible vendor/renderer pairs |
||
|
|
31963aa83b | pythonlib: Update WebGL sample database | ||
|
|
f6ef52a39b | pythonlib: Support for main world evaluation 0.4.5 | ||
|
|
267b285faa | pythonlib: Bump to 0.4.4 | ||
|
|
cf3f8e6bde | pythonlib: Fix WebGL injection causing crashing 0.4.4-beta | ||
|
|
145b737069 |
pythonlib: [Rollback] Disable WebGL by default #90 0.4.3
Disables WebGL by default until a fix is available. |
||
|
|
01aff40fc5 |
pythonlib: Rollback WebGL fingerprint injection 0.4.2
Rolling back WebGL injection temporarily until the crashing issue on certain devices is fixed. |
||
|
|
673fdc7271 | pythonlib: Hotfix imports 0.4.1 | ||
|
|
af937cce55 | pythonlib: WebGL rotation & leak fixes 0.4.0 | ||
|
|
01291d2ad1 | pythonlib: Bump to 0.3.10 | ||
|
|
3a33283cd2 |
pythonlib: Add enable_cache, fixed font spacing, etc 0.3.9
- Enable bf cache with `enable_cache=True` #74 - Font spacing is now fixed per session #63 - Re-download (or raise error) when target install path is empty. Caused when the user cancels the download. - Bumped minimum version to beta.15 |
||
|
|
964f49028c | pythonlib: Fix broken import #70 0.3.8 | ||
|
|
e6beecb3a9 | pythonlib: Bump to 0.3.7 | ||
|
|
33189cd5b4 | pythonlib: Fully fix #61 0.3.6 | ||
|
|
692e8a162c | pythonlib: Support Python 3.8 #61 0.3.5 | ||
|
|
347885e9cb |
pythonlib: Default to http schema #57 0.3.4
Assume the proxy is using http schema when no other schema is specified. |
||
|
|
f94452fa93 |
pythonlib: Error handling for invalid addon paths & proxies 0.3.3
Help prevent confusion with using Camoufox addons #55 |
||
|
|
732d7bac8d |
Hotfix #51 0.3.2
Fixes missing VirtualDisplay import on non-Linux systems. |
||
|
|
75ea7b0880 |
pythonlib: Set window size & kill Xvfb on browser close #49 #50
- Added window: Tuple[int, int] argument to set a fixed width and height #50 - Experimental fix to automatically terminate Xvfb on browser.close() #49 - Bump to 0.3.1 |
||
|
|
0ff90fc750 |
Version range control, multi locale usage, etc
- Python library now constrains the supported Camoufox version, and will force an update if you are out of date. - Added support patch for multiple accepted languages #37 - Added pysocks #43 - Added README deprecation notices - Added public launch_options command - Bumped python library to 0.3.0 - Full support for beta.12 |
||
|
|
216718845c |
pythonlib: Add dict expected type 0.2.15
Adds support for beta.12. |
||
|
|
c448ec81ab |
More warnings & reformat README to avoid confusion/misuse 0.2.14
- Warns the user if they try to manually set the geolocation and locale. - Move manual config documentation to the bottom of the README to discourage use. - Make the parameters list more obvious. |
||
|
|
deddba00c3 |
Allow passing in a region or language into locale 0.2.13
- Passing in a region like `locale='US'` will generate a language based on the region, "US". - Passing in a language like `locale='en'` will generate a region based on the language, "en". - Fix locale parsing errors when generating using territoryInfo.xml #36 |
||
|
|
3e2551e65b | pythonlib: Fix geolocation domain detection #31 0.2.12 | ||
|
|
76bedb5251 |
pythonlib: Hotfix screen property out of bounds 0.2.11
Adds back handling for when Browserforge screen properties are out of bounds. |
||
|
|
b2f74a24f3 |
Update pythonlib & README for beta.11
- Removed warnings for using headless mode - Xvfb is now activated with headless='virtual' - Correctly pass window.screenX & calculate window.screenY - Update documentation on main README and pythonlib README - Bumped pythonlib to 0.2.10 |
||
|
|
bd59304481 |
pythonlib: Add Xvfb integration #26 0.2.9
- Automatically uses virtual displays on Linux - Update documentation on README |
||
|
|
351e99ed18 |
pythonlib: Add more leak warnings 0.2.8
Warns the user for when the passed parameters are likely to cause a leak. |