diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5adc552b0..1c47d0525 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,6 +2,11 @@ name: Build and Release on: workflow_dispatch: + inputs: + fail_on_leaks: + description: "Fail the verify job when a suite comes back dirty" + type: boolean + default: false push: tags: - "*" @@ -9,19 +14,32 @@ on: jobs: build: runs-on: ubuntu-24.04 + # The build system lives in browser/; python/ and typescript/ are the + # launcher packages and are not part of this job. + defaults: + run: + working-directory: browser strategy: + # One target failing to cross-compile must not cancel the others: these + # are multi-hour builds, and the `verify` job below already documents + # this as the contract it relies on. Without it the matrix default + # (fail-fast: true) throws away two nearly-finished builds to report a + # failure that is already reported. + fail-fast: false matrix: - target: [linux, windows, macos] - arch: [x86_64, arm64, i686] - exclude: - # Fails (.mozbuild does not include clang++-cl) - - target: windows - arch: arm64 - # Unsupported - - target: macos - arch: i686 + # Listed explicitly rather than as a cross product with exclusions, so + # adding an arch to one target cannot silently add it to the other. + # Each arch is picked to match a GitHub-hosted runner, because the + # `verify` job below has to *run* what this job cross-compiles: + # ubuntu-24.04 is x86_64, macos-latest is arm64, windows-latest is + # x86_64. + include: - target: linux - arch: i686 + arch: x86_64 + - target: macos + arch: arm64 + - target: windows + arch: x86_64 steps: - name: Maximize build space @@ -38,6 +56,10 @@ jobs: - name: Remove unwanted tools # Originally from here: https://github.com/AdityaGarg8/remove-unwanted-software/blob/master/action.yml + # Runs before actions/checkout, so the job-level `working-directory: + # browser` default does not exist yet -- override it to the workspace + # root or the step fails with "No such file or directory". + working-directory: ${{ github.workspace }} run: | sudo apt-get remove -y '^aspnetcore-.*' > /dev/null sudo apt-get remove -y '^dotnet-.*' > /dev/null @@ -120,25 +142,180 @@ jobs: uses: actions/upload-artifact@v4 with: name: CamoufoxBuilds-${{ matrix.target }}-${{ matrix.arch }} - path: dist/* + path: browser/dist/* - release: + # Runs what `build` cross-compiled, on the OS it was compiled for. Every + # build therefore ships measured results rather than an assertion that it + # probably still works. + verify: needs: build + # `always()`: `build` sets fail-fast: false so one target failing to + # compile does not stop the others, and the same has to hold here -- a + # broken Windows build must not cost us the Linux and macOS results. A leg + # whose artifact never got uploaded fails at the download step, which + # fail-fast: false below keeps contained to that leg. + if: always() && !cancelled() + strategy: + fail-fast: false + matrix: + include: + - target: linux + arch: x86_64 + runner: ubuntu-24.04 + - target: macos + arch: arm64 + runner: macos-latest + - target: windows + arch: x86_64 + runner: windows-latest + runs-on: ${{ matrix.runner }} + timeout-minutes: 150 + defaults: + run: + shell: bash + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Read version and release + run: | + . browser/upstream.sh + echo "CAMOUFOX_VERSION=$version" >> "$GITHUB_ENV" + echo "CAMOUFOX_RELEASE=$release" >> "$GITHUB_ENV" + + - name: Download the build + uses: actions/download-artifact@v4 + with: + name: CamoufoxBuilds-${{ matrix.target }}-${{ matrix.arch }} + path: build-artifact + + - name: Unpack the package + # zipfile rather than unzip/7z/Expand-Archive: it is the one extractor + # present and identical on all three runners. + run: | + python -c " + import glob, pathlib, sys, zipfile + zips = sorted(glob.glob('build-artifact/**/*.zip', recursive=True)) + if not zips: + sys.exit('no package zip in build-artifact') + print('extracting', zips[0]) + pathlib.Path('package').mkdir(exist_ok=True) + zipfile.ZipFile(zips[0]).extractall('package') + " + + - name: Install test dependencies + run: | + python -m pip install --upgrade pip + # The Playwright suite's own pins, and the launcher (for the sundial + # scan) from this checkout rather than PyPI -- the point is to test + # what this commit produces. + # ci-requirements, not local-requirements: the developer set pulls + # auditwheel, which is manylinux tooling with nothing to do on a + # macOS or Windows runner. + pip install -r browser/tests/ci-requirements.txt + # NOT -e: poetry-core cannot express this project's + # `packages = [{include = "*", from = "src", to = "camoufox"}]` + # remap in an editable install -- it just drops python/src on + # sys.path, which makes `import utils` work and `import camoufox` + # fail. A real build applies the mapping. (python/conftest.py is + # the equivalent shim for an uninstalled working tree.) + pip install "./python[geoip]" + pip install -r release-tester/requirements.txt + # playwright-python refuses to start when its own Firefox is absent, + # even though every launch here overrides the executable path. + python -m playwright install firefox + + - name: Install Linux browser dependencies + if: matrix.target == 'linux' + run: python -m playwright install-deps firefox + + - name: Verify the build + env: + # NOT a username/password: sundial's /automated route is matched + # before its cookie session check and authenticates on this key + # alone. Use AUTOMATION_PRIVATE_KEY -- the guest key silently serves + # fewer detection vectors. Absent (forks, PRs) the scan is skipped + # and the Playwright half still reports. + SUNDIAL_AUTOMATION_KEY: ${{ secrets.SUNDIAL_AUTOMATION_KEY }} + run: | + python release-tester/run.py \ + --package-dir package \ + --target ${{ matrix.target }} \ + --arch ${{ matrix.arch }} \ + --version "$CAMOUFOX_VERSION" \ + --release "$CAMOUFOX_RELEASE" \ + --out "results/${{ matrix.target }}-${{ matrix.arch }}" \ + ${{ (github.event_name == 'workflow_dispatch' && inputs.fail_on_leaks) && '--fail-on-leaks' || '' }} + + - name: Publish to the job summary + # `always()`: a dirty build is exactly when the numbers are worth + # reading, and --fail-on-leaks would otherwise skip this step. + if: always() + run: | + summary="results/${{ matrix.target }}-${{ matrix.arch }}/summary.md" + if [ -f "$summary" ]; then + cat "$summary" >> "$GITHUB_STEP_SUMMARY" + else + echo "## ${{ matrix.target }}/${{ matrix.arch }}" >> "$GITHUB_STEP_SUMMARY" + echo "Verification produced no summary; see the job log." >> "$GITHUB_STEP_SUMMARY" + fi + + - name: Upload results + if: always() + uses: actions/upload-artifact@v4 + with: + name: CamoufoxResults-${{ matrix.target }}-${{ matrix.arch }} + path: results/** + + release: + # `always()` so a build whose verification came back dirty is still + # published -- as a draft prerelease carrying the report that says so, + # which is more useful than no artifacts at all. `build` must still have + # succeeded; there is nothing to release otherwise. + needs: [build, verify] + if: always() && needs.build.result == 'success' permissions: contents: write runs-on: ubuntu-latest steps: + - uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + - name: Download all artifacts uses: actions/download-artifact@v4 with: path: artifacts + - name: Build the combined report + run: | + python release-tester/combine.py \ + --results-root artifacts \ + --out RESULTS.md \ + --assets-dir release-assets + cat RESULTS.md >> "$GITHUB_STEP_SUMMARY" + - name: Create Release uses: softprops/action-gh-release@v1 if: startsWith(github.ref, 'refs/tags/') with: - files: artifacts/**/* + # The binaries, plus one report per build. Release assets are a flat + # namespace, so the reports come from release-assets/ where + # combine.py has already renamed them per build -- uploading + # artifacts/**/* directly would have three results.json collide. + files: | + artifacts/**/*.zip + release-assets/* + body_path: RESULTS.md generate_release_notes: true draft: true prerelease: true diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index 6cee8f7b6..636e88d08 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest defaults: run: - working-directory: pythonlib + working-directory: python steps: - name: Check out repository @@ -26,7 +26,7 @@ jobs: run: pip install vermin build twine - name: Check Python compatibility - run: vermin . --eval-annotations --target=3.8 --violations camoufox/ || exit 1 + run: vermin . --eval-annotations --target=3.8 --violations src/ || exit 1 - name: Build package run: python -m build diff --git a/.github/workflows/verify-release.yml b/.github/workflows/verify-release.yml new file mode 100644 index 000000000..8229d9ec4 --- /dev/null +++ b/.github/workflows/verify-release.yml @@ -0,0 +1,150 @@ +name: Verify a released build + +# Runs release-tester against a binary that already exists, so the stealth and +# functionality suites can be exercised -- or a leak re-checked -- without +# spending an hour per target rebuilding Firefox. build.yml does the same +# verification on freshly compiled artifacts. +# +# Note: GitHub only offers `workflow_dispatch` for workflows present on the +# DEFAULT branch, so this is dispatchable once it lands on main. +on: + workflow_dispatch: + inputs: + release_tag: + description: "Release to pull the binary from (blank = latest)" + type: string + default: "" + target: + description: "Which build to verify" + type: choice + options: [linux, macos, windows] + default: linux + suite: + description: "Which suite to run" + type: choice + options: [sundial, playwright, all] + default: sundial + profiles: + description: "OSes to emulate for the sundial scan" + type: string + default: "windows,macos,linux" + +jobs: + verify: + # A choice input cannot carry its own runner, so map it here. Same pairing + # as build.yml: linux is x86_64, macos is arm64, windows is x86_64. + runs-on: ${{ fromJSON('{"linux":"ubuntu-24.04","macos":"macos-latest","windows":"windows-latest"}')[inputs.target] }} + timeout-minutes: 120 + defaults: + run: + shell: bash + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Download the released build + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -euo pipefail + # Asset names are camoufox---..zip + # (scripts/package.py), so match on the three-letter os and let the + # version float -- this workflow is pointed at a tag, not a version. + case "${{ inputs.target }}" in + linux) pattern='*-lin.*.zip' ;; + macos) pattern='*-mac.*.zip' ;; + windows) pattern='*-win.*.zip' ;; + esac + + tag="${{ inputs.release_tag }}" + if [ -z "$tag" ]; then + tag=$(gh release list --repo "$GITHUB_REPOSITORY" --limit 1 --json tagName --jq '.[0].tagName') + echo "no tag given; using the latest release: $tag" + fi + echo "RELEASE_TAG=$tag" >> "$GITHUB_ENV" + + mkdir -p downloaded + gh release download "$tag" --repo "$GITHUB_REPOSITORY" \ + --pattern "$pattern" --dir downloaded + ls -la downloaded + + - name: Unpack the package + run: | + python -c " + import glob, pathlib, sys, zipfile + zips = sorted(glob.glob('downloaded/*.zip')) + if not zips: + sys.exit('the release carries no package for this target') + print('extracting', zips[0]) + pathlib.Path('package').mkdir(exist_ok=True) + zipfile.ZipFile(zips[0]).extractall('package') + " + # Version, release and arch as the asset itself reports them, not as + # browser/upstream.sh says on this branch -- the point of this + # workflow is to test a build that already shipped. + python -c " + import glob, os, re + name = os.path.basename(sorted(glob.glob('downloaded/*.zip'))[0]) + match = re.match(r'camoufox-(.+?)-(.+?)-(?:lin|mac|win)\.(\w+)\.zip', name) + if not match: + raise SystemExit(f'cannot read version/release/arch out of {name}') + version, release, arch = match.groups() + with open(os.environ['GITHUB_ENV'], 'a') as env: + env.write(f'CAMOUFOX_VERSION={version}\n') + env.write(f'CAMOUFOX_RELEASE={release}\n') + env.write(f'CAMOUFOX_ARCH={arch}\n') + print('package is', version, release, arch) + " + + - name: Install test dependencies + run: | + python -m pip install --upgrade pip + # Not -e: poetry-core cannot express python/'s src/ -> camoufox remap + # in an editable install. See build.yml. + pip install "./python[geoip]" + pip install -r release-tester/requirements.txt + if [ "${{ inputs.suite }}" != "sundial" ]; then + pip install -r browser/tests/ci-requirements.txt + python -m playwright install firefox + fi + + - name: Install Linux browser dependencies + if: inputs.target == 'linux' && inputs.suite != 'sundial' + run: python -m playwright install-deps firefox + + - name: Verify + env: + SUNDIAL_AUTOMATION_KEY: ${{ secrets.SUNDIAL_AUTOMATION_KEY }} + run: | + python release-tester/run.py \ + --package-dir package \ + --target ${{ inputs.target }} \ + --arch "$CAMOUFOX_ARCH" \ + --version "$CAMOUFOX_VERSION" \ + --release "$CAMOUFOX_RELEASE" \ + --suite ${{ inputs.suite }} \ + --profiles "${{ inputs.profiles }}" \ + --out results + + - name: Publish to the job summary + if: always() + run: | + echo "Verified \`$RELEASE_TAG\` (${{ inputs.target }})" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + if [ -f results/summary.md ]; then + cat results/summary.md >> "$GITHUB_STEP_SUMMARY" + else + echo "Verification produced no summary; see the job log." >> "$GITHUB_STEP_SUMMARY" + fi + + - name: Upload results + if: always() + uses: actions/upload-artifact@v4 + with: + name: VerifyRelease-${{ inputs.target }}-${{ github.run_id }} + path: results/** diff --git a/.gitignore b/.gitignore index 8d8bf104d..21aedde91 100644 --- a/.gitignore +++ b/.gitignore @@ -1,82 +1,68 @@ -# Local builds -/camoufox-* -/firefox-* -/mozilla-unified -dist/ -bin/ -launch -launch.exe +# ── Camoufox monorepo .gitignore ───────────────────────────────────────── +# Layout: browser/ (the Firefox fork + build system), python/ (the PyPI +# launcher), typescript/ (the npm launcher). +# +# NOTE: an upstream Firefox tree wants to ignore bare paths like `/browser/`, +# `/dist/`, `/fonts/` because those are Firefox build-output dirs at ITS repo +# root. Here `browser/` is OUR source folder, so every Firefox-build ignore +# stays SCOPED under browser/ (see browser/.gitignore) and is never bare, or +# it would erase real source. +# ── Node / TS ──────────────────────────────────────────────────────────── +node_modules/ +**/node_modules/ +typescript/dist/ +.npmrc +.yarn +yarn.lock -# Internal testing -/extra-docs -pythonlib/test* -!pythonlib/tests/ -jsonvv/test* -/.vscode -/bundle/fonts/extra -pythonlib/*.png -scripts/*.png -scripts/test* -.vscode -.idea -/tests/*.disabled -k8s/ -camoufox-*.*.*/ +# ── Python ─────────────────────────────────────────────────────────────── +__pycache__/ +**/__pycache__/ +*.pyc +*.egg-info/ +.venv/ +venv/ +venv[0-9]*/ +.pytest_cache/ +python/test* +!python/tests/ +python/*.png -# Old data -_old/ -_old_*/ -*.old +# ── Browser: regenerable Firefox trees + build artifacts ───────────────── +# The extracted+patched Firefox trees (~13-20GB each), the upstream tarballs +# and the packaged zips are all reproduced by the build; never commit them. +browser/camoufox-*/ +browser/firefox-*/ +browser/camoufox-*.zip +browser/*.tar.xz -# Logs -wget-log -*.kate-swp -*.log +# ── Test harnesses (build-tester/, service-tester/, release-tester/) ───── +# These used to be covered by the single root .gitignore that now lives in +# browser/; the patterns follow the suites out here. +checks-bundle.js +proxies.txt +# release-tester writes its reports here; they are published as CI artifacts +# and release assets, never committed. +release-tester/results/ -# Python interface -venv/ -venv[0-9]*/ -__pycache__/ -*.pyc +# ── Secrets must never be committed ────────────────────────────────────── +# Deliberately NOT a bare `*.pem` here: browser/tests ships tracked client- and +# server-certificate fixtures, and a root-level `*.pem` silently drops them +# (an ignore only spares a file while its path stays put -- a rename re-adds +# it). Certificate ignores stay scoped in browser/.gitignore. +.env +.passwd *.mmdb -pythonlib-dev/ -run-pw-dev.py - -# Closed source patches private -.passwd closedsrc -.venv -*.pem +# ── Editors / OS ───────────────────────────────────────────────────────── +.vscode/ +.idea/ .DS_Store -# Build outputs -*.so -/camoufox -/omni.ja -/precomplete -/dependentlibs.list -/application.ini -/platform.ini -/camoufox.cfg -/camoucfg.jvv -/chrome.css -/properties.json -/removed-files -/nul -/browser/ -/defaults/ -/distribution/ -/fontconfigs/ -/fonts/ -/gmp-clearkey/ -/windows-build/ -_bisect_extract/ -node_modules/ - -node_modules/ -proxies.txt -checks-bundle.js -.env +# ── Logs ───────────────────────────────────────────────────────────────── +wget-log +*.kate-swp +*.log diff --git a/CLAUDE.md b/CLAUDE.md index 1ecc539d0..aa5847082 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,17 +4,34 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## What this is -Camoufox is an anti-detect fork of Firefox for web scraping and automation. This repo is **not the Firefox source** — it is a *build system* that fetches upstream Firefox, applies a stack of patches + code additions, and produces a hardened, fingerprint-spoofing browser. The distinguishing design choice is that fingerprint spoofing happens at the **C++/Juggler implementation level**, not via injected JavaScript, so it is invisible to page-side inspection. +Camoufox is an anti-detect fork of Firefox for web scraping and automation. The distinguishing design choice is that fingerprint spoofing happens at the **C++/Juggler implementation level**, not via injected JavaScript, so it is invisible to page-side inspection. -The actual Firefox tree lives in `camoufox--/` (e.g. `camoufox-150.0.2-beta.25/`), created by the build. That directory is generated — never edit it directly to make lasting changes; changes there are captured as patches (see "Making patches" below). +This is a **monorepo with three top-level parts**. Know which one you are in before you start: + +| Directory | What it is | +| --- | --- | +| `browser/` | The browser. **Not the Firefox source** — a *build system* that fetches upstream Firefox, applies a stack of patches + code additions, and produces the hardened binary. Everything below about builds and patches happens here. | +| `python/` | The `camoufox` PyPI package: the Playwright-compatible Python launcher. Sources in `python/src/`. | +| `typescript/` | The `camoufox` npm package: the Playwright-compatible JS/TS launcher. Sources in `typescript/src/`. | + +The two launchers are **twins** — same `properties.json`, same chunked `CAMOU_CONFIG`, same `~/.cache/camoufox` install dir and `version.json`, same presets/fonts/voices/WebGL/GeoIP data. **A behaviour change in one belongs in the other.** Module names line up one-to-one (`python/src/pkgman.py` ↔ `typescript/src/pkgman.ts`); the exceptions are `locales.py` + `geolocation.py`, which the TS side merges into `locale.ts`, and `sync_api.py` + `async_api.py`, which merge into `sync_api.ts`. + +Both packages keep their sources under `src/`, but the Python **import** name is still `camoufox` — `pyproject.toml` maps the directory at build time (`packages = [{ include = "*", from = "src", to = "camoufox" }]`) and `python/conftest.py` does the same for an uninstalled working tree. So `from camoufox.utils import ...` is correct everywhere; `from src.utils import ...` is never correct. + +Root also holds `build-tester/`, `service-tester/` and `release-tester/` (cross-cutting QA suites, see Testing) and `.github/` (CI: `build.yml` builds, verifies and releases; `publish-pypi.yml` runs in `python/`). + +### Inside `browser/` + +The actual Firefox tree lives in `browser/camoufox--/` (e.g. `camoufox-152.0.4-beta.28/`), created by the build. That directory is generated — never edit it directly to make lasting changes; changes there are captured as patches (see "Making patches" below). `upstream.sh` pins `version` / `release`, and is sourced+exported by the `Makefile`, so those variables flow into every script. ## Build commands -The build system is designed for **Linux**. Windows and macOS binaries are **cross-compiled from Linux** — they are never built natively. (`scripts/install-deps.sh` covers macOS/Linux host dependencies for local `make dir` + bootstrap experimentation; a full production build path is Linux/Docker.) +**All of these run from `browser/`.** The build system is designed for **Linux**. Windows and macOS binaries are **cross-compiled from Linux** — they are never built natively. (`scripts/install-deps.sh` covers macOS/Linux host dependencies for local `make dir` + bootstrap experimentation; a full production build path is Linux/Docker.) ```bash +cd browser # every command in this section runs here bash scripts/install-deps.sh # install host build deps (Python ≥3.11, Rust, aria2, p7zip, go, msitools, wget, sqlite) make dir # fetch Firefox source, extract, copy additions/settings, apply all patches → touches _READY make bootstrap # install system deps (apt/dnf/pacman) + run `mach bootstrap` (one-time) @@ -26,13 +43,13 @@ python3 multibuild.py --target linux windows macos --arch x86_64 arm64 i686 # `make dir` is the pipeline that matters: `setup` (fetch tarball via `aria2c` → extract → `copy-additions.sh`) → `python3 scripts/patch.py` (applies every patch, writes `mozconfig`) → `_READY`. `mach` requires **Python ≥ 3.11** (stdlib `tomllib`); older `python3` crashes with `ModuleNotFoundError: No module named 'tomllib'`. -Docker is the portable path: `docker build -t camoufox-builder .` then `docker run -v "$(pwd)/dist:/app/dist" camoufox-builder --target --arch `. +Docker is the portable path (context is `browser/`): `docker build -t camoufox-builder browser/` then `docker run -v "$(pwd)/dist:/app/dist" camoufox-builder --target --arch `. Packaging: `make package-linux|package-macos|package-windows arch=` (wraps `scripts/package.py`). Launcher (Go): `make build-launcher arch= os=`. ## Working with patches (the core workflow) -Almost all browser-behavior changes are `patches/*.patch` (~49 patches: `fingerprint-injection.patch`, `webgl-spoofing.patch`, `navigator-spoofing.patch`, `webrtc-ip-spoofing.patch`, the `playwright/` and `librewolf/` and `ghostery/` subdirs, etc.). Do not hand-edit patch files. +All paths in this section are relative to `browser/`. Almost all browser-behavior changes are `patches/*.patch` (~49 patches: `fingerprint-injection.patch`, `webgl-spoofing.patch`, `navigator-spoofing.patch`, `webrtc-ip-spoofing.patch`, the `playwright/` and `librewolf/` and `ghostery/` subdirs, etc.). Do not hand-edit patch files. Use the developer UI instead: @@ -47,13 +64,16 @@ Low-level equivalents: `make patch ./patches/x.patch`, `make unpatch ./patches/x ## Repository layout (the parts that require cross-file understanding) +Paths below are relative to `browser/` unless the entry says otherwise. + - **`patches/`** — the diffs applied to Firefox source. This is where browser behavior is changed. - **`additions/`** — whole files copied *into* the source tree (not diffs) by `scripts/copy-additions.sh`: - `additions/camoucfg/` — the C++ config layer. `MaskConfig.hpp` reads the spoofing config (from `CAMOU_CONFIG` env var / `camoufox.cfg`) that the patches consult at the C++ level; `MouseTrajectories.hpp` is the human-cursor algorithm. - `additions/juggler/` — Camoufox's patched **Juggler** (Firefox's Playwright automation protocol, the Firefox analog of CDP). This is where Playwright is made undetectable — the page agent runs in an isolated scope so injected automation JS is not visible to the page. - **`settings/`** — `camoufox.cfg`, `chrome.css`, `properties.json`, `camoucfg.jvv`, prefs/policies. Copied into the source's `lw/` dir by `copy-additions.sh`. Edit the built config with `make edit-cfg`. - **`scripts/`** — `patch.py` (the patcher, LibreWolf-derived), `developer.py` (the `make edits` UI), `package.py`, `copy-additions.sh`, `install-deps.sh`. -- **`pythonlib/`** — the `camoufox` PyPI package: the Playwright-compatible Python interface that generates + injects fingerprints via BrowserForge and launches the binary. `fingerprint-presets-v150.json` holds real scraped fingerprints. This is the user-facing API; the browser binary is the backend. +- **`../python/`** (repo root) — the `camoufox` PyPI package: the Playwright-compatible Python interface that generates + injects fingerprints via BrowserForge and launches the binary. Sources in `python/src/`, imported as `camoufox` (see above). `fingerprint-presets-v150.json` holds real scraped fingerprints. This is the user-facing API; the browser binary is the backend. +- **`../typescript/`** (repo root) — the `camoufox` npm package: a **port** of `python/` (it does not shell out to Python). `src/data-files/` is generated from `python/src/` (`webgl_data.json` is converted from the SQLite `webgl_data.db`); the YAML data files become type-checked modules under `src/mappings/`. `playwright-core` is pinned to the same `<1.61` range as `pyproject.toml` — newer versions send Juggler params this browser rejects. - **`jsonvv/`** — JSON-with-validation format library used for `camoucfg.jvv` (config schema). - **`legacy/launcher/`** — Go launcher binary. - **`assets/`** — `base.mozconfig` and other build inputs. @@ -68,12 +88,30 @@ Two suites, **both required for PRs** (they cover different layers): python scripts/run_tests.py /path/to/camoufox-binary ``` - **`service-tester/`** — tests the Python package / service layer. -- **`tests/`** — Playwright tests, run via `make tests` (add `headful=true` for headful): points at `camoufox-*/obj-*/dist/bin/camoufox-bin`. +- **`release-tester/`** — verifies a *packaged* build on the OS it targets, and is what CI + runs between `build` and `release` so every release ships measured results. Two suites: + the upstream Playwright suite from `browser/tests` (functionality) and sundial's + `/automated` scan (leaks, per category, one scan per emulated OS). + ```bash + cd release-tester && python run.py --package-dir ./unpacked \ + --target linux --arch x86_64 --version 152.0.4 --release beta.29 --out ./results + ``` + sundial's `/automated` route authenticates on a `key` query parameter, **not** a + username/password — it is matched before the cookie session check. Pass sundial's + `AUTOMATION_PRIVATE_KEY` as `SUNDIAL_AUTOMATION_KEY`; the guest key silently serves fewer + detection vectors. Unset, the scan is skipped rather than failing the build. +- **`browser/tests/`** — Playwright tests, run via `cd browser && make tests` (add `headful=true` for headful): points at `camoufox-*/obj-*/dist/bin/camoufox-bin`. +- **`typescript/tests/`** — vitest unit tests for the TS launcher (no browser needed). Run when changing `typescript/`: + ```bash + cd typescript && pnpm install && pnpm test && pnpm check && pnpm typecheck + ``` +- **`python/tests/`** — pytest unit tests for the Python launcher (no browser needed): `cd python && python -m pytest tests`. `ccache` is enabled in the build config — install it for fast incremental rebuilds (cold ~40 min, incremental ~5 min). ## Constraints when editing this repo -- The `camoufox-*/` source directory is regenerated — persist changes as patches, never as edits committed to that tree. -- Keep the `Makefile` diff clean against `main` unless a change genuinely belongs there — dependency setup lives in `scripts/install-deps.sh`, not the Makefile. +- The `browser/camoufox-*/` source directory is regenerated — persist changes as patches, never as edits committed to that tree. +- Keep the `browser/Makefile` diff clean against `main` unless a change genuinely belongs there — dependency setup lives in `browser/scripts/install-deps.sh`, not the Makefile. +- Never add a bare `/browser/` ignore to the root `.gitignore`. Upstream Firefox trees ignore that path as a build-output dir; here it is our source folder. Firefox-build ignores stay scoped inside `browser/.gitignore`. - Every PR must be tied to a GitHub issue and pass both test suites (see `CONTRIBUTING.md`). diff --git a/README.md b/README.md index e8f9436a8..c593805ad 100644 --- a/README.md +++ b/README.md @@ -1,734 +1,215 @@ - - -

Camoufox

- -

Camoufox is an open source anti-detect browser built for webscraping & AI agents. 🦊

- -
- - daijro%2Fcamoufox | Trendshift -
- Total Downloads - Monthly Downloads - Weekly Downloads -

⚠️ This project is under development. It may not be suitable for stable production use. ⚠️

-
- ---- - -> [!NOTE] -> **All of the latest documentation is available at [camoufox.com](https://camoufox.com).** - -> [!NOTE] -> Browser development is active at [github.com/CloverLabsAI/camoufox](https://github.com/CloverLabsAI/camoufox) and [github.com/VulpineOS/VulpineOS](https://github.com/VulpineOS/VulpineOS).
This repo is being used to merge checkpoint releases and should be treated as the master copy. - ---- - -# Sponsors - -
-View/Collapse All - -## Premium - - - - - - -
- - nodemaven - - - NodeMaven: The most efficient proxy provider for Web Scrapping and Automation with the Highest Quality IP on the market.
- Why NodeMaven?
- • 99.9% uptime
- • ZIP Targeting
- • IP filtering: all proxies have fraud score <97%
- • No KYC required
- • Unique free tools: Proxy Bandwidth Checker, Meta Tag Checker, IP Lookup and others!
- Special codes for Camoufox users:
- • CAMOUFOX35 - 35% off to Mobile and Residential Proxies
- • CAMOUFOX40 - 40% off to ISP (Static) Proxies
-
- -## Tools & Services - - - - - - - - - - - - - - - - - - - - - - -
- - Scrapfly.io - - - Scrapfly is an enterprise-grade solution providing Web Scraping API that aims to simplify the scraping process by managing everything: real browser rendering, rotating proxies, and fingerprints (TLS, HTTP, browser) to bypass all major anti-bots. Scrapfly also unlocks the observability by providing an analytical dashboard and measuring the success rate/block rate in detail. -
- - cloverlabs.ai - - - Clover Labs is a Toronto based venture studio building AI agents for growth and distribution. -
- - color horizontal - - - SerpApi, a web search API to scrape Google and other search engines with a simple API. -
- - color horizontal - - - Web data that survives the anti-bots.
- Crawlbase gives developers and AI teams reliable data at scale: a 99% success-rate Crawler, Crawling API, Smart AI Proxies, and Web MCP Server that get through, so your scrapers and agents don't break. You build, we handle the infrastructure. - -Get 15% off your first 3 months with code CAMOUFOXcrawlbase.com -
- - scrappey - - - Scrappey is a Web Scraping API that only charges successful scrapes with pay as you go - no subscriptions. Scrape complex sites. Residential proxies included, no hidden proxy fees, or expiring balances. One API for direct HTTP, full-browser rendering, JavaScript-heavy pages, screenshots, sessions, 30+ browser actions and 200+ concurrent sessions at a time - trusted by 1000+ developers and AI agents. Get 10% off with code CAMOUFOX. -
- -## Proxy Providers - -Camoufox is intended to be used with rotating proxies (preferably residential IPs). Check out these providers: - - - - - - - - - - - - - - - - - - - - - - -
- - proxyempire - - - 🚀 Camoufox × ProxyEmpire
- Running Camoufox? Your proxy layer decides whether you scale — or get blocked.
- ProxyEmpire delivers:
- • 🌍 30M+ Residential IPs (170+ countries)
- • 📱 4G/5G Mobile Proxies
- • 🔄 Rotating & Sticky Sessions
- • ⚡ Unlimited Concurrent Sessions
- • 🎯 Precise geo-targeting
- • HTTP, HTTPS & SOCKS5 Support
- Built for scraping, automation, and high-stealth workflows.
- 🔥 Exclusive Offer - Use code Camoufox30
- Get 30% recurring discount (not just first month). Upgrade your proxies. Reduce bans. Scale properly -
- - rapidproxy - - - RapidProxy - Power Your Data with Premium Proxies.
- 🎁 Try proxies for free + Use code RAPID10 for 10% OFF -
- Why Choose RapidProxy?
- • 🌍 90M+ IPs in 200+ countries & regions
- • ♾️ No expiration on traffic — use anytime, no pressure
- • 🔥 Unlimited concurrency for maximum performance
- • 💰 Starting from just $0.65/GB — built for scale
- • 📍 City-level targeting for precise geo access
- • 🔄 Flexible session control tailored to your needs
- Don’t miss out — start your free trial today and experience fast, stable, and scalable proxy performance with RapidProxy. -
- - swiftproxy - - - Swiftproxy - High-Performance Residential Proxies for Scalable Data Collection
- Built for developers who need reliable, anti-detection proxy infrastructure. Swiftproxy delivers stable connections, high success rates, and flexible control for large-scale scraping and automation.
- • 🌍 195+ locations with ethically sourced residential IPs
- • 🔄 Rotating & sticky sessions with precise geo-targeting
- • ⚡ Optimized for anti-ban & high success rate
- • 🔌 HTTP / HTTPS / SOCKS5 support
- • 🧪 Free 500MB trial for testing
- • 💸 Special discount code for Camoufox users: PROXY90 - 10%
- Best for: Web scraping, automation, multi-accounting, and large-scale data extraction -
- - mangoproxy - - - MangoProxy is a Residential, ISP, Mobile and Datacenter proxy service designed for professional tasks where stability, speed, and anonymity matter.
- Use code DAIJRO for 8% OFF ISP Static Proxies -
- - proxidize - - - Proxidize | Mobile and Residential Proxies for Camoufox
- Running Camoufox at scale? Your browser setup is only half the stack. Your proxy layer matters too.
- Proxidize provides mobile and residential proxies built for scraping, browser automation, SEO monitoring, AI agents, and data collection workflows.
- Why Proxidize?
- • Real 4G and 5G mobile proxies
- • Residential proxies in 195+ countries
- • Rotating and sticky sessions
- • City-level and carrier targeting
- • Unlimited concurrency
- • HTTP(S), SOCKS5, and UDP over SOCKS support
- • No hardware or DIY setup required
- Built for teams that need reliable proxy infrastructure without managing devices, servers, or proxy rotation themselves.
- Special offer for Camoufox users: Use code CAMOUFOX20 for 20% off.
- Start now: https://proxidize.com -
-
- ---- - -# Introduction - -Camoufox is a Firefox fork engineered for web scraping and AI agents. It is headless, undetectable, and optimized to run at scale. Every run gets a fresh identity drawn from the real-world distribution of devices, so it blends into normal traffic instead of standing out. - -## Highlights - -* **Built for AI agents** 🤖 - * Minimal, debloated Firefox - fast to launch, cheap to run - * Drop-in Playwright compatibility via Python interface - * Invisible to anti-bot systems so you can run your agent cluster locally or in the cloud without being flagged - -- **Undetectable by design** 🎭 - - Page automation hidden from JavaScript inspection. See the [stealth page](https://camoufox.com/stealth) for more details. - -* **Fingerprint injection & rotation (without JS injection!)** - * All navigator properties (device, OS, hardware, browser, etc.) ✅ - * Screen size, resolution, window, & viewport properties ✅ - * Geolocation, timezone, locale, & Intl spoofing ✅ - * WebRTC IP spoofing at the protocol level ✅ - * Voices, speech playback rate, etc. ✅ - * And much, much more! - -- **Anti Graphical fingerprinting** - - WebGL parameters, supported extensions, context attributes, & shader precision formats ✅ - - Font spoofing & anti-fingerprinting ✅ - -* **Optimized for automation** - * Human-like mouse movement 🖱️ - * Blocks & circumvents ads 🛡️ - * No CSS animations 💨 - -- Debloated & optimized for memory efficiency ⚡ -- [PyPi package](https://pypi.org/project/camoufox/) for updates & auto fingerprint injection 📦 -- Stays up to date with the latest Firefox version 🕓 - ---- - -## Fingerprint Injection - -In Camoufox, data is intercepted at the C++ implementation level, making the changes undetectable through JavaScript inspection. - -To spoof individual fingerprint properties, pass a JSON containing properties to spoof to the [Python interface](https://github.com/daijro/camoufox/tree/main/pythonlib#camoufox-python-interface): - -```py ->>> with Camoufox(config={"property": "value"}) as browser: -``` - -Config data not set by the user will be automatically populated using [BrowserForge](https://github.com/daijro/browserforge) fingerprints, which mimic the statistical distribution of device characteristics in real-world traffic. - -[[See implemented properties](https://camoufox.com/fingerprint/)] +# Camoufox ---- +Camoufox is an anti-detect fork of Firefox for web scraping and automation. +Fingerprint spoofing happens at the **C++/Juggler implementation level**, not +via injected JavaScript, so it is invisible to page-side inspection. -## Python Usage +This repository is a monorepo with three parts: -Camoufox is compatible with your existing Playwright code. You only have to change your browser initialization. +| Directory | What it is | +| --- | --- | +| [`browser/`](browser) | The browser itself: a build system that fetches upstream Firefox, applies a stack of patches + code additions, and produces the hardened binary. Start here to build or to change browser behaviour. | +| [`python/`](python) | The `camoufox` PyPI package — the Playwright-compatible Python launcher. | +| [`typescript/`](typescript) | The `camoufox` npm package — the Playwright-compatible JS/TS launcher. A port of the Python one, not a wrapper around it. | -**Sync API** - -```python -from camoufox.sync_api import Camoufox +The two launchers are twins: same `properties.json`, same chunked +`CAMOU_CONFIG`, same `~/.cache/camoufox` install directory, same fingerprint +data. **A behaviour change in one belongs in the other.** -with Camoufox() as browser: - page = browser.new_page() - page.goto("https://example.com") -``` +Both keep their sources under `src/`. The Python import name is still +`camoufox` — `pyproject.toml` maps `python/src/` onto it when the wheel is +built, and `python/conftest.py` does the same for an uninstalled checkout, so +`from camoufox.sync_api import Camoufox` is correct in every context. -**Async API** +## Quick start -```python -from camoufox.async_api import AsyncCamoufox - -async with AsyncCamoufox() as browser: - page = await browser.new_page() - await page.goto("https://example.com") -``` - -[[Installation & usage](https://camoufox.com/python/)] - -### Making Full use of Hardware Spoofing - -For stable releases, you should always use the main [`camoufox`](https://pypi.org/project/camoufox/) pip package. However, if you want to make use of per-context fingerprints and hardware spoofing, use the [`cloverlabs-camoufox`](https://pypi.org/project/cloverlabs-camoufox/) package. This package is updated with each releases, whereas the official package is released on delay. - -Make sure you are using a virtual env to avoid conflicts between the two packages. - -**Installation** +Using the browser (you do not need to build it — the launchers download a +release binary on first use): ```bash -pip install cloverlabs-camoufox +# Python +pip install camoufox[geoip] && python -m camoufox fetch ``` -**Fetch the latest prerelease browser** (recommended for newest patches) - ```bash -python -m camoufox sync -python -m camoufox set official/prerelease -python -m camoufox fetch +# JavaScript / TypeScript +npm install camoufox playwright-core && npx camoufox fetch ``` -**Usage** — the API is identical to the upstream package: - ```python from camoufox.sync_api import Camoufox -with Camoufox() as browser: +with Camoufox(headless=True) as browser: page = browser.new_page() page.goto("https://example.com") ``` -#### Real fingerprint presets (recommended for v149+ binaries) +```javascript +import { Camoufox } from "camoufox"; -By default, fingerprint values are synthesized by BrowserForge. For better evasion against complex consistency checks, opt into the bundled presets — real fingerprints scraped from in-the-wild Firefox traffic: - -```python -with Camoufox(fingerprint_preset=True, os="macos") as browser: - ... +const browser = await Camoufox({ headless: true }); +const page = await browser.newPage(); +await page.goto("https://example.com"); ``` -The library auto-routes by binary version: Firefox ≥ 149 loads `fingerprint-presets-v150.json` (312 presets covering v149–v152, 67 macOS / 180 Windows / 65 Linux); older binaries fall back to the original bundle. UA strings are rewritten to match the active binary, so opting in is safe across versions. Pass a preset dict instead of `True` to pin a specific fingerprint. - ---- - -## Capabilities - -Below is a list of patches and features implemented in Camoufox. - -### Fingerprint spoofing - -- Navigator properties spoofing (device, browser, locale, etc.) -- Support for emulating screen size, resolution, etc. -- Spoof WebGL parameters, supported extensions, context attributes, and shader precision formats. -- Spoof inner and outer window viewport sizes -- Spoof AudioContext sample rate, output latency, and max channel count -- Spoof device voices & playback rates -- Spoof the amount of microphones, webcams, and speakers available. -- Network headers (Accept-Languages and User-Agent) are spoofed to match the navigator properties -- WebRTC IP spoofing at the protocol level -- Geolocation, timezone, and locale spoofing -- Battery API spoofing -- etc. - -### Stealth patches - -- Avoids main world execution leaks. All page agent javascript is sandboxed -- Avoids frame execution context leaks -- Fixes `navigator.webdriver` detection -- Fixes Firefox headless detection via pointer type ([#26](https://github.com/daijro/camoufox/issues/26)) -- Removed potentially leaking anti-zoom/meta viewport handling patches -- Uses non-default screen & window sizes -- Re-enable fission content isolations -- Re-enable PDF.js -- Other leaking config properties changed -- Human-like cursor movement - -### Anti font fingerprinting - -- Automatically uses the correct system fonts for your User Agent -- Bundled with Windows, Mac, and Linux system fonts -- Prevents font metrics fingerprinting by randomly offsetting letter spacing - -### Playwright support - -- Custom implementation of Playwright for the latest Firefox -- Various config patches to evade bot detection - -### Debloat/Optimizations - -- Stripped out/disabled _many, many_ Mozilla services. Runs faster than the original Mozilla Firefox, and uses less memory (200mb) -- Patches from LibreWolf & Ghostery to help remove telemetry & bloat -- Debloat config from PeskyFox, LibreWolf, and others -- Speed & network optimizations from FastFox -- Removed all CSS animations -- Minimalistic theming -- etc. - -### Addons - -- Load Firefox addons without a debug server by passing a list of paths to the `addons` property -- Added uBlock Origin with custom privacy filters -- Addons are not allowed to open tabs -- Addons are automatically enabled in Private Browsing mode -- Addons are automatically pinned to the toolbar -- Fixes DNS leaks with uBO prefetching - -### Python Interface - -- Automatically generates & injects unique device characteristics into Camoufox based on their real-world distribution -- WebGL fingerprint injection & rotation -- Uses the correct system fonts and subpixel antialiasing & hinting based on your target OS -- Avoid proxy detection by calculating your target geolocation, timezone, & locale from your proxy's target region -- Calculate and spoof the browser's language based on the distribution of language speakers in the proxy's target region -- Remote server hosting to use Camoufox with other languages that support Playwright -- Built-in virtual display buffer to run Camoufox headfully on a headless server -- Toggle image loading, WebRTC, and WebGL -- etc. - -> [!NOTE] -> Camoufox does **not** fully support injecting Chromium fingerprints. Some WAFs (such as [Interstitial](https://nopecha.com/demo/cloudflare)) test for Spidermonkey engine behavior, which is impossible to spoof. - ---- - -# Stealth Overview - -## How Camoufox hides its automation library - -> [!WARNING] -> **Current status as of 2026**: -> There has been a year gap in maintenance due to a personal situation. Camoufox has gone down in performance due to the base Firefox version and newly discovered fingerprint inconsistencies. **Camoufox is currently under active development.** - -In Camoufox, all of Playwright's internal Page Agent's code is sandboxed and isolated. This makes it impossible for a page to detect the presence of Playwright through Javascript inspection. - -Normally, Playwright injects some JavaScript into the page such as `window.__playwright__binding__` and to perform actions like querying elements, evaluating javascript, or running init scripts, which can be detected by websites. In Camoufox, these actions are handled in an isolated scope outside of the page. In other words, websites can no longer "see" any JavaScript that Playwright would typically inject. This prevents traces of Playwright altogether. - -However, even with hiding its automation library, Camoufox is not immune to inconsistencies in fingerprint rotation. This still requires maintenance to spot and fix. - -### Page Interactions - -Anti-bot systems also run client-side scripts to monitor your behavior. For example, they look for patterns in mouse movements, clicks, scrolling, and the timing between actions. - - - -Camoufox tries its best with its human-like mouse movement algorithm. The natural motion algorithm was originally from [riflosnake's HumanCursor](https://github.com/riflosnake/HumanCursor) and has been rewritten in C++ and modified for more distance-aware trajectories. - -However, this isn't perfect. It may still be detected with sophisticated enough analysis. (WIP for the future) - ---- - -## How Camoufox rotates identities - -AI agents need to operate across many sessions without getting flagged or rate-limited. Rotating your IP address isn't enough — every browser session carries thousands of signals that create a unique **fingerprint**. A website can see your OS, GPU, screen resolution, fonts, timezone, and more. If those signals are inconsistent or unusual, you get blocked. - -### Market Share Distribution - -Even if you are rotating your IP for each running bot instance, web access firewalls can still use machine learning to analyze incoming web traffic to detect if it's abnormal. If the Linux market share was 5%, then suddenly it's 20%, it's a red flag. They will unconditionally require all Linux users to complete a captcha. - -Camoufox uses [BrowserForge](https://github.com/daijro/browserforge)'s fingerprint generator to mimic the statistical distribution of device data in real-world traffic. For example, Camoufox will make your browser look like a Linux user 5% of the time. Of that 5%, it will spoof a 2560x1440 screen resolution 9.5% of the time and an Intel HD GPU 27.5% of the time. - -### How can Camoufox be detected? - -Camoufox can spoof fingerprints with a correct market share. However, **fingerprints must also be internally consistent.** A Windows user agent with an Apple M1 GPU, a MacOS user agent with a Windows DirectX renderer, and a mobile device with a desktop screen resolution are all impossible, and will be flagged for being suspicious. - -Of the thousands of possible datapoints that must be changed to create a believable spoofed fingerprint, where each change must be consistent with the others, Camoufox doesn't always succeed. Anti-bot providers test Camoufox over and over again to find even 1 unique inconsistency, then they immediately update their background scripts to test for it. - ---- - -## How does Camoufox compare to other solutions? - -### JavaScript-based solutions - -In the past, developers tried injecting JavaScript to spoof these values, but it doesn't work reliably since JavaScript can't spoof everything. Incomplete coverage causes inconsistent fingerprints. For example, an anti-bot system will flag you if your network request's User Agent doesn't match your navigator's User Agent. - -Additionally, all injected JavaScript is detectable in some way. Anti-bot systems can check if `Object.getOwnPropertyDescriptor` reveals an overwritten property, if a function's `toString()` no longer returns `[native code]` (revealing it was hijacked), or if data in the window context doesn't match the worker thread context. Workarounds only take you so far, but there will always be a way to detect JS injection if you search deep enough. - -#### Camoufox's approach - -Since Camoufox intercepts calls in the browser's C++ implementation level, all of the hijacked objects and properties appear native. There is no JavaScript hijacking to be detected. - -Camoufox also attempts to generate consistent and believable fingerprints with Browserforge as well. However, this can still be detected by complex fingerprint detection methods like mismatching data (as described earlier). - -
- -### CDP-based libraries - -CDP (Chrome DevTools Protocol) is an automation protocol built into Chromium and Firefox. However, CDP makes no effort to hide the fact that it's an automation protocol and exposes much of its functionality in the page scope. Some common methods are checking if `navigator.webdriver` is true, catching it reading the stack debugger, checking for variables that ChromeDriver injects into the document object for internal communication, and more. - -#### Camoufox's approach - -While Playwright uses CDP to control Chromium, it uses _Juggler_ for Firefox. Juggler is a custom protocol developed before Firefox supported CDP ([original repo](https://github.com/puppeteer/juggler)). It is a distinct module within Firefox, and not part of its core browser. This makes it easier to edit and control what's revealed to the page. - -Camoufox patches Juggler to give it its own isolated "copy" of the page to work with. Playwright can read and edit its own version of the page freely. Everything appears to work normally to it, but the real page is completely unaffected by these changes. The page also can't detect when things are being read (through tricks like hijacking getters) or listeners being added to watch elements. - -Additionally, Juggler sends its inputs directly through the Firefox's original user input handlers, meaning they are handled the exact same way as if you were using the browser normally. Camoufox also patches Firefox's headless mode to appear the same as if it were running in a normal window. But as a fallback, the Python library can run Camoufox in a [virtual display](https://camoufox.com/python/virtual-display/) if headless mode ever leaks. +Building the browser from source, the patch workflow, and the full +fingerprint-property reference all live in +[`browser/README.md`](browser/README.md). ---- +## CAPTCHA solving -

Build System

- -> [!WARNING] -> The content below is intended for those interested in building & debugging Camoufox. For Playwright usage instructions, see [here](https://github.com/daijro/camoufox/tree/main/pythonlib#camoufox-python-interface). - -### Overview - -Here is a diagram of the build system, and its associated make commands: - -```mermaid -graph TD - FFSRC[Firefox Source] -->|make fetch| REPO - - subgraph REPO[Camoufox Repository] - PATCHES[Fingerprint masking patches] - ADDONS[uBlock & B.P.C.] - DEBLOAT[Debloat/optimizations] - SYSTEM_FONTS[Win, Mac, Linux fonts] - JUGGLER[Patched Juggler] - end - - subgraph Local - REPO -->|make dir| PATCH[Patched Source] - PATCH -->|make build| BUILD[Built] - BUILD -->|make package-linux| LINUX[Linux Portable] - BUILD -->|make package-windows| WIN[Windows Portable] - BUILD -->|make package-macos| MAC[macOS Portable] - end -``` - -This was originally based on the LibreWolf build system. - -## Build CLI - -> [!WARNING] -> Camoufox's build system is designed to be used in Linux. WSL will not work! - -First, clone this repository with Git: - -```bash -git clone --depth 1 https://github.com/daijro/camoufox -cd camoufox -``` - -Next, build the Camoufox source code with the following command: - -```bash -make dir -``` - -Before bootstrapping, install the system build dependencies with the helper -script. It detects your platform and installs everything the build needs -(Python ≥ 3.11, Rust, `aria2`, `p7zip`, `go`, `msitools`, `wget`, `sqlite`, and -the core build tools) using the appropriate package manager — Homebrew on macOS, -or `apt`/`dnf`/`pacman` on Linux: - -```bash -bash scripts/install-deps.sh -``` - -> [!NOTE] -> The dependency installer has so far only been tested on macOS. - -After that, you have to bootstrap your system to be able to build Camoufox. You only have to do this one time. It is done by running the following command: - -```bash -make bootstrap -``` - -Finally you can build and package Camoufox the following command: - -```bash -python3 multibuild.py --target linux windows macos --arch x86_64 arm64 i686 -``` - -For new builds, `i686` is supported only for Windows. Unsupported target/architecture combinations are skipped. - -
- -CLI Parameters - - -```bash -Options: - -h, --help show this help message and exit - --target {linux,windows,macos} [{linux,windows,macos} ...] - Target platforms to build - --arch {x86_64,arm64,i686} [{x86_64,arm64,i686} ...] - Target architectures to build for each platform - --bootstrap Bootstrap the build system - --clean Clean the build directory before starting - -Example: -$ python3 multibuild.py --target linux windows macos --arch x86_64 arm64 -``` - -
- -### Using Docker - -Camoufox can be built through Docker on all platforms. - -1. Create the Docker image containing Firefox's source code: - -```bash -docker build -t camoufox-builder . -``` - -2. Build Camoufox patches to a target platform and architecture: +Camoufox keeps a site from flagging your browser as automated. It does not, by +itself, get you past a challenge that is already on screen. For that there is an +optional integration with +[CaptchaKraken](https://github.com/JWriter20/CaptchaKraken) — OpenCV grid +detection plus a fine-tuned vision model — which drives the visible puzzle to +completion. ```bash -docker run -v "$(pwd)/dist:/app/dist" camoufox-builder --target --arch +# Python +pip install "camoufox[captcha]" ``` -
- -How can I use my local ~/.mozbuild directory? - - -If you want to use the host's .mozbuild directory, you can use the following command instead to run the docker: - ```bash -docker run \ - -v "$HOME/.mozbuild":/root/.mozbuild:rw,z \ - -v "$(pwd)/dist:/app/dist" \ - camoufox-builder \ - --target \ - --arch +# JavaScript / TypeScript +npm install captchakraken ``` -
+Pass credentials with the `captcha` launch option. A **token** uses the hosted +service; a **URL** uses your own server. Give it a token and camoufox sends +solves to `https://api.captchakraken.com/v1` by default — you do not also have +to name the endpoint. -
- -Docker CLI Parameters - +```python +from camoufox.sync_api import Camoufox +from camoufox.captcha import solve_captcha -```bash -Options: - -h, --help show this help message and exit - --target {linux,windows,macos} [{linux,windows,macos} ...] - Target platforms to build - --arch {x86_64,arm64,i686} [{x86_64,arm64,i686} ...] - Target architectures to build for each platform - --bootstrap Bootstrap the build system - --clean Clean the build directory before starting - -Example: -$ docker run -v "$(pwd)/dist:/app/dist" camoufox-builder --target windows macos linux --arch x86_64 arm64 i686 +with Camoufox(headless=False, captcha={"token": "ck_live_..."}) as browser: + page = browser.new_page() + page.goto("https://example.com/protected") + print(solve_captcha(page).is_solved) ``` -
- -Build artifacts will now appear written under the `dist/` folder. - ---- - -## Development Tools - -This repo comes with a developer UI under scripts/developer.py: +```javascript +import { Camoufox, solveCaptcha } from "camoufox"; +const browser = await Camoufox({ headless: false, captcha: "ck_live_..." }); +const page = await browser.newPage(); +await page.goto("https://example.com/protected"); +const result = await solveCaptcha(page); ``` -make edits -``` - -Patches can be edited, created, removed, and managed through here. - +| `captcha=` | Meaning | +| --- | --- | +| `"ck_live_…"` or `{"token": …}` | Hosted service, billed to that key | +| `"http://host:8000/v1"` or `{"url": …}` | Your own vLLM; nothing leaves your network | +| `{"token": …, "url": …}` | Your own endpoint behind an auth proxy | +| `True` | Use `CAPTCHA_KRAKEN_API_KEY` / `VLLM_BASE_URL` already in the environment | -### How to make a patch +Add `model` to pick which served adapter to ask for, when your server runs more +than one. It defaults to whatever the installed CaptchaKraken release pinned: -1. In the developer UI, click **Reset workspace**. -2. Make changes in the `camoufox-*/` folder as needed. You can test your changes with `make build` and `make run`. -3. After you're done making changes, click **Write workspace to patch** and save the patch file. - -### How to work on an existing patch - -1. In the developer UI, click **Edit a patch**. -2. Select the patch you'd like to edit. Your workspace will be reset to the state of the selected patch. -3. After you're done making changes, hit **Write workspace to patch** and overwrite the existing patch file. - ---- - -## Leak Debugging - -This is a flow chart demonstrating my process for determining leaks without deobfuscating WAF Javascript. The method incrementally reintroduces Camoufox's features into Firefox's source code until the testing site flags. - -This process requires a Linux system and assumes you have Firefox build tools installed (see [here](https://github.com/daijro/camoufox?tab=readme-ov-file#build-cli)). - -
- -See flow chart... - - -```mermaid -flowchart TD - A[Start] --> B[Does website flag in the official Firefox?] - B -->|Yes| C[Likely bad IP/rate-limiting. If the website fails on both headless and headful mode on the official Firefox distribution, the issue is not with the browser.] - B -->|No| D["Run make ff-dbg(1) and build(2) a clean distribution of Firefox. Does the website flag in Firefox **headless** mode(4)?"] - D -->|Yes| E["Does the website flag in headful mode(3) AND headless mode(4)?"] - D -->|No| F["Open the developer UI(5), apply config.patch, then rebuild(2). Does the website still flag(3)?"] - E -->|No| G["Enable privacy.resistFingerprinting in the config(6). Does the website still flag(3)?"] - E -->|Yes| C - G -->|No| H["In the config(6), enable FPP and start omitting overrides until you find the one that fixed the leak."] - G -->|Yes| I[If you get to this point, you may need to deobfuscate the Javascript behind the website to identify what it's testing.] - F -->|Yes| K["Open the developer UI, apply the playwright bootstrap patch, then rebuild. Does it still flag?"] - F -->|No| J["Omit options from camoufox.cfg(6) and rerun(3) until you find the one causing the leak."] - K -->|No| M[Juggler needs to be debugged to locate the leak.] - K -->|Yes| L[The issue has nothing to do with Playwright. Apply the rest of the Camoufox patches one by one until the one causing the leak is found.] - M --> I +```python +Camoufox(captcha={"url": "http://gpu:8000/v1", "model": "captcha-v12"}) ``` -#### Cited Commands +### Solve them as they appear -| # | Command | Description | -| --- | --------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | -| (1) | `make ff-dbg` | Setup vanilla Firefox with minimal patches. | -| (2) | `make build` | Build the source code. | -| (3) | `make run` | Runs the built browser. | -| (4) | `make run args="--headless https://test.com"` | Run a URL in headless mode. All redirects will be printed to the console to determine if the test passed. | -| (5) | `make edits` | Opens the developer UI. Allows the user to apply/undo patches, and see which patches are currently applied. | -| (6) | `make edit-cfg` | Edit camoufox.cfg in the default system editor. | +`solve_captcha` is a one-shot: it handles whatever is on the page right now. +When you do not know *where* in a script a challenge will interrupt you, install +a watcher instead and let it work underneath your automation. -
- ---- - -## Thanks - -Debloating & references: - -- [LibreWolf](https://gitlab.com/librewolf-community/browser/source): Debloat patches & build system inspiration -- [BetterFox](https://github.com/yokoffing/BetterFox): Speed and debloat preferences -- [Ghostery](https://github.com/ghostery/user-agent-desktop): Debloat reference ([disable onboarding](https://github.com/daijro/camoufox/blob/main/patches/ghostery/Disable-Onboarding-Messages.patch)) - -Web scraping & testing: - -- [riflosnake/HumanCursor](https://github.com/riflosnake/HumanCursor): Original human-like cursor movement algorithm, ported to C++ -- [CreepJS](https://github.com/abrahamjuliot/creepjs), [Browserleaks](https://browserleaks.com), [BrowserScan](https://www.browserscan.net/) - Valuable leak testing sites - -UI theming: - -- [Jamir-boop/minimalisticfox](https://github.com/Jamir-boop/minimalisticfox): Inspired Camoufox's minimal css theming [(link)](https://github.com/daijro/camoufox/blob/main/settings/chrome.css) +```python +from camoufox.captcha import watch_captcha + +watch_captcha(page).run() # blocking: hold this page clean + +watcher = watch_captcha(page) # or cooperatively, in your own loop +while working(): + watcher.poll_once() +``` + +```typescript +import { Camoufox, watchCaptcha } from "camoufox"; + +const watcher = await watchCaptcha(page); // returns immediately +await page.goto("https://example.com/protected"); +await watcher.stop(); +``` + +Options: `interval_ms` (default 1000), `max_solves`, `error_backoff_ms`, +`on_solved`, `on_error` — camelCased in TypeScript. + +**It runs in the isolated world, and injects nothing.** The watcher adds no +script and no binding to the page; it drives CaptchaKraken's own detection from +the driver side on a timer. The DOM reads that probe performs go through +Playwright, which under Camoufox means the sandboxed Juggler world — the same +isolation that already hides Playwright itself, and the reason `main_world_eval` +exists as an opt-*out*. A page can no more see the watcher than it can see +Camoufox. The trade is that a captcha appearing just after a tick waits up to one +`interval_ms`; against a solve measured in seconds, that is not the number that +matters. + +The Python watcher blocks and the TypeScript one does not, because a +synchronous Playwright handle is bound to the greenlet that created it and +cannot be driven from a worker thread. `AsyncCamoufox` users cannot use either +solver entry point for the same reason. + +An explicit environment variable always wins over the launch option, so a +self-hoster's `VLLM_BASE_URL` is never silently redirected. Check a key before +you rely on it — `verify_credentials()` (`verifyCredentials()` in TS) reports +whether the endpoint answers, the key is accepted, and the account has credit; +a `402` means out of credit. + +Handles reCAPTCHA v2 (3×3 dynamic and 4×4 one-shot grids), hCaptcha grids, and +the checkbox / Turnstile flows. Non-grid hCaptcha puzzles are detected and +skipped rather than guessed at. The Python side is synchronous only — +`AsyncCamoufox` users cannot call it, because a sync Playwright handle cannot be +driven from inside an event loop. + +**Run it yourself, or don't.** CaptchaKraken is source-available and runs +entirely on your own GPU: point `VLLM_BASE_URL` at your server and no request +leaves your machine. The hosted API is a convenience for people who would rather +not run a 9B vision model; set `CAPTCHA_KRAKEN_API_KEY` to use it. + +**Licensing.** CaptchaKraken is source-available under the CaptchaKraken +Source-Available License; the model weights are covered by the same terms. Using +the solver with Camoufox for your own automation is explicitly permitted, free or +commercial. What v1.1 §3(d) restricts is *shipping* it: bundling or advertising +CaptchaKraken as a built-in captcha capability of a stealth/antidetect browser +you distribute to third parties needs a commercial license. This integration is +published by the copyright holder, so Camoufox itself is covered — a fork of +Camoufox that keeps this module and redistributes it is not automatically. See +[LICENSE](https://github.com/JWriter20/CaptchaKraken/blob/main/LICENSE). + +**Attribution.** Requests issued through this integration are tagged +`camoufox/` (the `X-CK-Client` header), which is how camoufox-originated +usage is identified and credited. The tag carries no pricing power — the server +derives the billable puzzle class from the request body — and self-hosted users +report nothing to anyone. + +## Testing + +| Suite | Covers | Run | +| --- | --- | --- | +| [`build-tester/`](build-tester) | The raw binary, bypassing the launchers | `cd build-tester && python scripts/run_tests.py /path/to/camoufox-bin` | +| [`service-tester/`](service-tester) | The packaged Python wheel end to end | `cd service-tester && bash run_tests.sh` | +| [`browser/tests/`](browser/tests) | Playwright tests against a local build | `cd browser && make tests` | +| [`typescript/tests/`](typescript/tests) | The TS launcher (no browser needed) | `cd typescript && pnpm test` | +| [`python/tests/`](python/tests) | The Python launcher (no browser needed) | `cd python && python -m pytest tests` | +| [`release-tester/`](release-tester) | A packaged build, on the OS it targets | `cd release-tester && python run.py --package-dir ./unpacked --target linux --arch x86_64 --out ./results` | + +`release-tester/` is the one CI runs on every build: it drives the upstream +Playwright suite for functionality and [sundial](https://sundial.daijro.dev)'s +`/automated` scan for leaks, then publishes per-category results to the job +summary and onto the release. Each released binary therefore carries its own +scores. See its [README](release-tester/README.md) for the sundial key setup. + +## Contributing + +See [`browser/CONTRIBUTING.md`](browser/CONTRIBUTING.md). Every PR must be tied +to a GitHub issue and pass the test suites above. + +## License + +MPL-2.0. See [`browser/LICENSE`](browser/LICENSE). diff --git a/.dockerignore b/browser/.dockerignore similarity index 82% rename from .dockerignore rename to browser/.dockerignore index a53cdc37f..85cf4b3df 100644 --- a/.dockerignore +++ b/browser/.dockerignore @@ -1,8 +1,9 @@ # The Dockerfile does `COPY . /app` and then runs `make setup-minimal` inside # the image, so everything the build system itself needs (Makefile, upstream.sh, # multibuild.py, scripts/, patches/, additions/, settings/, assets/, bundle/, -# pythonlib/, jsonvv/, legacy/) must stay in the context. This file only drops -# things the in-image build never reads. See #698. +# jsonvv/, legacy/) must stay in the context. This file only drops things the +# in-image build never reads. The build context is this directory (browser/), +# so ../python and ../typescript are already outside it. See #698. # Git history -- by far the largest single item, and the build only needs the # working tree. @@ -53,9 +54,7 @@ closedsrc proxies.txt # Test harnesses -- the container's entrypoint is multibuild.py, which never -# runs these. Removing them also keeps build-tester/node_modules out. -build-tester/ -service-tester/ +# runs these. tests/ example/ docs/ diff --git a/.gitattributes b/browser/.gitattributes similarity index 100% rename from .gitattributes rename to browser/.gitattributes diff --git a/browser/.gitignore b/browser/.gitignore new file mode 100644 index 000000000..b11fcb896 --- /dev/null +++ b/browser/.gitignore @@ -0,0 +1,84 @@ +# Local builds +/camoufox-* +/firefox-* +/mozilla-unified +dist/ +bin/ +launch +launch.exe + + +# Internal testing +# (the launcher packages moved out to ../python and ../typescript; their +# ignores live in the monorepo .gitignore at the repo root) +/extra-docs +jsonvv/test* +/.vscode +/bundle/fonts/extra +scripts/*.png +scripts/test* +.vscode +.idea +/tests/*.disabled +k8s/ +camoufox-*.*.*/ + +# Old data +_old/ +_old_*/ +*.old + +# Logs +wget-log +*.kate-swp +*.log + +# Python interface +venv/ +venv[0-9]*/ +__pycache__/ +*.pyc +*.mmdb +run-pw-dev.py + +# Closed source patches +private +.passwd +closedsrc + +.venv +*.pem +# ...but tests/ ships tracked client- and server-certificate fixtures. They +# survived the bare `*.pem` above only because they were already tracked; a +# rename re-adds them at a new path, where the ignore would silently drop them. +!tests/**/*.pem +.DS_store + +# Build outputs +*.so +/camoufox +/omni.ja +/precomplete +/dependentlibs.list +/application.ini +/platform.ini +/camoufox.cfg +/camoucfg.jvv +/chrome.css +/properties.json +/removed-files +/nul +/browser/ +/defaults/ +/distribution/ +/fontconfigs/ +/fonts/ +/gmp-clearkey/ +/windows-build/ +_bisect_extract/ +node_modules/ + +node_modules/ +proxies.txt +checks-bundle.js +.env diff --git a/CONTRIBUTING.md b/browser/CONTRIBUTING.md similarity index 98% rename from CONTRIBUTING.md rename to browser/CONTRIBUTING.md index 19d7d5299..9b58e5749 100644 --- a/CONTRIBUTING.md +++ b/browser/CONTRIBUTING.md @@ -57,7 +57,7 @@ See [`build-tester/README.md`](build-tester/README.md) for full details. Tests the **full stack** — the binary and the Python package together — using only the public `AsyncNewContext` API. Fingerprints are generated entirely by camoufox/browserforge with no manual injection. Real proxies are required; the WebRTC IP and timezone are auto-derived from each proxy's exit IP. This is a black-box trust test: if it fails, the fix belongs in the Python package, not in the test. -**Run this when you change:** `pythonlib/` (fingerprint generation, `AsyncNewContext`, `NewContext`), proxy handling, or any behaviour that affects how the Python package interacts with the binary. +**Run this when you change:** `../python/` (fingerprint generation, `AsyncNewContext`, `NewContext`), proxy handling, or any behaviour that affects how the Python package interacts with the binary. ```bash cd service-tester diff --git a/Dockerfile b/browser/Dockerfile similarity index 100% rename from Dockerfile rename to browser/Dockerfile diff --git a/LICENSE b/browser/LICENSE similarity index 100% rename from LICENSE rename to browser/LICENSE diff --git a/Makefile b/browser/Makefile similarity index 100% rename from Makefile rename to browser/Makefile diff --git a/browser/README.md b/browser/README.md new file mode 100644 index 000000000..5c28bbcaa --- /dev/null +++ b/browser/README.md @@ -0,0 +1,759 @@ + + +

Camoufox

+ +

Camoufox is an open source anti-detect browser built for webscraping & AI agents. 🦊

+ +
+ + daijro%2Fcamoufox | Trendshift +
+ Total Downloads + Monthly Downloads + Weekly Downloads +

⚠️ This project is under development. It may not be suitable for stable production use. ⚠️

+
+ +--- + +> [!NOTE] +> **All of the latest documentation is available at [camoufox.com](https://camoufox.com).** + +> [!NOTE] +> Browser development is active at [github.com/CloverLabsAI/camoufox](https://github.com/CloverLabsAI/camoufox) and [github.com/VulpineOS/VulpineOS](https://github.com/VulpineOS/VulpineOS).
This repo is being used to merge checkpoint releases and should be treated as the master copy. + +--- + +# Sponsors + +
+View/Collapse All +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + Scrapfly.io + + + Scrapfly is an enterprise-grade solution providing Web Scraping API that aims to simplify the scraping process by managing everything: real browser rendering, rotating proxies, and fingerprints (TLS, HTTP, browser) to bypass all major anti-bots. Scrapfly also unlocks the observability by providing an analytical dashboard and measuring the success rate/block rate in detail. +
+ + cloverlabs.ai + + + Clover Labs is a Toronto based venture studio building AI agents for growth and distribution. +
+ + color horizontal + + + SerpApi, a web search API to scrape Google and other search engines with a simple API. +
+ + color horizontal + + + Talordata is a simple web search API to scrape Google and other search engines at a fraction of the cost. Get 1,000 free requests upon registration, and pay just $0.25 per 1,000 successful responses—zero charges for failed scrapes.
+Use coupon code CAMOUFOX for 10% OFF Residential Proxies. [Discord] +
+ + color horizontal + + + Web data that survives the anti-bots.
+ Crawlbase gives developers and AI teams reliable data at scale: a 99% success-rate Crawler, Crawling API, Smart AI Proxies, and Web MCP Server that get through, so your scrapers and agents don't break. You build, we handle the infrastructure. + +Get 15% off your first 3 months with code CAMOUFOXcrawlbase.com +
+ + scrappey + + + Scrappey is a Web Scraping API that only charges successful scrapes with pay as you go - no subscriptions. Scrape complex sites. Residential proxies included, no hidden proxy fees, or expiring balances. One API for direct HTTP, full-browser rendering, JavaScript-heavy pages, screenshots, sessions, 30+ browser actions and 200+ concurrent sessions at a time - trusted by 1000+ developers and AI agents. Get 10% off with code CAMOUFOX. +
+ +## Proxy providers + +Camoufox is intended to be used with rotating proxies (preferably residential IPs). Check out these providers: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + proxyempire + + + 🚀 Camoufox × ProxyEmpire
+ Running Camoufox? Your proxy layer decides whether you scale — or get blocked.
+ ProxyEmpire delivers:
+ • 🌍 30M+ Residential IPs (170+ countries)
+ • 📱 4G/5G Mobile Proxies
+ • 🔄 Rotating & Sticky Sessions
+ • ⚡ Unlimited Concurrent Sessions
+ • 🎯 Precise geo-targeting
+ • HTTP, HTTPS & SOCKS5 Support
+ Built for scraping, automation, and high-stealth workflows.
+ 🔥 Exclusive Offer - Use code Camoufox30
+ Get 30% recurring discount (not just first month). Upgrade your proxies. Reduce bans. Scale properly +
+ + birdproxies + + + Hey, we built BirdProxies because proxies shouldn't be complicated or overpriced. Fast residential and ISP proxies in 195+ locations, fair pricing, and real support.
+ Try our FlappyBird game on the landing page for free data!
+ Try Now | Discord +
+ + rapidproxy + + + RapidProxy - Power Your Data with Premium Proxies.
+ 🎁 Try proxies for free + Use code RAPID10 for 10% OFF +
+ Why Choose RapidProxy?
+ • 🌍 90M+ IPs in 200+ countries & regions
+ • ♾️ No expiration on traffic — use anytime, no pressure
+ • 🔥 Unlimited concurrency for maximum performance
+ • 💰 Starting from just $0.65/GB — built for scale
+ • 📍 City-level targeting for precise geo access
+ • 🔄 Flexible session control tailored to your needs
+ Don’t miss out — start your free trial today and experience fast, stable, and scalable proxy performance with RapidProxy. +
+ + swiftproxy + + + Swiftproxy - High-Performance Residential Proxies for Scalable Data Collection
+ Built for developers who need reliable, anti-detection proxy infrastructure. Swiftproxy delivers stable connections, high success rates, and flexible control for large-scale scraping and automation.
+ • 🌍 195+ locations with ethically sourced residential IPs
+ • 🔄 Rotating & sticky sessions with precise geo-targeting
+ • ⚡ Optimized for anti-ban & high success rate
+ • 🔌 HTTP / HTTPS / SOCKS5 support
+ • 🧪 Free 500MB trial for testing
+ • 💸 Special discount code for Camoufox users: PROXY90 - 10%
+ Best for: Web scraping, automation, multi-accounting, and large-scale data extraction +
+ + mangoproxy + + + MangoProxy is a Residential, ISP, Mobile and Datacenter proxy service designed for professional tasks where stability, speed, and anonymity matter.
+ Use code DAIJRO for 8% OFF ISP Static Proxies +
+ + 9proxy + + + 9Proxy provides residential proxies from just $0.018/IP or $0.68/GB. 20M+ IPs across 90+ countries. Sticky or rotating sessions, managed from desktop or mobile app.
+
+ + nodemaven + + + NodeMaven: The most reliable proxy provider with the Highest Quality IP on the market.
+ Best solution for automation, web scraping, SEO research, and social media management.
+ NodeMaven offers:
+ • Sticky sessions up to 7 days
+ • 99.9% uptime
+ • IP filtering: all proxies have fraud score <97%
+ • No KYC required
+ • Cashback on traffic - burn GB and earn up to 10% back
+Special offer: Use code CAMOUFOX35 to get 35% discount on Proxies. +
+ + proxidize + + + Proxidize | Mobile and Residential Proxies for Camoufox
+ Running Camoufox at scale? Your browser setup is only half the stack. Your proxy layer matters too.
+ Proxidize provides mobile and residential proxies built for scraping, browser automation, SEO monitoring, AI agents, and data collection workflows.
+ Why Proxidize?
+ • Real 4G and 5G mobile proxies
+ • Residential proxies in 195+ countries
+ • Rotating and sticky sessions
+ • City-level and carrier targeting
+ • Unlimited concurrency
+ • HTTP(S), SOCKS5, and UDP over SOCKS support
+ • No hardware or DIY setup required
+ Built for teams that need reliable proxy infrastructure without managing devices, servers, or proxy rotation themselves.
+ Special offer for Camoufox users: Use code CAMOUFOX20 for 20% off.
+ Start now: https://proxidize.com +
+
+ +--- + +# Introduction + +Camoufox is a Firefox fork engineered for web scraping and AI agents. It is headless, undetectable, and optimized to run at scale. Every run gets a fresh identity drawn from the real-world distribution of devices, so it blends into normal traffic instead of standing out. + +## Highlights + +* **Built for AI agents** 🤖 + * Minimal, debloated Firefox - fast to launch, cheap to run + * Drop-in Playwright compatibility via Python interface + * Invisible to anti-bot systems so you can run your agent cluster locally or in the cloud without being flagged + +- **Undetectable by design** 🎭 + - Page automation hidden from JavaScript inspection. See the [stealth page](https://camoufox.com/stealth) for more details. + +* **Fingerprint injection & rotation (without JS injection!)** + * All navigator properties (device, OS, hardware, browser, etc.) ✅ + * Screen size, resolution, window, & viewport properties ✅ + * Geolocation, timezone, locale, & Intl spoofing ✅ + * WebRTC IP spoofing at the protocol level ✅ + * Voices, speech playback rate, etc. ✅ + * And much, much more! + +- **Anti Graphical fingerprinting** + - WebGL parameters, supported extensions, context attributes, & shader precision formats ✅ + - Font spoofing & anti-fingerprinting ✅ + +* **Optimized for automation** + * Human-like mouse movement 🖱️ + * Blocks & circumvents ads 🛡️ + * No CSS animations 💨 + +- Debloated & optimized for memory efficiency ⚡ +- [PyPi package](https://pypi.org/project/camoufox/) for updates & auto fingerprint injection 📦 +- Stays up to date with the latest Firefox version 🕓 + +--- + +## Fingerprint Injection + +In Camoufox, data is intercepted at the C++ implementation level, making the changes undetectable through JavaScript inspection. + +To spoof individual fingerprint properties, pass a JSON containing properties to spoof to the [Python interface](https://github.com/daijro/camoufox/tree/main/python#camoufox-python-interface): + +```py +>>> with Camoufox(config={"property": "value"}) as browser: +``` + +Config data not set by the user will be automatically populated using [BrowserForge](https://github.com/daijro/browserforge) fingerprints, which mimic the statistical distribution of device characteristics in real-world traffic. + +[[See implemented properties](https://camoufox.com/fingerprint/)] + +--- + +## Python Usage + +Camoufox is compatible with your existing Playwright code. You only have to change your browser initialization. + +**Sync API** + +```python +from camoufox.sync_api import Camoufox + +with Camoufox() as browser: + page = browser.new_page() + page.goto("https://example.com") +``` + +**Async API** + +```python +from camoufox.async_api import AsyncCamoufox + +async with AsyncCamoufox() as browser: + page = await browser.new_page() + await page.goto("https://example.com") +``` + +[[Installation & usage](https://camoufox.com/python/)] + +### Making Full use of Hardware Spoofing + +For stable releases, you should always use the main [`camoufox`](https://pypi.org/project/camoufox/) pip package. However, if you want to make use of per-context fingerprints and hardware spoofing, use the [`cloverlabs-camoufox`](https://pypi.org/project/cloverlabs-camoufox/) package. This package is updated with each releases, whereas the official package is released on delay. + +Make sure you are using a virtual env to avoid conflicts between the two packages. + +**Installation** + +```bash +pip install cloverlabs-camoufox +``` + +**Fetch the latest prerelease browser** (recommended for newest patches) + +```bash +python -m camoufox sync +python -m camoufox set official/prerelease +python -m camoufox fetch +``` + +**Usage** — the API is identical to the upstream package: + +```python +from camoufox.sync_api import Camoufox + +with Camoufox() as browser: + page = browser.new_page() + page.goto("https://example.com") +``` + +#### Real fingerprint presets (recommended for v149+ binaries) + +By default, fingerprint values are synthesized by BrowserForge. For better evasion against complex consistency checks, opt into the bundled presets — real fingerprints scraped from in-the-wild Firefox traffic: + +```python +with Camoufox(fingerprint_preset=True, os="macos") as browser: + ... +``` + +The library auto-routes by binary version: Firefox ≥ 149 loads `fingerprint-presets-v150.json` (312 presets covering v149–v152, 67 macOS / 180 Windows / 65 Linux); older binaries fall back to the original bundle. UA strings are rewritten to match the active binary, so opting in is safe across versions. Pass a preset dict instead of `True` to pin a specific fingerprint. + +--- + +## Capabilities + +Below is a list of patches and features implemented in Camoufox. + +### Fingerprint spoofing + +- Navigator properties spoofing (device, browser, locale, etc.) +- Support for emulating screen size, resolution, etc. +- Spoof WebGL parameters, supported extensions, context attributes, and shader precision formats. +- Spoof inner and outer window viewport sizes +- Spoof AudioContext sample rate, output latency, and max channel count +- Spoof device voices & playback rates +- Spoof the amount of microphones, webcams, and speakers available. +- Network headers (Accept-Languages and User-Agent) are spoofed to match the navigator properties +- WebRTC IP spoofing at the protocol level +- Geolocation, timezone, and locale spoofing +- Battery API spoofing +- etc. + +### Stealth patches + +- Avoids main world execution leaks. All page agent javascript is sandboxed +- Avoids frame execution context leaks +- Fixes `navigator.webdriver` detection +- Fixes Firefox headless detection via pointer type ([#26](https://github.com/daijro/camoufox/issues/26)) +- Removed potentially leaking anti-zoom/meta viewport handling patches +- Uses non-default screen & window sizes +- Re-enable fission content isolations +- Re-enable PDF.js +- Other leaking config properties changed +- Human-like cursor movement + +### Anti font fingerprinting + +- Automatically uses the correct system fonts for your User Agent +- Bundled with Windows, Mac, and Linux system fonts +- Prevents font metrics fingerprinting by randomly offsetting letter spacing + +### Playwright support + +- Custom implementation of Playwright for the latest Firefox +- Various config patches to evade bot detection + +### Debloat/Optimizations + +- Stripped out/disabled _many, many_ Mozilla services. Runs faster than the original Mozilla Firefox, and uses less memory (200mb) +- Patches from LibreWolf & Ghostery to help remove telemetry & bloat +- Debloat config from PeskyFox, LibreWolf, and others +- Speed & network optimizations from FastFox +- Removed all CSS animations +- Minimalistic theming +- etc. + +### Addons + +- Load Firefox addons without a debug server by passing a list of paths to the `addons` property +- Added uBlock Origin with custom privacy filters +- Addons are not allowed to open tabs +- Addons are automatically enabled in Private Browsing mode +- Addons are automatically pinned to the toolbar +- Fixes DNS leaks with uBO prefetching + +### Python Interface + +- Automatically generates & injects unique device characteristics into Camoufox based on their real-world distribution +- WebGL fingerprint injection & rotation +- Uses the correct system fonts and subpixel antialiasing & hinting based on your target OS +- Avoid proxy detection by calculating your target geolocation, timezone, & locale from your proxy's target region +- Calculate and spoof the browser's language based on the distribution of language speakers in the proxy's target region +- Remote server hosting to use Camoufox with other languages that support Playwright +- Built-in virtual display buffer to run Camoufox headfully on a headless server +- Toggle image loading, WebRTC, and WebGL +- etc. + +> [!NOTE] +> Camoufox does **not** fully support injecting Chromium fingerprints. Some WAFs (such as [Interstitial](https://nopecha.com/demo/cloudflare)) test for Spidermonkey engine behavior, which is impossible to spoof. + +--- + +# Stealth Overview + +## How Camoufox hides its automation library + +> [!WARNING] +> **Current status as of 2026**: +> There has been a year gap in maintenance due to a personal situation. Camoufox has gone down in performance due to the base Firefox version and newly discovered fingerprint inconsistencies. **Camoufox is currently under active development.** + +In Camoufox, all of Playwright's internal Page Agent's code is sandboxed and isolated. This makes it impossible for a page to detect the presence of Playwright through Javascript inspection. + +Normally, Playwright injects some JavaScript into the page such as `window.__playwright__binding__` and to perform actions like querying elements, evaluating javascript, or running init scripts, which can be detected by websites. In Camoufox, these actions are handled in an isolated scope outside of the page. In other words, websites can no longer "see" any JavaScript that Playwright would typically inject. This prevents traces of Playwright altogether. + +However, even with hiding its automation library, Camoufox is not immune to inconsistencies in fingerprint rotation. This still requires maintenance to spot and fix. + +### Page Interactions + +Anti-bot systems also run client-side scripts to monitor your behavior. For example, they look for patterns in mouse movements, clicks, scrolling, and the timing between actions. + + + +Camoufox tries its best with its human-like mouse movement algorithm. The natural motion algorithm was originally from [riflosnake's HumanCursor](https://github.com/riflosnake/HumanCursor) and has been rewritten in C++ and modified for more distance-aware trajectories. + +However, this isn't perfect. It may still be detected with sophisticated enough analysis. (WIP for the future) + +--- + +## How Camoufox rotates identities + +AI agents need to operate across many sessions without getting flagged or rate-limited. Rotating your IP address isn't enough — every browser session carries thousands of signals that create a unique **fingerprint**. A website can see your OS, GPU, screen resolution, fonts, timezone, and more. If those signals are inconsistent or unusual, you get blocked. + +### Market Share Distribution + +Even if you are rotating your IP for each running bot instance, web access firewalls can still use machine learning to analyze incoming web traffic to detect if it's abnormal. If the Linux market share was 5%, then suddenly it's 20%, it's a red flag. They will unconditionally require all Linux users to complete a captcha. + +Camoufox uses [BrowserForge](https://github.com/daijro/browserforge)'s fingerprint generator to mimic the statistical distribution of device data in real-world traffic. For example, Camoufox will make your browser look like a Linux user 5% of the time. Of that 5%, it will spoof a 2560x1440 screen resolution 9.5% of the time and an Intel HD GPU 27.5% of the time. + +### How can Camoufox be detected? + +Camoufox can spoof fingerprints with a correct market share. However, **fingerprints must also be internally consistent.** A Windows user agent with an Apple M1 GPU, a MacOS user agent with a Windows DirectX renderer, and a mobile device with a desktop screen resolution are all impossible, and will be flagged for being suspicious. + +Of the thousands of possible datapoints that must be changed to create a believable spoofed fingerprint, where each change must be consistent with the others, Camoufox doesn't always succeed. Anti-bot providers test Camoufox over and over again to find even 1 unique inconsistency, then they immediately update their background scripts to test for it. + +--- + +## How does Camoufox compare to other solutions? + +### JavaScript-based solutions + +In the past, developers tried injecting JavaScript to spoof these values, but it doesn't work reliably since JavaScript can't spoof everything. Incomplete coverage causes inconsistent fingerprints. For example, an anti-bot system will flag you if your network request's User Agent doesn't match your navigator's User Agent. + +Additionally, all injected JavaScript is detectable in some way. Anti-bot systems can check if `Object.getOwnPropertyDescriptor` reveals an overwritten property, if a function's `toString()` no longer returns `[native code]` (revealing it was hijacked), or if data in the window context doesn't match the worker thread context. Workarounds only take you so far, but there will always be a way to detect JS injection if you search deep enough. + +#### Camoufox's approach + +Since Camoufox intercepts calls in the browser's C++ implementation level, all of the hijacked objects and properties appear native. There is no JavaScript hijacking to be detected. + +Camoufox also attempts to generate consistent and believable fingerprints with Browserforge as well. However, this can still be detected by complex fingerprint detection methods like mismatching data (as described earlier). + +
+ +### CDP-based libraries + +CDP (Chrome DevTools Protocol) is an automation protocol built into Chromium and Firefox. However, CDP makes no effort to hide the fact that it's an automation protocol and exposes much of its functionality in the page scope. Some common methods are checking if `navigator.webdriver` is true, catching it reading the stack debugger, checking for variables that ChromeDriver injects into the document object for internal communication, and more. + +#### Camoufox's approach + +While Playwright uses CDP to control Chromium, it uses _Juggler_ for Firefox. Juggler is a custom protocol developed before Firefox supported CDP ([original repo](https://github.com/puppeteer/juggler)). It is a distinct module within Firefox, and not part of its core browser. This makes it easier to edit and control what's revealed to the page. + +Camoufox patches Juggler to give it its own isolated "copy" of the page to work with. Playwright can read and edit its own version of the page freely. Everything appears to work normally to it, but the real page is completely unaffected by these changes. The page also can't detect when things are being read (through tricks like hijacking getters) or listeners being added to watch elements. + +Additionally, Juggler sends its inputs directly through the Firefox's original user input handlers, meaning they are handled the exact same way as if you were using the browser normally. Camoufox also patches Firefox's headless mode to appear the same as if it were running in a normal window. But as a fallback, the Python library can run Camoufox in a [virtual display](https://camoufox.com/python/virtual-display/) if headless mode ever leaks. + +--- + +

Build System

+ +> [!WARNING] +> The content below is intended for those interested in building & debugging Camoufox. For Playwright usage instructions, see [here](https://github.com/daijro/camoufox/tree/main/python#camoufox-python-interface). + +### Overview + +Here is a diagram of the build system, and its associated make commands: + +```mermaid +graph TD + FFSRC[Firefox Source] -->|make fetch| REPO + + subgraph REPO[Camoufox Repository] + PATCHES[Fingerprint masking patches] + ADDONS[uBlock & B.P.C.] + DEBLOAT[Debloat/optimizations] + SYSTEM_FONTS[Win, Mac, Linux fonts] + JUGGLER[Patched Juggler] + end + + subgraph Local + REPO -->|make dir| PATCH[Patched Source] + PATCH -->|make build| BUILD[Built] + BUILD -->|make package-linux| LINUX[Linux Portable] + BUILD -->|make package-windows| WIN[Windows Portable] + BUILD -->|make package-macos| MAC[macOS Portable] + end +``` + +This was originally based on the LibreWolf build system. + +## Build CLI + +> [!WARNING] +> Camoufox's build system is designed to be used in Linux. WSL will not work! + +First, clone this repository with Git: + +```bash +git clone --depth 1 https://github.com/daijro/camoufox +cd camoufox +``` + +Next, build the Camoufox source code with the following command: + +```bash +make dir +``` + +Before bootstrapping, install the system build dependencies with the helper +script. It detects your platform and installs everything the build needs +(Python ≥ 3.11, Rust, `aria2`, `p7zip`, `go`, `msitools`, `wget`, `sqlite`, and +the core build tools) using the appropriate package manager — Homebrew on macOS, +or `apt`/`dnf`/`pacman` on Linux: + +```bash +bash scripts/install-deps.sh +``` + +> [!NOTE] +> The dependency installer has so far only been tested on macOS. + +After that, you have to bootstrap your system to be able to build Camoufox. You only have to do this one time. It is done by running the following command: + +```bash +make bootstrap +``` + +Finally you can build and package Camoufox the following command: + +```bash +python3 multibuild.py --target linux windows macos --arch x86_64 arm64 i686 +``` + +For new builds, `i686` is supported only for Windows. Unsupported target/architecture combinations are skipped. + +
+ +CLI Parameters + + +```bash +Options: + -h, --help show this help message and exit + --target {linux,windows,macos} [{linux,windows,macos} ...] + Target platforms to build + --arch {x86_64,arm64,i686} [{x86_64,arm64,i686} ...] + Target architectures to build for each platform + --bootstrap Bootstrap the build system + --clean Clean the build directory before starting + +Example: +$ python3 multibuild.py --target linux windows macos --arch x86_64 arm64 +``` + +
+ +### Using Docker + +Camoufox can be built through Docker on all platforms. + +1. Create the Docker image containing Firefox's source code: + +```bash +docker build -t camoufox-builder . +``` + +2. Build Camoufox patches to a target platform and architecture: + +```bash +docker run -v "$(pwd)/dist:/app/dist" camoufox-builder --target --arch +``` + +
+ +How can I use my local ~/.mozbuild directory? + + +If you want to use the host's .mozbuild directory, you can use the following command instead to run the docker: + +```bash +docker run \ + -v "$HOME/.mozbuild":/root/.mozbuild:rw,z \ + -v "$(pwd)/dist:/app/dist" \ + camoufox-builder \ + --target \ + --arch +``` + +
+ +
+ +Docker CLI Parameters + + +```bash +Options: + -h, --help show this help message and exit + --target {linux,windows,macos} [{linux,windows,macos} ...] + Target platforms to build + --arch {x86_64,arm64,i686} [{x86_64,arm64,i686} ...] + Target architectures to build for each platform + --bootstrap Bootstrap the build system + --clean Clean the build directory before starting + +Example: +$ docker run -v "$(pwd)/dist:/app/dist" camoufox-builder --target windows macos linux --arch x86_64 arm64 i686 +``` + +
+ +Build artifacts will now appear written under the `dist/` folder. + +--- + +## Development Tools + +This repo comes with a developer UI under scripts/developer.py: + +``` +make edits +``` + +Patches can be edited, created, removed, and managed through here. + + + +### How to make a patch + +1. In the developer UI, click **Reset workspace**. +2. Make changes in the `camoufox-*/` folder as needed. You can test your changes with `make build` and `make run`. +3. After you're done making changes, click **Write workspace to patch** and save the patch file. + +### How to work on an existing patch + +1. In the developer UI, click **Edit a patch**. +2. Select the patch you'd like to edit. Your workspace will be reset to the state of the selected patch. +3. After you're done making changes, hit **Write workspace to patch** and overwrite the existing patch file. + +--- + +## Leak Debugging + +This is a flow chart demonstrating my process for determining leaks without deobfuscating WAF Javascript. The method incrementally reintroduces Camoufox's features into Firefox's source code until the testing site flags. + +This process requires a Linux system and assumes you have Firefox build tools installed (see [here](https://github.com/daijro/camoufox?tab=readme-ov-file#build-cli)). + +
+ +See flow chart... + + +```mermaid +flowchart TD + A[Start] --> B[Does website flag in the official Firefox?] + B -->|Yes| C[Likely bad IP/rate-limiting. If the website fails on both headless and headful mode on the official Firefox distribution, the issue is not with the browser.] + B -->|No| D["Run make ff-dbg(1) and build(2) a clean distribution of Firefox. Does the website flag in Firefox **headless** mode(4)?"] + D -->|Yes| E["Does the website flag in headful mode(3) AND headless mode(4)?"] + D -->|No| F["Open the developer UI(5), apply config.patch, then rebuild(2). Does the website still flag(3)?"] + E -->|No| G["Enable privacy.resistFingerprinting in the config(6). Does the website still flag(3)?"] + E -->|Yes| C + G -->|No| H["In the config(6), enable FPP and start omitting overrides until you find the one that fixed the leak."] + G -->|Yes| I[If you get to this point, you may need to deobfuscate the Javascript behind the website to identify what it's testing.] + F -->|Yes| K["Open the developer UI, apply the playwright bootstrap patch, then rebuild. Does it still flag?"] + F -->|No| J["Omit options from camoufox.cfg(6) and rerun(3) until you find the one causing the leak."] + K -->|No| M[Juggler needs to be debugged to locate the leak.] + K -->|Yes| L[The issue has nothing to do with Playwright. Apply the rest of the Camoufox patches one by one until the one causing the leak is found.] + M --> I +``` + +#### Cited Commands + +| # | Command | Description | +| --- | --------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | +| (1) | `make ff-dbg` | Setup vanilla Firefox with minimal patches. | +| (2) | `make build` | Build the source code. | +| (3) | `make run` | Runs the built browser. | +| (4) | `make run args="--headless https://test.com"` | Run a URL in headless mode. All redirects will be printed to the console to determine if the test passed. | +| (5) | `make edits` | Opens the developer UI. Allows the user to apply/undo patches, and see which patches are currently applied. | +| (6) | `make edit-cfg` | Edit camoufox.cfg in the default system editor. | + +
+ +--- + +## Thanks + +Debloating & references: + +- [LibreWolf](https://gitlab.com/librewolf-community/browser/source): Debloat patches & build system inspiration +- [BetterFox](https://github.com/yokoffing/BetterFox): Speed and debloat preferences +- [Ghostery](https://github.com/ghostery/user-agent-desktop): Debloat reference ([disable onboarding](https://github.com/daijro/camoufox/blob/main/patches/ghostery/Disable-Onboarding-Messages.patch)) + +Web scraping & testing: + +- [riflosnake/HumanCursor](https://github.com/riflosnake/HumanCursor): Original human-like cursor movement algorithm, ported to C++ +- [CreepJS](https://github.com/abrahamjuliot/creepjs), [Browserleaks](https://browserleaks.com), [BrowserScan](https://www.browserscan.net/) - Valuable leak testing sites + +UI theming: + +- [Jamir-boop/minimalisticfox](https://github.com/Jamir-boop/minimalisticfox): Inspired Camoufox's minimal css theming [(link)](https://github.com/daijro/camoufox/blob/main/settings/chrome.css) diff --git a/additions/browser/base/content/aboutDialog.css b/browser/additions/browser/base/content/aboutDialog.css similarity index 100% rename from additions/browser/base/content/aboutDialog.css rename to browser/additions/browser/base/content/aboutDialog.css diff --git a/additions/browser/base/content/aboutDialog.js b/browser/additions/browser/base/content/aboutDialog.js similarity index 100% rename from additions/browser/base/content/aboutDialog.js rename to browser/additions/browser/base/content/aboutDialog.js diff --git a/additions/browser/base/content/aboutDialog.xhtml b/browser/additions/browser/base/content/aboutDialog.xhtml similarity index 100% rename from additions/browser/base/content/aboutDialog.xhtml rename to browser/additions/browser/base/content/aboutDialog.xhtml diff --git a/additions/browser/branding/camoufox/Assets.car b/browser/additions/browser/branding/camoufox/Assets.car similarity index 100% rename from additions/browser/branding/camoufox/Assets.car rename to browser/additions/browser/branding/camoufox/Assets.car diff --git a/additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/Contents.json b/browser/additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/Contents.json rename to browser/additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_128x128.png b/browser/additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_128x128.png similarity index 100% rename from additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_128x128.png rename to browser/additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_128x128.png diff --git a/additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png b/browser/additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png similarity index 100% rename from additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png rename to browser/additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png diff --git a/additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_16x16.png b/browser/additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_16x16.png similarity index 100% rename from additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_16x16.png rename to browser/additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_16x16.png diff --git a/additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png b/browser/additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png similarity index 100% rename from additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png rename to browser/additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png diff --git a/additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_256x256.png b/browser/additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_256x256.png similarity index 100% rename from additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_256x256.png rename to browser/additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_256x256.png diff --git a/additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png b/browser/additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png similarity index 100% rename from additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png rename to browser/additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png diff --git a/additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_32x32.png b/browser/additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_32x32.png similarity index 100% rename from additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_32x32.png rename to browser/additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_32x32.png diff --git a/additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png b/browser/additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png similarity index 100% rename from additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png rename to browser/additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png diff --git a/additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_512x512.png b/browser/additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_512x512.png similarity index 100% rename from additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_512x512.png rename to browser/additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_512x512.png diff --git a/additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png b/browser/additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png similarity index 100% rename from additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png rename to browser/additions/browser/branding/camoufox/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png diff --git a/additions/browser/branding/camoufox/Assets.xcassets/Contents.json b/browser/additions/browser/branding/camoufox/Assets.xcassets/Contents.json similarity index 100% rename from additions/browser/branding/camoufox/Assets.xcassets/Contents.json rename to browser/additions/browser/branding/camoufox/Assets.xcassets/Contents.json diff --git a/additions/browser/branding/camoufox/PrivateBrowsing_150.png b/browser/additions/browser/branding/camoufox/PrivateBrowsing_150.png similarity index 100% rename from additions/browser/branding/camoufox/PrivateBrowsing_150.png rename to browser/additions/browser/branding/camoufox/PrivateBrowsing_150.png diff --git a/additions/browser/branding/camoufox/PrivateBrowsing_70.png b/browser/additions/browser/branding/camoufox/PrivateBrowsing_70.png similarity index 100% rename from additions/browser/branding/camoufox/PrivateBrowsing_70.png rename to browser/additions/browser/branding/camoufox/PrivateBrowsing_70.png diff --git a/additions/browser/branding/camoufox/VisualElements_150.png b/browser/additions/browser/branding/camoufox/VisualElements_150.png similarity index 100% rename from additions/browser/branding/camoufox/VisualElements_150.png rename to browser/additions/browser/branding/camoufox/VisualElements_150.png diff --git a/additions/browser/branding/camoufox/VisualElements_70.png b/browser/additions/browser/branding/camoufox/VisualElements_70.png similarity index 100% rename from additions/browser/branding/camoufox/VisualElements_70.png rename to browser/additions/browser/branding/camoufox/VisualElements_70.png diff --git a/additions/browser/branding/camoufox/background.png b/browser/additions/browser/branding/camoufox/background.png similarity index 100% rename from additions/browser/branding/camoufox/background.png rename to browser/additions/browser/branding/camoufox/background.png diff --git a/additions/browser/branding/camoufox/branding.nsi b/browser/additions/browser/branding/camoufox/branding.nsi similarity index 100% rename from additions/browser/branding/camoufox/branding.nsi rename to browser/additions/browser/branding/camoufox/branding.nsi diff --git a/additions/browser/branding/camoufox/configure.sh b/browser/additions/browser/branding/camoufox/configure.sh similarity index 100% rename from additions/browser/branding/camoufox/configure.sh rename to browser/additions/browser/branding/camoufox/configure.sh diff --git a/additions/browser/branding/camoufox/content/about-logo-private.png b/browser/additions/browser/branding/camoufox/content/about-logo-private.png similarity index 100% rename from additions/browser/branding/camoufox/content/about-logo-private.png rename to browser/additions/browser/branding/camoufox/content/about-logo-private.png diff --git a/additions/browser/branding/camoufox/content/about-logo-private@2x.png b/browser/additions/browser/branding/camoufox/content/about-logo-private@2x.png similarity index 100% rename from additions/browser/branding/camoufox/content/about-logo-private@2x.png rename to browser/additions/browser/branding/camoufox/content/about-logo-private@2x.png diff --git a/additions/browser/branding/camoufox/content/about-logo.png b/browser/additions/browser/branding/camoufox/content/about-logo.png similarity index 100% rename from additions/browser/branding/camoufox/content/about-logo.png rename to browser/additions/browser/branding/camoufox/content/about-logo.png diff --git a/additions/browser/branding/camoufox/content/about-logo.svg b/browser/additions/browser/branding/camoufox/content/about-logo.svg similarity index 100% rename from additions/browser/branding/camoufox/content/about-logo.svg rename to browser/additions/browser/branding/camoufox/content/about-logo.svg diff --git a/additions/browser/branding/camoufox/content/about-logo@2x.png b/browser/additions/browser/branding/camoufox/content/about-logo@2x.png similarity index 100% rename from additions/browser/branding/camoufox/content/about-logo@2x.png rename to browser/additions/browser/branding/camoufox/content/about-logo@2x.png diff --git a/additions/browser/branding/camoufox/content/about-wordmark.svg b/browser/additions/browser/branding/camoufox/content/about-wordmark.svg similarity index 100% rename from additions/browser/branding/camoufox/content/about-wordmark.svg rename to browser/additions/browser/branding/camoufox/content/about-wordmark.svg diff --git a/additions/browser/branding/camoufox/content/about.png b/browser/additions/browser/branding/camoufox/content/about.png similarity index 100% rename from additions/browser/branding/camoufox/content/about.png rename to browser/additions/browser/branding/camoufox/content/about.png diff --git a/additions/browser/branding/camoufox/content/aboutDialog.css b/browser/additions/browser/branding/camoufox/content/aboutDialog.css similarity index 100% rename from additions/browser/branding/camoufox/content/aboutDialog.css rename to browser/additions/browser/branding/camoufox/content/aboutDialog.css diff --git a/additions/browser/branding/camoufox/content/firefox-wordmark.svg b/browser/additions/browser/branding/camoufox/content/firefox-wordmark.svg similarity index 100% rename from additions/browser/branding/camoufox/content/firefox-wordmark.svg rename to browser/additions/browser/branding/camoufox/content/firefox-wordmark.svg diff --git a/additions/browser/branding/camoufox/content/jar.mn b/browser/additions/browser/branding/camoufox/content/jar.mn similarity index 100% rename from additions/browser/branding/camoufox/content/jar.mn rename to browser/additions/browser/branding/camoufox/content/jar.mn diff --git a/additions/browser/branding/camoufox/content/moz.build b/browser/additions/browser/branding/camoufox/content/moz.build similarity index 100% rename from additions/browser/branding/camoufox/content/moz.build rename to browser/additions/browser/branding/camoufox/content/moz.build diff --git a/additions/browser/branding/camoufox/default128.png b/browser/additions/browser/branding/camoufox/default128.png similarity index 100% rename from additions/browser/branding/camoufox/default128.png rename to browser/additions/browser/branding/camoufox/default128.png diff --git a/additions/browser/branding/camoufox/default16.png b/browser/additions/browser/branding/camoufox/default16.png similarity index 100% rename from additions/browser/branding/camoufox/default16.png rename to browser/additions/browser/branding/camoufox/default16.png diff --git a/additions/browser/branding/camoufox/default22.png b/browser/additions/browser/branding/camoufox/default22.png similarity index 100% rename from additions/browser/branding/camoufox/default22.png rename to browser/additions/browser/branding/camoufox/default22.png diff --git a/additions/browser/branding/camoufox/default24.png b/browser/additions/browser/branding/camoufox/default24.png similarity index 100% rename from additions/browser/branding/camoufox/default24.png rename to browser/additions/browser/branding/camoufox/default24.png diff --git a/additions/browser/branding/camoufox/default256.png b/browser/additions/browser/branding/camoufox/default256.png similarity index 100% rename from additions/browser/branding/camoufox/default256.png rename to browser/additions/browser/branding/camoufox/default256.png diff --git a/additions/browser/branding/camoufox/default32.png b/browser/additions/browser/branding/camoufox/default32.png similarity index 100% rename from additions/browser/branding/camoufox/default32.png rename to browser/additions/browser/branding/camoufox/default32.png diff --git a/additions/browser/branding/camoufox/default48.png b/browser/additions/browser/branding/camoufox/default48.png similarity index 100% rename from additions/browser/branding/camoufox/default48.png rename to browser/additions/browser/branding/camoufox/default48.png diff --git a/additions/browser/branding/camoufox/default64.png b/browser/additions/browser/branding/camoufox/default64.png similarity index 100% rename from additions/browser/branding/camoufox/default64.png rename to browser/additions/browser/branding/camoufox/default64.png diff --git a/additions/browser/branding/camoufox/disk.icns b/browser/additions/browser/branding/camoufox/disk.icns similarity index 100% rename from additions/browser/branding/camoufox/disk.icns rename to browser/additions/browser/branding/camoufox/disk.icns diff --git a/additions/browser/branding/camoufox/document.icns b/browser/additions/browser/branding/camoufox/document.icns similarity index 100% rename from additions/browser/branding/camoufox/document.icns rename to browser/additions/browser/branding/camoufox/document.icns diff --git a/additions/browser/branding/camoufox/document.ico b/browser/additions/browser/branding/camoufox/document.ico similarity index 100% rename from additions/browser/branding/camoufox/document.ico rename to browser/additions/browser/branding/camoufox/document.ico diff --git a/additions/browser/branding/camoufox/document_pdf.ico b/browser/additions/browser/branding/camoufox/document_pdf.ico similarity index 100% rename from additions/browser/branding/camoufox/document_pdf.ico rename to browser/additions/browser/branding/camoufox/document_pdf.ico diff --git a/additions/browser/branding/camoufox/dsstore b/browser/additions/browser/branding/camoufox/dsstore similarity index 100% rename from additions/browser/branding/camoufox/dsstore rename to browser/additions/browser/branding/camoufox/dsstore diff --git a/additions/browser/branding/camoufox/firefox.VisualElementsManifest.xml b/browser/additions/browser/branding/camoufox/firefox.VisualElementsManifest.xml similarity index 100% rename from additions/browser/branding/camoufox/firefox.VisualElementsManifest.xml rename to browser/additions/browser/branding/camoufox/firefox.VisualElementsManifest.xml diff --git a/additions/browser/branding/camoufox/firefox.icns b/browser/additions/browser/branding/camoufox/firefox.icns similarity index 100% rename from additions/browser/branding/camoufox/firefox.icns rename to browser/additions/browser/branding/camoufox/firefox.icns diff --git a/additions/browser/branding/camoufox/firefox.ico b/browser/additions/browser/branding/camoufox/firefox.ico similarity index 100% rename from additions/browser/branding/camoufox/firefox.ico rename to browser/additions/browser/branding/camoufox/firefox.ico diff --git a/additions/browser/branding/camoufox/firefox64.ico b/browser/additions/browser/branding/camoufox/firefox64.ico similarity index 100% rename from additions/browser/branding/camoufox/firefox64.ico rename to browser/additions/browser/branding/camoufox/firefox64.ico diff --git a/additions/browser/branding/camoufox/locales/en-US/brand.dtd b/browser/additions/browser/branding/camoufox/locales/en-US/brand.dtd similarity index 100% rename from additions/browser/branding/camoufox/locales/en-US/brand.dtd rename to browser/additions/browser/branding/camoufox/locales/en-US/brand.dtd diff --git a/additions/browser/branding/camoufox/locales/en-US/brand.ftl b/browser/additions/browser/branding/camoufox/locales/en-US/brand.ftl similarity index 100% rename from additions/browser/branding/camoufox/locales/en-US/brand.ftl rename to browser/additions/browser/branding/camoufox/locales/en-US/brand.ftl diff --git a/additions/browser/branding/camoufox/locales/en-US/brand.properties b/browser/additions/browser/branding/camoufox/locales/en-US/brand.properties similarity index 100% rename from additions/browser/branding/camoufox/locales/en-US/brand.properties rename to browser/additions/browser/branding/camoufox/locales/en-US/brand.properties diff --git a/additions/browser/branding/camoufox/locales/jar.mn b/browser/additions/browser/branding/camoufox/locales/jar.mn similarity index 100% rename from additions/browser/branding/camoufox/locales/jar.mn rename to browser/additions/browser/branding/camoufox/locales/jar.mn diff --git a/additions/browser/branding/camoufox/locales/moz.build b/browser/additions/browser/branding/camoufox/locales/moz.build similarity index 100% rename from additions/browser/branding/camoufox/locales/moz.build rename to browser/additions/browser/branding/camoufox/locales/moz.build diff --git a/additions/browser/branding/camoufox/logo.png b/browser/additions/browser/branding/camoufox/logo.png similarity index 100% rename from additions/browser/branding/camoufox/logo.png rename to browser/additions/browser/branding/camoufox/logo.png diff --git a/additions/browser/branding/camoufox/moz.build b/browser/additions/browser/branding/camoufox/moz.build similarity index 100% rename from additions/browser/branding/camoufox/moz.build rename to browser/additions/browser/branding/camoufox/moz.build diff --git a/additions/browser/branding/camoufox/msix/Assets/Document44x44.png b/browser/additions/browser/branding/camoufox/msix/Assets/Document44x44.png similarity index 100% rename from additions/browser/branding/camoufox/msix/Assets/Document44x44.png rename to browser/additions/browser/branding/camoufox/msix/Assets/Document44x44.png diff --git a/additions/browser/branding/camoufox/msix/Assets/LargeTile.scale-200.png b/browser/additions/browser/branding/camoufox/msix/Assets/LargeTile.scale-200.png similarity index 100% rename from additions/browser/branding/camoufox/msix/Assets/LargeTile.scale-200.png rename to browser/additions/browser/branding/camoufox/msix/Assets/LargeTile.scale-200.png diff --git a/additions/browser/branding/camoufox/msix/Assets/SmallTile.scale-200.png b/browser/additions/browser/branding/camoufox/msix/Assets/SmallTile.scale-200.png similarity index 100% rename from additions/browser/branding/camoufox/msix/Assets/SmallTile.scale-200.png rename to browser/additions/browser/branding/camoufox/msix/Assets/SmallTile.scale-200.png diff --git a/additions/browser/branding/camoufox/msix/Assets/Square150x150Logo.scale-200.png b/browser/additions/browser/branding/camoufox/msix/Assets/Square150x150Logo.scale-200.png similarity index 100% rename from additions/browser/branding/camoufox/msix/Assets/Square150x150Logo.scale-200.png rename to browser/additions/browser/branding/camoufox/msix/Assets/Square150x150Logo.scale-200.png diff --git a/additions/browser/branding/camoufox/msix/Assets/Square44x44Logo.altform-lightunplated_targetsize-256.png b/browser/additions/browser/branding/camoufox/msix/Assets/Square44x44Logo.altform-lightunplated_targetsize-256.png similarity index 100% rename from additions/browser/branding/camoufox/msix/Assets/Square44x44Logo.altform-lightunplated_targetsize-256.png rename to browser/additions/browser/branding/camoufox/msix/Assets/Square44x44Logo.altform-lightunplated_targetsize-256.png diff --git a/additions/browser/branding/camoufox/msix/Assets/Square44x44Logo.altform-unplated_targetsize-256.png b/browser/additions/browser/branding/camoufox/msix/Assets/Square44x44Logo.altform-unplated_targetsize-256.png similarity index 100% rename from additions/browser/branding/camoufox/msix/Assets/Square44x44Logo.altform-unplated_targetsize-256.png rename to browser/additions/browser/branding/camoufox/msix/Assets/Square44x44Logo.altform-unplated_targetsize-256.png diff --git a/additions/browser/branding/camoufox/msix/Assets/Square44x44Logo.scale-200.png b/browser/additions/browser/branding/camoufox/msix/Assets/Square44x44Logo.scale-200.png similarity index 100% rename from additions/browser/branding/camoufox/msix/Assets/Square44x44Logo.scale-200.png rename to browser/additions/browser/branding/camoufox/msix/Assets/Square44x44Logo.scale-200.png diff --git a/additions/browser/branding/camoufox/msix/Assets/Square44x44Logo.targetsize-256.png b/browser/additions/browser/branding/camoufox/msix/Assets/Square44x44Logo.targetsize-256.png similarity index 100% rename from additions/browser/branding/camoufox/msix/Assets/Square44x44Logo.targetsize-256.png rename to browser/additions/browser/branding/camoufox/msix/Assets/Square44x44Logo.targetsize-256.png diff --git a/additions/browser/branding/camoufox/msix/Assets/StoreLogo.scale-200.png b/browser/additions/browser/branding/camoufox/msix/Assets/StoreLogo.scale-200.png similarity index 100% rename from additions/browser/branding/camoufox/msix/Assets/StoreLogo.scale-200.png rename to browser/additions/browser/branding/camoufox/msix/Assets/StoreLogo.scale-200.png diff --git a/additions/browser/branding/camoufox/msix/Assets/Wide310x150Logo.scale-200.png b/browser/additions/browser/branding/camoufox/msix/Assets/Wide310x150Logo.scale-200.png similarity index 100% rename from additions/browser/branding/camoufox/msix/Assets/Wide310x150Logo.scale-200.png rename to browser/additions/browser/branding/camoufox/msix/Assets/Wide310x150Logo.scale-200.png diff --git a/additions/browser/branding/camoufox/newtab.ico b/browser/additions/browser/branding/camoufox/newtab.ico similarity index 100% rename from additions/browser/branding/camoufox/newtab.ico rename to browser/additions/browser/branding/camoufox/newtab.ico diff --git a/additions/browser/branding/camoufox/newwindow.ico b/browser/additions/browser/branding/camoufox/newwindow.ico similarity index 100% rename from additions/browser/branding/camoufox/newwindow.ico rename to browser/additions/browser/branding/camoufox/newwindow.ico diff --git a/additions/browser/branding/camoufox/pbmode.ico b/browser/additions/browser/branding/camoufox/pbmode.ico similarity index 100% rename from additions/browser/branding/camoufox/pbmode.ico rename to browser/additions/browser/branding/camoufox/pbmode.ico diff --git a/additions/browser/branding/camoufox/pref/firefox-branding.js b/browser/additions/browser/branding/camoufox/pref/firefox-branding.js similarity index 100% rename from additions/browser/branding/camoufox/pref/firefox-branding.js rename to browser/additions/browser/branding/camoufox/pref/firefox-branding.js diff --git a/additions/browser/branding/camoufox/private_browsing.VisualElementsManifest.xml b/browser/additions/browser/branding/camoufox/private_browsing.VisualElementsManifest.xml similarity index 100% rename from additions/browser/branding/camoufox/private_browsing.VisualElementsManifest.xml rename to browser/additions/browser/branding/camoufox/private_browsing.VisualElementsManifest.xml diff --git a/additions/browser/branding/camoufox/stubinstaller/bgstub.jpg b/browser/additions/browser/branding/camoufox/stubinstaller/bgstub.jpg similarity index 100% rename from additions/browser/branding/camoufox/stubinstaller/bgstub.jpg rename to browser/additions/browser/branding/camoufox/stubinstaller/bgstub.jpg diff --git a/additions/browser/branding/camoufox/stubinstaller/installing_page.css b/browser/additions/browser/branding/camoufox/stubinstaller/installing_page.css similarity index 100% rename from additions/browser/branding/camoufox/stubinstaller/installing_page.css rename to browser/additions/browser/branding/camoufox/stubinstaller/installing_page.css diff --git a/additions/browser/branding/camoufox/stubinstaller/profile_cleanup_page.css b/browser/additions/browser/branding/camoufox/stubinstaller/profile_cleanup_page.css similarity index 100% rename from additions/browser/branding/camoufox/stubinstaller/profile_cleanup_page.css rename to browser/additions/browser/branding/camoufox/stubinstaller/profile_cleanup_page.css diff --git a/additions/browser/branding/camoufox/wizHeader.bmp b/browser/additions/browser/branding/camoufox/wizHeader.bmp similarity index 100% rename from additions/browser/branding/camoufox/wizHeader.bmp rename to browser/additions/browser/branding/camoufox/wizHeader.bmp diff --git a/additions/browser/branding/camoufox/wizHeaderRTL.bmp b/browser/additions/browser/branding/camoufox/wizHeaderRTL.bmp similarity index 100% rename from additions/browser/branding/camoufox/wizHeaderRTL.bmp rename to browser/additions/browser/branding/camoufox/wizHeaderRTL.bmp diff --git a/additions/browser/branding/camoufox/wizWatermark.bmp b/browser/additions/browser/branding/camoufox/wizWatermark.bmp similarity index 100% rename from additions/browser/branding/camoufox/wizWatermark.bmp rename to browser/additions/browser/branding/camoufox/wizWatermark.bmp diff --git a/additions/browser/components/search/extensions/none/manifest.json b/browser/additions/browser/components/search/extensions/none/manifest.json similarity index 100% rename from additions/browser/components/search/extensions/none/manifest.json rename to browser/additions/browser/components/search/extensions/none/manifest.json diff --git a/additions/browser/locales/en-US/chrome/overrides/appstrings.properties b/browser/additions/browser/locales/en-US/chrome/overrides/appstrings.properties similarity index 100% rename from additions/browser/locales/en-US/chrome/overrides/appstrings.properties rename to browser/additions/browser/locales/en-US/chrome/overrides/appstrings.properties diff --git a/additions/browser/themes/addons/dark/background.gif b/browser/additions/browser/themes/addons/dark/background.gif similarity index 100% rename from additions/browser/themes/addons/dark/background.gif rename to browser/additions/browser/themes/addons/dark/background.gif diff --git a/additions/browser/themes/addons/dark/manifest.json b/browser/additions/browser/themes/addons/dark/manifest.json similarity index 100% rename from additions/browser/themes/addons/dark/manifest.json rename to browser/additions/browser/themes/addons/dark/manifest.json diff --git a/additions/camoucfg/MaskConfig.hpp b/browser/additions/camoucfg/MaskConfig.hpp similarity index 100% rename from additions/camoucfg/MaskConfig.hpp rename to browser/additions/camoucfg/MaskConfig.hpp diff --git a/additions/camoucfg/MouseTrajectories.hpp b/browser/additions/camoucfg/MouseTrajectories.hpp similarity index 100% rename from additions/camoucfg/MouseTrajectories.hpp rename to browser/additions/camoucfg/MouseTrajectories.hpp diff --git a/additions/camoucfg/json.hpp b/browser/additions/camoucfg/json.hpp similarity index 100% rename from additions/camoucfg/json.hpp rename to browser/additions/camoucfg/json.hpp diff --git a/additions/camoucfg/moz.build b/browser/additions/camoucfg/moz.build similarity index 100% rename from additions/camoucfg/moz.build rename to browser/additions/camoucfg/moz.build diff --git a/additions/juggler/ChannelEventSink.sys.mjs b/browser/additions/juggler/ChannelEventSink.sys.mjs similarity index 100% rename from additions/juggler/ChannelEventSink.sys.mjs rename to browser/additions/juggler/ChannelEventSink.sys.mjs diff --git a/additions/juggler/Helper.js b/browser/additions/juggler/Helper.js similarity index 100% rename from additions/juggler/Helper.js rename to browser/additions/juggler/Helper.js diff --git a/additions/juggler/JugglerFrameParent.jsm b/browser/additions/juggler/JugglerFrameParent.jsm similarity index 100% rename from additions/juggler/JugglerFrameParent.jsm rename to browser/additions/juggler/JugglerFrameParent.jsm diff --git a/additions/juggler/JugglerFrameParent.sys.mjs b/browser/additions/juggler/JugglerFrameParent.sys.mjs similarity index 100% rename from additions/juggler/JugglerFrameParent.sys.mjs rename to browser/additions/juggler/JugglerFrameParent.sys.mjs diff --git a/additions/juggler/NetworkObserver.js b/browser/additions/juggler/NetworkObserver.js similarity index 100% rename from additions/juggler/NetworkObserver.js rename to browser/additions/juggler/NetworkObserver.js diff --git a/additions/juggler/SimpleChannel.js b/browser/additions/juggler/SimpleChannel.js similarity index 100% rename from additions/juggler/SimpleChannel.js rename to browser/additions/juggler/SimpleChannel.js diff --git a/additions/juggler/TargetRegistry.js b/browser/additions/juggler/TargetRegistry.js similarity index 100% rename from additions/juggler/TargetRegistry.js rename to browser/additions/juggler/TargetRegistry.js diff --git a/additions/juggler/TargetRegistry.js.bak b/browser/additions/juggler/TargetRegistry.js.bak similarity index 100% rename from additions/juggler/TargetRegistry.js.bak rename to browser/additions/juggler/TargetRegistry.js.bak diff --git a/additions/juggler/components/Juggler.js b/browser/additions/juggler/components/Juggler.js similarity index 100% rename from additions/juggler/components/Juggler.js rename to browser/additions/juggler/components/Juggler.js diff --git a/additions/juggler/components/components.conf b/browser/additions/juggler/components/components.conf similarity index 100% rename from additions/juggler/components/components.conf rename to browser/additions/juggler/components/components.conf diff --git a/additions/juggler/components/moz.build b/browser/additions/juggler/components/moz.build similarity index 100% rename from additions/juggler/components/moz.build rename to browser/additions/juggler/components/moz.build diff --git a/additions/juggler/content/FrameTree.js b/browser/additions/juggler/content/FrameTree.js similarity index 100% rename from additions/juggler/content/FrameTree.js rename to browser/additions/juggler/content/FrameTree.js diff --git a/additions/juggler/content/JugglerFrameChild.jsm b/browser/additions/juggler/content/JugglerFrameChild.jsm similarity index 100% rename from additions/juggler/content/JugglerFrameChild.jsm rename to browser/additions/juggler/content/JugglerFrameChild.jsm diff --git a/additions/juggler/content/JugglerFrameChild.sys.mjs b/browser/additions/juggler/content/JugglerFrameChild.sys.mjs similarity index 100% rename from additions/juggler/content/JugglerFrameChild.sys.mjs rename to browser/additions/juggler/content/JugglerFrameChild.sys.mjs diff --git a/additions/juggler/content/PageAgent.js b/browser/additions/juggler/content/PageAgent.js similarity index 100% rename from additions/juggler/content/PageAgent.js rename to browser/additions/juggler/content/PageAgent.js diff --git a/additions/juggler/content/Runtime.js b/browser/additions/juggler/content/Runtime.js similarity index 100% rename from additions/juggler/content/Runtime.js rename to browser/additions/juggler/content/Runtime.js diff --git a/additions/juggler/content/WorkerMain.js b/browser/additions/juggler/content/WorkerMain.js similarity index 100% rename from additions/juggler/content/WorkerMain.js rename to browser/additions/juggler/content/WorkerMain.js diff --git a/additions/juggler/content/hidden-scrollbars.css b/browser/additions/juggler/content/hidden-scrollbars.css similarity index 100% rename from additions/juggler/content/hidden-scrollbars.css rename to browser/additions/juggler/content/hidden-scrollbars.css diff --git a/additions/juggler/content/main.js b/browser/additions/juggler/content/main.js similarity index 100% rename from additions/juggler/content/main.js rename to browser/additions/juggler/content/main.js diff --git a/additions/juggler/jar.mn b/browser/additions/juggler/jar.mn similarity index 100% rename from additions/juggler/jar.mn rename to browser/additions/juggler/jar.mn diff --git a/additions/juggler/moz.build b/browser/additions/juggler/moz.build similarity index 100% rename from additions/juggler/moz.build rename to browser/additions/juggler/moz.build diff --git a/additions/juggler/pipe/components.conf b/browser/additions/juggler/pipe/components.conf similarity index 100% rename from additions/juggler/pipe/components.conf rename to browser/additions/juggler/pipe/components.conf diff --git a/additions/juggler/pipe/moz.build b/browser/additions/juggler/pipe/moz.build similarity index 100% rename from additions/juggler/pipe/moz.build rename to browser/additions/juggler/pipe/moz.build diff --git a/additions/juggler/pipe/nsIRemoteDebuggingPipe.idl b/browser/additions/juggler/pipe/nsIRemoteDebuggingPipe.idl similarity index 100% rename from additions/juggler/pipe/nsIRemoteDebuggingPipe.idl rename to browser/additions/juggler/pipe/nsIRemoteDebuggingPipe.idl diff --git a/additions/juggler/pipe/nsRemoteDebuggingPipe.cpp b/browser/additions/juggler/pipe/nsRemoteDebuggingPipe.cpp similarity index 100% rename from additions/juggler/pipe/nsRemoteDebuggingPipe.cpp rename to browser/additions/juggler/pipe/nsRemoteDebuggingPipe.cpp diff --git a/additions/juggler/pipe/nsRemoteDebuggingPipe.h b/browser/additions/juggler/pipe/nsRemoteDebuggingPipe.h similarity index 100% rename from additions/juggler/pipe/nsRemoteDebuggingPipe.h rename to browser/additions/juggler/pipe/nsRemoteDebuggingPipe.h diff --git a/additions/juggler/protocol/BrowserHandler.js b/browser/additions/juggler/protocol/BrowserHandler.js similarity index 100% rename from additions/juggler/protocol/BrowserHandler.js rename to browser/additions/juggler/protocol/BrowserHandler.js diff --git a/additions/juggler/protocol/Dispatcher.js b/browser/additions/juggler/protocol/Dispatcher.js similarity index 100% rename from additions/juggler/protocol/Dispatcher.js rename to browser/additions/juggler/protocol/Dispatcher.js diff --git a/additions/juggler/protocol/PageHandler.js b/browser/additions/juggler/protocol/PageHandler.js similarity index 100% rename from additions/juggler/protocol/PageHandler.js rename to browser/additions/juggler/protocol/PageHandler.js diff --git a/additions/juggler/protocol/PrimitiveTypes.js b/browser/additions/juggler/protocol/PrimitiveTypes.js similarity index 100% rename from additions/juggler/protocol/PrimitiveTypes.js rename to browser/additions/juggler/protocol/PrimitiveTypes.js diff --git a/additions/juggler/protocol/Protocol.js b/browser/additions/juggler/protocol/Protocol.js similarity index 100% rename from additions/juggler/protocol/Protocol.js rename to browser/additions/juggler/protocol/Protocol.js diff --git a/additions/juggler/screencast/HeadlessWindowCapturer.cpp b/browser/additions/juggler/screencast/HeadlessWindowCapturer.cpp similarity index 100% rename from additions/juggler/screencast/HeadlessWindowCapturer.cpp rename to browser/additions/juggler/screencast/HeadlessWindowCapturer.cpp diff --git a/additions/juggler/screencast/HeadlessWindowCapturer.h b/browser/additions/juggler/screencast/HeadlessWindowCapturer.h similarity index 100% rename from additions/juggler/screencast/HeadlessWindowCapturer.h rename to browser/additions/juggler/screencast/HeadlessWindowCapturer.h diff --git a/additions/juggler/screencast/ScreencastEncoder.cpp b/browser/additions/juggler/screencast/ScreencastEncoder.cpp similarity index 100% rename from additions/juggler/screencast/ScreencastEncoder.cpp rename to browser/additions/juggler/screencast/ScreencastEncoder.cpp diff --git a/additions/juggler/screencast/ScreencastEncoder.h b/browser/additions/juggler/screencast/ScreencastEncoder.h similarity index 100% rename from additions/juggler/screencast/ScreencastEncoder.h rename to browser/additions/juggler/screencast/ScreencastEncoder.h diff --git a/additions/juggler/screencast/WebMFileWriter.cpp b/browser/additions/juggler/screencast/WebMFileWriter.cpp similarity index 100% rename from additions/juggler/screencast/WebMFileWriter.cpp rename to browser/additions/juggler/screencast/WebMFileWriter.cpp diff --git a/additions/juggler/screencast/WebMFileWriter.h b/browser/additions/juggler/screencast/WebMFileWriter.h similarity index 100% rename from additions/juggler/screencast/WebMFileWriter.h rename to browser/additions/juggler/screencast/WebMFileWriter.h diff --git a/additions/juggler/screencast/components.conf b/browser/additions/juggler/screencast/components.conf similarity index 100% rename from additions/juggler/screencast/components.conf rename to browser/additions/juggler/screencast/components.conf diff --git a/additions/juggler/screencast/moz.build b/browser/additions/juggler/screencast/moz.build similarity index 100% rename from additions/juggler/screencast/moz.build rename to browser/additions/juggler/screencast/moz.build diff --git a/additions/juggler/screencast/nsIScreencastService.idl b/browser/additions/juggler/screencast/nsIScreencastService.idl similarity index 100% rename from additions/juggler/screencast/nsIScreencastService.idl rename to browser/additions/juggler/screencast/nsIScreencastService.idl diff --git a/additions/juggler/screencast/nsScreencastService.cpp b/browser/additions/juggler/screencast/nsScreencastService.cpp similarity index 100% rename from additions/juggler/screencast/nsScreencastService.cpp rename to browser/additions/juggler/screencast/nsScreencastService.cpp diff --git a/additions/juggler/screencast/nsScreencastService.h b/browser/additions/juggler/screencast/nsScreencastService.h similarity index 100% rename from additions/juggler/screencast/nsScreencastService.h rename to browser/additions/juggler/screencast/nsScreencastService.h diff --git a/assets/base.mozconfig b/browser/assets/base.mozconfig similarity index 100% rename from assets/base.mozconfig rename to browser/assets/base.mozconfig diff --git a/assets/linux.mozconfig b/browser/assets/linux.mozconfig similarity index 100% rename from assets/linux.mozconfig rename to browser/assets/linux.mozconfig diff --git a/assets/macos.mozconfig b/browser/assets/macos.mozconfig similarity index 100% rename from assets/macos.mozconfig rename to browser/assets/macos.mozconfig diff --git a/assets/scrapfly.png b/browser/assets/scrapfly.png similarity index 100% rename from assets/scrapfly.png rename to browser/assets/scrapfly.png diff --git a/assets/search-config.json b/browser/assets/search-config.json similarity index 100% rename from assets/search-config.json rename to browser/assets/search-config.json diff --git a/assets/uBOAssets.json b/browser/assets/uBOAssets.json similarity index 100% rename from assets/uBOAssets.json rename to browser/assets/uBOAssets.json diff --git a/assets/windows.mozconfig b/browser/assets/windows.mozconfig similarity index 100% rename from assets/windows.mozconfig rename to browser/assets/windows.mozconfig diff --git a/bundle/fontconfig/linux/fonts.conf b/browser/bundle/fontconfig/linux/fonts.conf similarity index 100% rename from bundle/fontconfig/linux/fonts.conf rename to browser/bundle/fontconfig/linux/fonts.conf diff --git a/bundle/fontconfig/macos/fonts.conf b/browser/bundle/fontconfig/macos/fonts.conf similarity index 100% rename from bundle/fontconfig/macos/fonts.conf rename to browser/bundle/fontconfig/macos/fonts.conf diff --git a/bundle/fontconfig/windows/fonts.conf b/browser/bundle/fontconfig/windows/fonts.conf similarity index 100% rename from bundle/fontconfig/windows/fonts.conf rename to browser/bundle/fontconfig/windows/fonts.conf diff --git a/bundle/fonts/000_README.txt b/browser/bundle/fonts/000_README.txt similarity index 100% rename from bundle/fonts/000_README.txt rename to browser/bundle/fonts/000_README.txt diff --git a/bundle/fonts/cleanfonts.sh b/browser/bundle/fonts/cleanfonts.sh similarity index 100% rename from bundle/fonts/cleanfonts.sh rename to browser/bundle/fonts/cleanfonts.sh diff --git a/bundle/fonts/linux/Arimo-Bold.ttf b/browser/bundle/fonts/linux/Arimo-Bold.ttf similarity index 100% rename from bundle/fonts/linux/Arimo-Bold.ttf rename to browser/bundle/fonts/linux/Arimo-Bold.ttf diff --git a/bundle/fonts/linux/Arimo-BoldItalic.ttf b/browser/bundle/fonts/linux/Arimo-BoldItalic.ttf similarity index 100% rename from bundle/fonts/linux/Arimo-BoldItalic.ttf rename to browser/bundle/fonts/linux/Arimo-BoldItalic.ttf diff --git a/bundle/fonts/linux/Arimo-Italic.ttf b/browser/bundle/fonts/linux/Arimo-Italic.ttf similarity index 100% rename from bundle/fonts/linux/Arimo-Italic.ttf rename to browser/bundle/fonts/linux/Arimo-Italic.ttf diff --git a/bundle/fonts/linux/Arimo-Regular.ttf b/browser/bundle/fonts/linux/Arimo-Regular.ttf similarity index 100% rename from bundle/fonts/linux/Arimo-Regular.ttf rename to browser/bundle/fonts/linux/Arimo-Regular.ttf diff --git a/bundle/fonts/linux/Cousine-Bold.ttf b/browser/bundle/fonts/linux/Cousine-Bold.ttf similarity index 100% rename from bundle/fonts/linux/Cousine-Bold.ttf rename to browser/bundle/fonts/linux/Cousine-Bold.ttf diff --git a/bundle/fonts/linux/Cousine-BoldItalic.ttf b/browser/bundle/fonts/linux/Cousine-BoldItalic.ttf similarity index 100% rename from bundle/fonts/linux/Cousine-BoldItalic.ttf rename to browser/bundle/fonts/linux/Cousine-BoldItalic.ttf diff --git a/bundle/fonts/linux/Cousine-Italic.ttf b/browser/bundle/fonts/linux/Cousine-Italic.ttf similarity index 100% rename from bundle/fonts/linux/Cousine-Italic.ttf rename to browser/bundle/fonts/linux/Cousine-Italic.ttf diff --git a/bundle/fonts/linux/Cousine-Regular.ttf b/browser/bundle/fonts/linux/Cousine-Regular.ttf similarity index 100% rename from bundle/fonts/linux/Cousine-Regular.ttf rename to browser/bundle/fonts/linux/Cousine-Regular.ttf diff --git a/bundle/fonts/linux/NotoNaskhArabic-Regular.ttf b/browser/bundle/fonts/linux/NotoNaskhArabic-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoNaskhArabic-Regular.ttf rename to browser/bundle/fonts/linux/NotoNaskhArabic-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansAdlam-Regular.ttf b/browser/bundle/fonts/linux/NotoSansAdlam-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansAdlam-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansAdlam-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansArmenian-Regular.ttf b/browser/bundle/fonts/linux/NotoSansArmenian-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansArmenian-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansArmenian-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansBalinese-Regular.ttf b/browser/bundle/fonts/linux/NotoSansBalinese-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansBalinese-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansBalinese-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansBamum-Regular.ttf b/browser/bundle/fonts/linux/NotoSansBamum-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansBamum-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansBamum-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansBassaVah-Regular.ttf b/browser/bundle/fonts/linux/NotoSansBassaVah-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansBassaVah-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansBassaVah-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansBatak-Regular.ttf b/browser/bundle/fonts/linux/NotoSansBatak-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansBatak-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansBatak-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansBengali-Regular.ttf b/browser/bundle/fonts/linux/NotoSansBengali-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansBengali-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansBengali-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansBuginese-Regular.ttf b/browser/bundle/fonts/linux/NotoSansBuginese-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansBuginese-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansBuginese-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansBuhid-Regular.ttf b/browser/bundle/fonts/linux/NotoSansBuhid-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansBuhid-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansBuhid-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansCanadianAboriginal-Regular.ttf b/browser/bundle/fonts/linux/NotoSansCanadianAboriginal-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansCanadianAboriginal-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansCanadianAboriginal-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansChakma-Regular.ttf b/browser/bundle/fonts/linux/NotoSansChakma-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansChakma-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansChakma-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansCham-Regular.ttf b/browser/bundle/fonts/linux/NotoSansCham-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansCham-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansCham-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansCherokee-Regular.ttf b/browser/bundle/fonts/linux/NotoSansCherokee-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansCherokee-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansCherokee-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansCoptic-Regular.ttf b/browser/bundle/fonts/linux/NotoSansCoptic-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansCoptic-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansCoptic-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansDeseret-Regular.ttf b/browser/bundle/fonts/linux/NotoSansDeseret-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansDeseret-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansDeseret-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansDevanagari-Regular.ttf b/browser/bundle/fonts/linux/NotoSansDevanagari-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansDevanagari-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansDevanagari-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansElbasan-Regular.ttf b/browser/bundle/fonts/linux/NotoSansElbasan-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansElbasan-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansElbasan-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansEthiopic-Regular.ttf b/browser/bundle/fonts/linux/NotoSansEthiopic-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansEthiopic-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansEthiopic-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansGeorgian-Regular.ttf b/browser/bundle/fonts/linux/NotoSansGeorgian-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansGeorgian-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansGeorgian-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansGrantha-Regular.ttf b/browser/bundle/fonts/linux/NotoSansGrantha-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansGrantha-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansGrantha-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansGujarati-Regular.ttf b/browser/bundle/fonts/linux/NotoSansGujarati-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansGujarati-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansGujarati-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansGunjalaGondi-Regular.ttf b/browser/bundle/fonts/linux/NotoSansGunjalaGondi-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansGunjalaGondi-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansGunjalaGondi-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansGurmukhi-Regular.ttf b/browser/bundle/fonts/linux/NotoSansGurmukhi-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansGurmukhi-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansGurmukhi-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansHanifiRohingya-Regular.ttf b/browser/bundle/fonts/linux/NotoSansHanifiRohingya-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansHanifiRohingya-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansHanifiRohingya-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansHanunoo-Regular.ttf b/browser/bundle/fonts/linux/NotoSansHanunoo-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansHanunoo-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansHanunoo-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansHebrew-Regular.ttf b/browser/bundle/fonts/linux/NotoSansHebrew-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansHebrew-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansHebrew-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansJP-Regular.otf b/browser/bundle/fonts/linux/NotoSansJP-Regular.otf similarity index 100% rename from bundle/fonts/linux/NotoSansJP-Regular.otf rename to browser/bundle/fonts/linux/NotoSansJP-Regular.otf diff --git a/bundle/fonts/linux/NotoSansJavanese-Regular.ttf b/browser/bundle/fonts/linux/NotoSansJavanese-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansJavanese-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansJavanese-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansKR-Regular.otf b/browser/bundle/fonts/linux/NotoSansKR-Regular.otf similarity index 100% rename from bundle/fonts/linux/NotoSansKR-Regular.otf rename to browser/bundle/fonts/linux/NotoSansKR-Regular.otf diff --git a/bundle/fonts/linux/NotoSansKannada-Regular.ttf b/browser/bundle/fonts/linux/NotoSansKannada-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansKannada-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansKannada-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansKayahLi-Regular.ttf b/browser/bundle/fonts/linux/NotoSansKayahLi-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansKayahLi-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansKayahLi-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansKhmer-Regular.ttf b/browser/bundle/fonts/linux/NotoSansKhmer-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansKhmer-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansKhmer-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansKhojki-Regular.ttf b/browser/bundle/fonts/linux/NotoSansKhojki-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansKhojki-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansKhojki-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansKhudawadi-Regular.ttf b/browser/bundle/fonts/linux/NotoSansKhudawadi-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansKhudawadi-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansKhudawadi-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansLao-Regular.ttf b/browser/bundle/fonts/linux/NotoSansLao-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansLao-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansLao-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansLepcha-Regular.ttf b/browser/bundle/fonts/linux/NotoSansLepcha-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansLepcha-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansLepcha-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansLimbu-Regular.ttf b/browser/bundle/fonts/linux/NotoSansLimbu-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansLimbu-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansLimbu-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansLisu-Regular.ttf b/browser/bundle/fonts/linux/NotoSansLisu-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansLisu-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansLisu-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansMahajani-Regular.ttf b/browser/bundle/fonts/linux/NotoSansMahajani-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansMahajani-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansMahajani-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansMalayalam-Regular.ttf b/browser/bundle/fonts/linux/NotoSansMalayalam-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansMalayalam-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansMalayalam-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansMandaic-Regular.ttf b/browser/bundle/fonts/linux/NotoSansMandaic-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansMandaic-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansMandaic-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansMasaramGondi-Regular.ttf b/browser/bundle/fonts/linux/NotoSansMasaramGondi-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansMasaramGondi-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansMasaramGondi-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansMedefaidrin-Regular.ttf b/browser/bundle/fonts/linux/NotoSansMedefaidrin-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansMedefaidrin-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansMedefaidrin-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansMeeteiMayek-Regular.ttf b/browser/bundle/fonts/linux/NotoSansMeeteiMayek-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansMeeteiMayek-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansMeeteiMayek-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansMendeKikakui-Regular.ttf b/browser/bundle/fonts/linux/NotoSansMendeKikakui-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansMendeKikakui-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansMendeKikakui-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansMiao-Regular.ttf b/browser/bundle/fonts/linux/NotoSansMiao-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansMiao-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansMiao-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansModi-Regular.ttf b/browser/bundle/fonts/linux/NotoSansModi-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansModi-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansModi-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansMongolian-Regular.ttf b/browser/bundle/fonts/linux/NotoSansMongolian-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansMongolian-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansMongolian-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansMro-Regular.ttf b/browser/bundle/fonts/linux/NotoSansMro-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansMro-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansMro-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansMultani-Regular.ttf b/browser/bundle/fonts/linux/NotoSansMultani-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansMultani-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansMultani-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansMyanmar-Regular.ttf b/browser/bundle/fonts/linux/NotoSansMyanmar-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansMyanmar-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansMyanmar-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansNKo-Regular.ttf b/browser/bundle/fonts/linux/NotoSansNKo-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansNKo-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansNKo-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansNewTaiLue-Regular.ttf b/browser/bundle/fonts/linux/NotoSansNewTaiLue-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansNewTaiLue-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansNewTaiLue-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansNewa-Regular.ttf b/browser/bundle/fonts/linux/NotoSansNewa-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansNewa-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansNewa-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansOlChiki-Regular.ttf b/browser/bundle/fonts/linux/NotoSansOlChiki-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansOlChiki-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansOlChiki-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansOriya-Regular.ttf b/browser/bundle/fonts/linux/NotoSansOriya-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansOriya-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansOriya-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansOsage-Regular.ttf b/browser/bundle/fonts/linux/NotoSansOsage-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansOsage-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansOsage-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansOsmanya-Regular.ttf b/browser/bundle/fonts/linux/NotoSansOsmanya-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansOsmanya-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansOsmanya-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansPahawhHmong-Regular.ttf b/browser/bundle/fonts/linux/NotoSansPahawhHmong-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansPahawhHmong-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansPahawhHmong-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansPauCinHau-Regular.ttf b/browser/bundle/fonts/linux/NotoSansPauCinHau-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansPauCinHau-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansPauCinHau-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansRejang-Regular.ttf b/browser/bundle/fonts/linux/NotoSansRejang-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansRejang-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansRejang-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansRunic-Regular.ttf b/browser/bundle/fonts/linux/NotoSansRunic-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansRunic-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansRunic-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansSC-Regular.otf b/browser/bundle/fonts/linux/NotoSansSC-Regular.otf similarity index 100% rename from bundle/fonts/linux/NotoSansSC-Regular.otf rename to browser/bundle/fonts/linux/NotoSansSC-Regular.otf diff --git a/bundle/fonts/linux/NotoSansSamaritan-Regular.ttf b/browser/bundle/fonts/linux/NotoSansSamaritan-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansSamaritan-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansSamaritan-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansSaurashtra-Regular.ttf b/browser/bundle/fonts/linux/NotoSansSaurashtra-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansSaurashtra-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansSaurashtra-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansSharada-Regular.ttf b/browser/bundle/fonts/linux/NotoSansSharada-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansSharada-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansSharada-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansShavian-Regular.ttf b/browser/bundle/fonts/linux/NotoSansShavian-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansShavian-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansShavian-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansSinhala-Regular.ttf b/browser/bundle/fonts/linux/NotoSansSinhala-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansSinhala-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansSinhala-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansSoraSompeng-Regular.ttf b/browser/bundle/fonts/linux/NotoSansSoraSompeng-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansSoraSompeng-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansSoraSompeng-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansSoyombo-Regular.ttf b/browser/bundle/fonts/linux/NotoSansSoyombo-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansSoyombo-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansSoyombo-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansSundanese-Regular.ttf b/browser/bundle/fonts/linux/NotoSansSundanese-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansSundanese-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansSundanese-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansSylotiNagri-Regular.ttf b/browser/bundle/fonts/linux/NotoSansSylotiNagri-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansSylotiNagri-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansSylotiNagri-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansSymbols-Regular.ttf b/browser/bundle/fonts/linux/NotoSansSymbols-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansSymbols-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansSymbols-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansSymbols2-Regular.ttf b/browser/bundle/fonts/linux/NotoSansSymbols2-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansSymbols2-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansSymbols2-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansSyriac-Regular.ttf b/browser/bundle/fonts/linux/NotoSansSyriac-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansSyriac-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansSyriac-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansTC-Regular.otf b/browser/bundle/fonts/linux/NotoSansTC-Regular.otf similarity index 100% rename from bundle/fonts/linux/NotoSansTC-Regular.otf rename to browser/bundle/fonts/linux/NotoSansTC-Regular.otf diff --git a/bundle/fonts/linux/NotoSansTagalog-Regular.ttf b/browser/bundle/fonts/linux/NotoSansTagalog-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansTagalog-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansTagalog-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansTagbanwa-Regular.ttf b/browser/bundle/fonts/linux/NotoSansTagbanwa-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansTagbanwa-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansTagbanwa-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansTaiLe-Regular.ttf b/browser/bundle/fonts/linux/NotoSansTaiLe-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansTaiLe-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansTaiLe-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansTaiTham-Regular.ttf b/browser/bundle/fonts/linux/NotoSansTaiTham-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansTaiTham-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansTaiTham-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansTaiViet-Regular.ttf b/browser/bundle/fonts/linux/NotoSansTaiViet-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansTaiViet-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansTaiViet-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansTakri-Regular.ttf b/browser/bundle/fonts/linux/NotoSansTakri-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansTakri-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansTakri-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansTamil-Regular.ttf b/browser/bundle/fonts/linux/NotoSansTamil-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansTamil-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansTamil-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansTelugu-Regular.ttf b/browser/bundle/fonts/linux/NotoSansTelugu-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansTelugu-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansTelugu-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansThaana-Regular.ttf b/browser/bundle/fonts/linux/NotoSansThaana-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansThaana-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansThaana-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansThai-Regular.ttf b/browser/bundle/fonts/linux/NotoSansThai-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansThai-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansThai-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansTifinagh-Regular.ttf b/browser/bundle/fonts/linux/NotoSansTifinagh-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansTifinagh-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansTifinagh-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansTifinaghAPT-Regular.ttf b/browser/bundle/fonts/linux/NotoSansTifinaghAPT-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansTifinaghAPT-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansTifinaghAPT-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansTifinaghAdrar-Regular.ttf b/browser/bundle/fonts/linux/NotoSansTifinaghAdrar-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansTifinaghAdrar-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansTifinaghAdrar-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansTifinaghAgrawImazighen-Regular.ttf b/browser/bundle/fonts/linux/NotoSansTifinaghAgrawImazighen-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansTifinaghAgrawImazighen-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansTifinaghAgrawImazighen-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansTifinaghAhaggar-Regular.ttf b/browser/bundle/fonts/linux/NotoSansTifinaghAhaggar-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansTifinaghAhaggar-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansTifinaghAhaggar-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansTifinaghAir-Regular.ttf b/browser/bundle/fonts/linux/NotoSansTifinaghAir-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansTifinaghAir-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansTifinaghAir-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansTifinaghAzawagh-Regular.ttf b/browser/bundle/fonts/linux/NotoSansTifinaghAzawagh-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansTifinaghAzawagh-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansTifinaghAzawagh-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansTifinaghGhat-Regular.ttf b/browser/bundle/fonts/linux/NotoSansTifinaghGhat-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansTifinaghGhat-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansTifinaghGhat-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansTifinaghHawad-Regular.ttf b/browser/bundle/fonts/linux/NotoSansTifinaghHawad-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansTifinaghHawad-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansTifinaghHawad-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansTifinaghRhissaIxa-Regular.ttf b/browser/bundle/fonts/linux/NotoSansTifinaghRhissaIxa-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansTifinaghRhissaIxa-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansTifinaghRhissaIxa-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansTifinaghSIL-Regular.ttf b/browser/bundle/fonts/linux/NotoSansTifinaghSIL-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansTifinaghSIL-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansTifinaghSIL-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansTifinaghTawellemmet-Regular.ttf b/browser/bundle/fonts/linux/NotoSansTifinaghTawellemmet-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansTifinaghTawellemmet-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansTifinaghTawellemmet-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansTirhuta-Regular.ttf b/browser/bundle/fonts/linux/NotoSansTirhuta-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansTirhuta-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansTirhuta-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansVai-Regular.ttf b/browser/bundle/fonts/linux/NotoSansVai-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansVai-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansVai-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansWancho-Regular.ttf b/browser/bundle/fonts/linux/NotoSansWancho-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansWancho-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansWancho-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansWarangCiti-Regular.ttf b/browser/bundle/fonts/linux/NotoSansWarangCiti-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansWarangCiti-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansWarangCiti-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansYi-Regular.ttf b/browser/bundle/fonts/linux/NotoSansYi-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansYi-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansYi-Regular.ttf diff --git a/bundle/fonts/linux/NotoSansZanabazarSquare-Regular.ttf b/browser/bundle/fonts/linux/NotoSansZanabazarSquare-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSansZanabazarSquare-Regular.ttf rename to browser/bundle/fonts/linux/NotoSansZanabazarSquare-Regular.ttf diff --git a/bundle/fonts/linux/NotoSerifArmenian-Regular.ttf b/browser/bundle/fonts/linux/NotoSerifArmenian-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSerifArmenian-Regular.ttf rename to browser/bundle/fonts/linux/NotoSerifArmenian-Regular.ttf diff --git a/bundle/fonts/linux/NotoSerifBalinese-Regular.ttf b/browser/bundle/fonts/linux/NotoSerifBalinese-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSerifBalinese-Regular.ttf rename to browser/bundle/fonts/linux/NotoSerifBalinese-Regular.ttf diff --git a/bundle/fonts/linux/NotoSerifBengali-Regular.ttf b/browser/bundle/fonts/linux/NotoSerifBengali-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSerifBengali-Regular.ttf rename to browser/bundle/fonts/linux/NotoSerifBengali-Regular.ttf diff --git a/bundle/fonts/linux/NotoSerifDevanagari-Regular.ttf b/browser/bundle/fonts/linux/NotoSerifDevanagari-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSerifDevanagari-Regular.ttf rename to browser/bundle/fonts/linux/NotoSerifDevanagari-Regular.ttf diff --git a/bundle/fonts/linux/NotoSerifDogra-Regular.ttf b/browser/bundle/fonts/linux/NotoSerifDogra-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSerifDogra-Regular.ttf rename to browser/bundle/fonts/linux/NotoSerifDogra-Regular.ttf diff --git a/bundle/fonts/linux/NotoSerifEthiopic-Regular.ttf b/browser/bundle/fonts/linux/NotoSerifEthiopic-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSerifEthiopic-Regular.ttf rename to browser/bundle/fonts/linux/NotoSerifEthiopic-Regular.ttf diff --git a/bundle/fonts/linux/NotoSerifGeorgian-Regular.ttf b/browser/bundle/fonts/linux/NotoSerifGeorgian-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSerifGeorgian-Regular.ttf rename to browser/bundle/fonts/linux/NotoSerifGeorgian-Regular.ttf diff --git a/bundle/fonts/linux/NotoSerifGrantha-Regular.ttf b/browser/bundle/fonts/linux/NotoSerifGrantha-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSerifGrantha-Regular.ttf rename to browser/bundle/fonts/linux/NotoSerifGrantha-Regular.ttf diff --git a/bundle/fonts/linux/NotoSerifGujarati-Regular.ttf b/browser/bundle/fonts/linux/NotoSerifGujarati-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSerifGujarati-Regular.ttf rename to browser/bundle/fonts/linux/NotoSerifGujarati-Regular.ttf diff --git a/bundle/fonts/linux/NotoSerifGurmukhi-Regular.ttf b/browser/bundle/fonts/linux/NotoSerifGurmukhi-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSerifGurmukhi-Regular.ttf rename to browser/bundle/fonts/linux/NotoSerifGurmukhi-Regular.ttf diff --git a/bundle/fonts/linux/NotoSerifHebrew-Regular.ttf b/browser/bundle/fonts/linux/NotoSerifHebrew-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSerifHebrew-Regular.ttf rename to browser/bundle/fonts/linux/NotoSerifHebrew-Regular.ttf diff --git a/bundle/fonts/linux/NotoSerifKannada-Regular.ttf b/browser/bundle/fonts/linux/NotoSerifKannada-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSerifKannada-Regular.ttf rename to browser/bundle/fonts/linux/NotoSerifKannada-Regular.ttf diff --git a/bundle/fonts/linux/NotoSerifKhmer-Regular.ttf b/browser/bundle/fonts/linux/NotoSerifKhmer-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSerifKhmer-Regular.ttf rename to browser/bundle/fonts/linux/NotoSerifKhmer-Regular.ttf diff --git a/bundle/fonts/linux/NotoSerifKhojki-Regular.ttf b/browser/bundle/fonts/linux/NotoSerifKhojki-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSerifKhojki-Regular.ttf rename to browser/bundle/fonts/linux/NotoSerifKhojki-Regular.ttf diff --git a/bundle/fonts/linux/NotoSerifLao-Regular.ttf b/browser/bundle/fonts/linux/NotoSerifLao-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSerifLao-Regular.ttf rename to browser/bundle/fonts/linux/NotoSerifLao-Regular.ttf diff --git a/bundle/fonts/linux/NotoSerifMalayalam-Regular.ttf b/browser/bundle/fonts/linux/NotoSerifMalayalam-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSerifMalayalam-Regular.ttf rename to browser/bundle/fonts/linux/NotoSerifMalayalam-Regular.ttf diff --git a/bundle/fonts/linux/NotoSerifMyanmar-Regular.ttf b/browser/bundle/fonts/linux/NotoSerifMyanmar-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSerifMyanmar-Regular.ttf rename to browser/bundle/fonts/linux/NotoSerifMyanmar-Regular.ttf diff --git a/bundle/fonts/linux/NotoSerifNPHmong-Regular.ttf b/browser/bundle/fonts/linux/NotoSerifNPHmong-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSerifNPHmong-Regular.ttf rename to browser/bundle/fonts/linux/NotoSerifNPHmong-Regular.ttf diff --git a/bundle/fonts/linux/NotoSerifSinhala-Regular.ttf b/browser/bundle/fonts/linux/NotoSerifSinhala-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSerifSinhala-Regular.ttf rename to browser/bundle/fonts/linux/NotoSerifSinhala-Regular.ttf diff --git a/bundle/fonts/linux/NotoSerifTamil-Regular.ttf b/browser/bundle/fonts/linux/NotoSerifTamil-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSerifTamil-Regular.ttf rename to browser/bundle/fonts/linux/NotoSerifTamil-Regular.ttf diff --git a/bundle/fonts/linux/NotoSerifTelugu-Regular.ttf b/browser/bundle/fonts/linux/NotoSerifTelugu-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSerifTelugu-Regular.ttf rename to browser/bundle/fonts/linux/NotoSerifTelugu-Regular.ttf diff --git a/bundle/fonts/linux/NotoSerifThai-Regular.ttf b/browser/bundle/fonts/linux/NotoSerifThai-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSerifThai-Regular.ttf rename to browser/bundle/fonts/linux/NotoSerifThai-Regular.ttf diff --git a/bundle/fonts/linux/NotoSerifTibetan-Regular.ttf b/browser/bundle/fonts/linux/NotoSerifTibetan-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSerifTibetan-Regular.ttf rename to browser/bundle/fonts/linux/NotoSerifTibetan-Regular.ttf diff --git a/bundle/fonts/linux/NotoSerifYezidi-Regular.ttf b/browser/bundle/fonts/linux/NotoSerifYezidi-Regular.ttf similarity index 100% rename from bundle/fonts/linux/NotoSerifYezidi-Regular.ttf rename to browser/bundle/fonts/linux/NotoSerifYezidi-Regular.ttf diff --git a/bundle/fonts/linux/STIXTwoMath-Regular.otf b/browser/bundle/fonts/linux/STIXTwoMath-Regular.otf similarity index 100% rename from bundle/fonts/linux/STIXTwoMath-Regular.otf rename to browser/bundle/fonts/linux/STIXTwoMath-Regular.otf diff --git a/bundle/fonts/linux/Tinos-Bold.ttf b/browser/bundle/fonts/linux/Tinos-Bold.ttf similarity index 100% rename from bundle/fonts/linux/Tinos-Bold.ttf rename to browser/bundle/fonts/linux/Tinos-Bold.ttf diff --git a/bundle/fonts/linux/Tinos-BoldItalic.ttf b/browser/bundle/fonts/linux/Tinos-BoldItalic.ttf similarity index 100% rename from bundle/fonts/linux/Tinos-BoldItalic.ttf rename to browser/bundle/fonts/linux/Tinos-BoldItalic.ttf diff --git a/bundle/fonts/linux/Tinos-Italic.ttf b/browser/bundle/fonts/linux/Tinos-Italic.ttf similarity index 100% rename from bundle/fonts/linux/Tinos-Italic.ttf rename to browser/bundle/fonts/linux/Tinos-Italic.ttf diff --git a/bundle/fonts/linux/Tinos-Regular.ttf b/browser/bundle/fonts/linux/Tinos-Regular.ttf similarity index 100% rename from bundle/fonts/linux/Tinos-Regular.ttf rename to browser/bundle/fonts/linux/Tinos-Regular.ttf diff --git a/bundle/fonts/linux/TwemojiMozilla.ttf b/browser/bundle/fonts/linux/TwemojiMozilla.ttf similarity index 100% rename from bundle/fonts/linux/TwemojiMozilla.ttf rename to browser/bundle/fonts/linux/TwemojiMozilla.ttf diff --git a/bundle/fonts/macos/Apple Braille Outline 6 Dot.ttf b/browser/bundle/fonts/macos/Apple Braille Outline 6 Dot.ttf similarity index 100% rename from bundle/fonts/macos/Apple Braille Outline 6 Dot.ttf rename to browser/bundle/fonts/macos/Apple Braille Outline 6 Dot.ttf diff --git a/bundle/fonts/macos/Apple Braille Outline 8 Dot.ttf b/browser/bundle/fonts/macos/Apple Braille Outline 8 Dot.ttf similarity index 100% rename from bundle/fonts/macos/Apple Braille Outline 8 Dot.ttf rename to browser/bundle/fonts/macos/Apple Braille Outline 8 Dot.ttf diff --git a/bundle/fonts/macos/Apple Braille Pinpoint 6 Dot.ttf b/browser/bundle/fonts/macos/Apple Braille Pinpoint 6 Dot.ttf similarity index 100% rename from bundle/fonts/macos/Apple Braille Pinpoint 6 Dot.ttf rename to browser/bundle/fonts/macos/Apple Braille Pinpoint 6 Dot.ttf diff --git a/bundle/fonts/macos/Apple Braille Pinpoint 8 Dot.ttf b/browser/bundle/fonts/macos/Apple Braille Pinpoint 8 Dot.ttf similarity index 100% rename from bundle/fonts/macos/Apple Braille Pinpoint 8 Dot.ttf rename to browser/bundle/fonts/macos/Apple Braille Pinpoint 8 Dot.ttf diff --git a/bundle/fonts/macos/Apple Braille.ttf b/browser/bundle/fonts/macos/Apple Braille.ttf similarity index 100% rename from bundle/fonts/macos/Apple Braille.ttf rename to browser/bundle/fonts/macos/Apple Braille.ttf diff --git a/bundle/fonts/macos/Apple Symbols.ttf b/browser/bundle/fonts/macos/Apple Symbols.ttf similarity index 100% rename from bundle/fonts/macos/Apple Symbols.ttf rename to browser/bundle/fonts/macos/Apple Symbols.ttf diff --git a/bundle/fonts/macos/AppleColorEmoji.ttf b/browser/bundle/fonts/macos/AppleColorEmoji.ttf similarity index 100% rename from bundle/fonts/macos/AppleColorEmoji.ttf rename to browser/bundle/fonts/macos/AppleColorEmoji.ttf diff --git a/bundle/fonts/macos/AppleSDGothicNeo.ttc b/browser/bundle/fonts/macos/AppleSDGothicNeo.ttc similarity index 100% rename from bundle/fonts/macos/AppleSDGothicNeo.ttc rename to browser/bundle/fonts/macos/AppleSDGothicNeo.ttc diff --git a/bundle/fonts/macos/AquaKana.ttc b/browser/bundle/fonts/macos/AquaKana.ttc similarity index 100% rename from bundle/fonts/macos/AquaKana.ttc rename to browser/bundle/fonts/macos/AquaKana.ttc diff --git a/bundle/fonts/macos/ArialHB.ttc b/browser/bundle/fonts/macos/ArialHB.ttc similarity index 100% rename from bundle/fonts/macos/ArialHB.ttc rename to browser/bundle/fonts/macos/ArialHB.ttc diff --git a/bundle/fonts/macos/Avenir Next Condensed.ttc b/browser/bundle/fonts/macos/Avenir Next Condensed.ttc similarity index 100% rename from bundle/fonts/macos/Avenir Next Condensed.ttc rename to browser/bundle/fonts/macos/Avenir Next Condensed.ttc diff --git a/bundle/fonts/macos/Avenir Next.ttc b/browser/bundle/fonts/macos/Avenir Next.ttc similarity index 100% rename from bundle/fonts/macos/Avenir Next.ttc rename to browser/bundle/fonts/macos/Avenir Next.ttc diff --git a/bundle/fonts/macos/Avenir.ttc b/browser/bundle/fonts/macos/Avenir.ttc similarity index 100% rename from bundle/fonts/macos/Avenir.ttc rename to browser/bundle/fonts/macos/Avenir.ttc diff --git a/bundle/fonts/macos/Courier.ttc b/browser/bundle/fonts/macos/Courier.ttc similarity index 100% rename from bundle/fonts/macos/Courier.ttc rename to browser/bundle/fonts/macos/Courier.ttc diff --git a/bundle/fonts/macos/GeezaPro.ttc b/browser/bundle/fonts/macos/GeezaPro.ttc similarity index 100% rename from bundle/fonts/macos/GeezaPro.ttc rename to browser/bundle/fonts/macos/GeezaPro.ttc diff --git a/bundle/fonts/macos/Geneva.ttf b/browser/bundle/fonts/macos/Geneva.ttf similarity index 100% rename from bundle/fonts/macos/Geneva.ttf rename to browser/bundle/fonts/macos/Geneva.ttf diff --git a/bundle/fonts/macos/HelveLTMM b/browser/bundle/fonts/macos/HelveLTMM similarity index 100% rename from bundle/fonts/macos/HelveLTMM rename to browser/bundle/fonts/macos/HelveLTMM diff --git a/bundle/fonts/macos/Helvetica.ttc b/browser/bundle/fonts/macos/Helvetica.ttc similarity index 100% rename from bundle/fonts/macos/Helvetica.ttc rename to browser/bundle/fonts/macos/Helvetica.ttc diff --git a/bundle/fonts/macos/HelveticaNeue.ttc b/browser/bundle/fonts/macos/HelveticaNeue.ttc similarity index 100% rename from bundle/fonts/macos/HelveticaNeue.ttc rename to browser/bundle/fonts/macos/HelveticaNeue.ttc diff --git a/bundle/fonts/macos/Hiragino Sans GB.ttc b/browser/bundle/fonts/macos/Hiragino Sans GB.ttc similarity index 100% rename from bundle/fonts/macos/Hiragino Sans GB.ttc rename to browser/bundle/fonts/macos/Hiragino Sans GB.ttc diff --git a/bundle/fonts/macos/Keyboard.ttf b/browser/bundle/fonts/macos/Keyboard.ttf similarity index 100% rename from bundle/fonts/macos/Keyboard.ttf rename to browser/bundle/fonts/macos/Keyboard.ttf diff --git a/bundle/fonts/macos/Kohinoor.ttc b/browser/bundle/fonts/macos/Kohinoor.ttc similarity index 100% rename from bundle/fonts/macos/Kohinoor.ttc rename to browser/bundle/fonts/macos/Kohinoor.ttc diff --git a/bundle/fonts/macos/KohinoorBangla.ttc b/browser/bundle/fonts/macos/KohinoorBangla.ttc similarity index 100% rename from bundle/fonts/macos/KohinoorBangla.ttc rename to browser/bundle/fonts/macos/KohinoorBangla.ttc diff --git a/bundle/fonts/macos/KohinoorGujarati.ttc b/browser/bundle/fonts/macos/KohinoorGujarati.ttc similarity index 100% rename from bundle/fonts/macos/KohinoorGujarati.ttc rename to browser/bundle/fonts/macos/KohinoorGujarati.ttc diff --git a/bundle/fonts/macos/KohinoorTelugu.ttc b/browser/bundle/fonts/macos/KohinoorTelugu.ttc similarity index 100% rename from bundle/fonts/macos/KohinoorTelugu.ttc rename to browser/bundle/fonts/macos/KohinoorTelugu.ttc diff --git a/bundle/fonts/macos/LastResort.otf b/browser/bundle/fonts/macos/LastResort.otf similarity index 100% rename from bundle/fonts/macos/LastResort.otf rename to browser/bundle/fonts/macos/LastResort.otf diff --git a/bundle/fonts/macos/LucidaGrande.ttc b/browser/bundle/fonts/macos/LucidaGrande.ttc similarity index 100% rename from bundle/fonts/macos/LucidaGrande.ttc rename to browser/bundle/fonts/macos/LucidaGrande.ttc diff --git a/bundle/fonts/macos/MarkerFelt.ttc b/browser/bundle/fonts/macos/MarkerFelt.ttc similarity index 100% rename from bundle/fonts/macos/MarkerFelt.ttc rename to browser/bundle/fonts/macos/MarkerFelt.ttc diff --git a/bundle/fonts/macos/Menlo.ttc b/browser/bundle/fonts/macos/Menlo.ttc similarity index 100% rename from bundle/fonts/macos/Menlo.ttc rename to browser/bundle/fonts/macos/Menlo.ttc diff --git a/bundle/fonts/macos/Monaco.ttf b/browser/bundle/fonts/macos/Monaco.ttf similarity index 100% rename from bundle/fonts/macos/Monaco.ttf rename to browser/bundle/fonts/macos/Monaco.ttf diff --git a/bundle/fonts/macos/MuktaMahee.ttc b/browser/bundle/fonts/macos/MuktaMahee.ttc similarity index 100% rename from bundle/fonts/macos/MuktaMahee.ttc rename to browser/bundle/fonts/macos/MuktaMahee.ttc diff --git a/bundle/fonts/macos/NewYork.ttf b/browser/bundle/fonts/macos/NewYork.ttf similarity index 100% rename from bundle/fonts/macos/NewYork.ttf rename to browser/bundle/fonts/macos/NewYork.ttf diff --git a/bundle/fonts/macos/NewYorkItalic.ttf b/browser/bundle/fonts/macos/NewYorkItalic.ttf similarity index 100% rename from bundle/fonts/macos/NewYorkItalic.ttf rename to browser/bundle/fonts/macos/NewYorkItalic.ttf diff --git a/bundle/fonts/macos/Noteworthy.ttc b/browser/bundle/fonts/macos/Noteworthy.ttc similarity index 100% rename from bundle/fonts/macos/Noteworthy.ttc rename to browser/bundle/fonts/macos/Noteworthy.ttc diff --git a/bundle/fonts/macos/NotoNastaliq.ttc b/browser/bundle/fonts/macos/NotoNastaliq.ttc similarity index 100% rename from bundle/fonts/macos/NotoNastaliq.ttc rename to browser/bundle/fonts/macos/NotoNastaliq.ttc diff --git a/bundle/fonts/macos/NotoSansArmenian.ttc b/browser/bundle/fonts/macos/NotoSansArmenian.ttc similarity index 100% rename from bundle/fonts/macos/NotoSansArmenian.ttc rename to browser/bundle/fonts/macos/NotoSansArmenian.ttc diff --git a/bundle/fonts/macos/NotoSansKannada.ttc b/browser/bundle/fonts/macos/NotoSansKannada.ttc similarity index 100% rename from bundle/fonts/macos/NotoSansKannada.ttc rename to browser/bundle/fonts/macos/NotoSansKannada.ttc diff --git a/bundle/fonts/macos/NotoSansMyanmar.ttc b/browser/bundle/fonts/macos/NotoSansMyanmar.ttc similarity index 100% rename from bundle/fonts/macos/NotoSansMyanmar.ttc rename to browser/bundle/fonts/macos/NotoSansMyanmar.ttc diff --git a/bundle/fonts/macos/NotoSansOriya.ttc b/browser/bundle/fonts/macos/NotoSansOriya.ttc similarity index 100% rename from bundle/fonts/macos/NotoSansOriya.ttc rename to browser/bundle/fonts/macos/NotoSansOriya.ttc diff --git a/bundle/fonts/macos/NotoSerifMyanmar.ttc b/browser/bundle/fonts/macos/NotoSerifMyanmar.ttc similarity index 100% rename from bundle/fonts/macos/NotoSerifMyanmar.ttc rename to browser/bundle/fonts/macos/NotoSerifMyanmar.ttc diff --git a/bundle/fonts/macos/Optima.ttc b/browser/bundle/fonts/macos/Optima.ttc similarity index 100% rename from bundle/fonts/macos/Optima.ttc rename to browser/bundle/fonts/macos/Optima.ttc diff --git a/bundle/fonts/macos/Palatino.ttc b/browser/bundle/fonts/macos/Palatino.ttc similarity index 100% rename from bundle/fonts/macos/Palatino.ttc rename to browser/bundle/fonts/macos/Palatino.ttc diff --git a/bundle/fonts/macos/PingFang.ttc b/browser/bundle/fonts/macos/PingFang.ttc similarity index 100% rename from bundle/fonts/macos/PingFang.ttc rename to browser/bundle/fonts/macos/PingFang.ttc diff --git a/bundle/fonts/macos/SFArabic.ttf b/browser/bundle/fonts/macos/SFArabic.ttf similarity index 100% rename from bundle/fonts/macos/SFArabic.ttf rename to browser/bundle/fonts/macos/SFArabic.ttf diff --git a/bundle/fonts/macos/SFArabicRounded.ttf b/browser/bundle/fonts/macos/SFArabicRounded.ttf similarity index 100% rename from bundle/fonts/macos/SFArabicRounded.ttf rename to browser/bundle/fonts/macos/SFArabicRounded.ttf diff --git a/bundle/fonts/macos/SFCompact.ttf b/browser/bundle/fonts/macos/SFCompact.ttf similarity index 100% rename from bundle/fonts/macos/SFCompact.ttf rename to browser/bundle/fonts/macos/SFCompact.ttf diff --git a/bundle/fonts/macos/SFCompactItalic.ttf b/browser/bundle/fonts/macos/SFCompactItalic.ttf similarity index 100% rename from bundle/fonts/macos/SFCompactItalic.ttf rename to browser/bundle/fonts/macos/SFCompactItalic.ttf diff --git a/bundle/fonts/macos/SFCompactRounded.ttf b/browser/bundle/fonts/macos/SFCompactRounded.ttf similarity index 100% rename from bundle/fonts/macos/SFCompactRounded.ttf rename to browser/bundle/fonts/macos/SFCompactRounded.ttf diff --git a/bundle/fonts/macos/SFNS.ttf b/browser/bundle/fonts/macos/SFNS.ttf similarity index 100% rename from bundle/fonts/macos/SFNS.ttf rename to browser/bundle/fonts/macos/SFNS.ttf diff --git a/bundle/fonts/macos/SFNSItalic.ttf b/browser/bundle/fonts/macos/SFNSItalic.ttf similarity index 100% rename from bundle/fonts/macos/SFNSItalic.ttf rename to browser/bundle/fonts/macos/SFNSItalic.ttf diff --git a/bundle/fonts/macos/SFNSMono.ttf b/browser/bundle/fonts/macos/SFNSMono.ttf similarity index 100% rename from bundle/fonts/macos/SFNSMono.ttf rename to browser/bundle/fonts/macos/SFNSMono.ttf diff --git a/bundle/fonts/macos/SFNSMonoItalic.ttf b/browser/bundle/fonts/macos/SFNSMonoItalic.ttf similarity index 100% rename from bundle/fonts/macos/SFNSMonoItalic.ttf rename to browser/bundle/fonts/macos/SFNSMonoItalic.ttf diff --git a/bundle/fonts/macos/SFNSRounded.ttf b/browser/bundle/fonts/macos/SFNSRounded.ttf similarity index 100% rename from bundle/fonts/macos/SFNSRounded.ttf rename to browser/bundle/fonts/macos/SFNSRounded.ttf diff --git a/bundle/fonts/macos/STHeiti Light.ttc b/browser/bundle/fonts{"code":"deadline_exceeded","msg":"operation timed out"}