master 59a7c3f655ed cached
197 files
3.4 MB
911.0k tokens
253 symbols
1 requests
Download .txt
Showing preview only (3,641K chars total). Download the full file or copy to clipboard to get everything.
Repository: ungoogled-software/ungoogled-chromium
Branch: master
Commit: 59a7c3f655ed
Files: 197
Total size: 3.4 MB

Directory structure:
gitextract_kl8f_uin/

├── .cirrus.yml
├── .cirrus_Dockerfile
├── .cirrus_requirements.txt
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bugreport.yml
│   │   ├── create-an--updating-to-chromium-x-x-x-x-.md
│   │   └── feature_request.yml
│   ├── PULL_REQUEST_TEMPLATE.md
│   └── workflows/
│       ├── new_version_check.yml
│       ├── release-on-tag.yml
│       └── stale.yml
├── .gitignore
├── .style.yapf
├── CODEOWNERS
├── LICENSE
├── README.md
├── SUPPORT.md
├── chromium_version.txt
├── devutils/
│   ├── .coveragerc
│   ├── README.md
│   ├── __init__.py
│   ├── check_all_code.sh
│   ├── check_downloads_ini.py
│   ├── check_files_exist.py
│   ├── check_gn_flags.py
│   ├── check_patch_files.py
│   ├── print_tag_version.sh
│   ├── pytest.ini
│   ├── run_devutils_pylint.py
│   ├── run_devutils_tests.sh
│   ├── run_devutils_yapf.sh
│   ├── run_other_pylint.py
│   ├── run_other_yapf.sh
│   ├── run_utils_pylint.py
│   ├── run_utils_tests.sh
│   ├── run_utils_yapf.sh
│   ├── set_quilt_vars.fish
│   ├── set_quilt_vars.sh
│   ├── tests/
│   │   ├── __init__.py
│   │   ├── test_check_patch_files.py
│   │   └── test_validate_patches.py
│   ├── third_party/
│   │   ├── README.md
│   │   ├── __init__.py
│   │   └── unidiff/
│   │       ├── __init__.py
│   │       ├── __version__.py
│   │       ├── constants.py
│   │       ├── errors.py
│   │       └── patch.py
│   ├── update_lists.py
│   ├── update_platform_patches.py
│   ├── validate_config.py
│   └── validate_patches.py
├── docs/
│   ├── building.md
│   ├── contributing.md
│   ├── default_settings.md
│   ├── design.md
│   ├── developing.md
│   ├── flags.md
│   ├── platforms.md
│   └── repo_management.md
├── domain_regex.list
├── domain_substitution.list
├── downloads.ini
├── flags.gn
├── patches/
│   ├── core/
│   │   ├── bromite/
│   │   │   └── disable-fetching-field-trials.patch
│   │   ├── inox-patchset/
│   │   │   ├── 0001-fix-building-without-safebrowsing.patch
│   │   │   ├── 0003-disable-autofill-download-manager.patch
│   │   │   ├── 0005-disable-default-extensions.patch
│   │   │   ├── 0015-disable-update-pings.patch
│   │   │   └── 0021-disable-rlz.patch
│   │   ├── iridium-browser/
│   │   │   ├── all-add-trk-prefixes-to-possibly-evil-connections.patch
│   │   │   ├── safe_browsing-disable-incident-reporting.patch
│   │   │   └── safe_browsing-disable-reporting-of-safebrowsing-over.patch
│   │   └── ungoogled-chromium/
│   │       ├── block-requests.patch
│   │       ├── block-trk-and-subdomains.patch
│   │       ├── build-with-wasm-rollup.patch
│   │       ├── disable-crash-reporter.patch
│   │       ├── disable-domain-reliability.patch
│   │       ├── disable-fonts-googleapis-references.patch
│   │       ├── disable-gaia.patch
│   │       ├── disable-gcm.patch
│   │       ├── disable-google-host-detection.patch
│   │       ├── disable-mei-preload.patch
│   │       ├── disable-network-time-tracker.patch
│   │       ├── disable-privacy-sandbox.patch
│   │       ├── disable-profile-avatar-downloading.patch
│   │       ├── disable-untraceable-urls.patch
│   │       ├── disable-webrtc-log-uploader.patch
│   │       ├── disable-webstore-urls.patch
│   │       ├── doh-changes.patch
│   │       ├── extensions-manifestv2.patch
│   │       ├── fix-building-with-prunned-binaries.patch
│   │       ├── fix-building-without-safebrowsing.patch
│   │       ├── fix-learn-doubleclick-hsts.patch
│   │       ├── move-js-optimizer-unfamiliar-sites.patch
│   │       ├── remove-f1-shortcut.patch
│   │       ├── remove-unused-preferences-fields.patch
│   │       ├── replace-google-search-engine-with-nosearch.patch
│   │       └── toggle-translation-via-switch.patch
│   ├── extra/
│   │   ├── bromite/
│   │   │   ├── fingerprinting-flags-client-rects-and-measuretext.patch
│   │   │   ├── flag-fingerprinting-canvas-image-data-noise.patch
│   │   │   └── flag-max-connections-per-host.patch
│   │   ├── debian/
│   │   │   └── disable/
│   │   │       └── google-api-warning.patch
│   │   ├── inox-patchset/
│   │   │   ├── 0006-modify-default-prefs.patch
│   │   │   ├── 0008-restore-classic-ntp.patch
│   │   │   ├── 0013-disable-missing-key-warning.patch
│   │   │   ├── 0016-chromium-sandbox-pie.patch
│   │   │   └── 0019-disable-battery-status-service.patch
│   │   ├── iridium-browser/
│   │   │   ├── Remove-EV-certificates.patch
│   │   │   ├── browser-disable-profile-auto-import-on-first-run.patch
│   │   │   ├── mime_util-force-text-x-suse-ymp-to-be-downloaded.patch
│   │   │   ├── prefs-always-prompt-for-download-directory-by-defaul.patch
│   │   │   └── updater-disable-auto-update.patch
│   │   └── ungoogled-chromium/
│   │       ├── add-components-ungoogled.patch
│   │       ├── add-credits.patch
│   │       ├── add-extra-channel-info.patch
│   │       ├── add-flag-for-bookmark-bar-ntp.patch
│   │       ├── add-flag-for-close-confirmation.patch
│   │       ├── add-flag-for-custom-ntp.patch
│   │       ├── add-flag-for-disabling-link-drag.patch
│   │       ├── add-flag-for-grab-handle.patch
│   │       ├── add-flag-for-incognito-themes.patch
│   │       ├── add-flag-for-omnibox-autocomplete-filtering.patch
│   │       ├── add-flag-for-qr-generator.patch
│   │       ├── add-flag-for-search-engine-collection.patch
│   │       ├── add-flag-for-tab-hover-cards.patch
│   │       ├── add-flag-for-tabsearch-button.patch
│   │       ├── add-flag-to-change-http-accept-header.patch
│   │       ├── add-flag-to-clear-data-on-exit.patch
│   │       ├── add-flag-to-close-window-with-last-tab.patch
│   │       ├── add-flag-to-configure-extension-downloading.patch
│   │       ├── add-flag-to-convert-popups-to-tabs.patch
│   │       ├── add-flag-to-disable-beforeunload.patch
│   │       ├── add-flag-to-disable-local-history-expiration.patch
│   │       ├── add-flag-to-disable-sharing-hub.patch
│   │       ├── add-flag-to-disable-tls-grease.patch
│   │       ├── add-flag-to-force-punycode-hostnames.patch
│   │       ├── add-flag-to-hide-crashed-bubble.patch
│   │       ├── add-flag-to-hide-extensions-menu.patch
│   │       ├── add-flag-to-hide-fullscreen-exit-ui.patch
│   │       ├── add-flag-to-hide-tab-close-buttons.patch
│   │       ├── add-flag-to-increase-incognito-storage-quota.patch
│   │       ├── add-flag-to-reduce-system-info.patch
│   │       ├── add-flag-to-remove-client-hints.patch
│   │       ├── add-flag-to-scroll-tabs.patch
│   │       ├── add-flag-to-show-avatar-button.patch
│   │       ├── add-flag-to-spoof-webgl-renderer-info.patch
│   │       ├── add-flags-for-existing-switches.patch
│   │       ├── add-flags-for-referrer-customization.patch
│   │       ├── add-ipv6-probing-option.patch
│   │       ├── add-suggestions-url-field.patch
│   │       ├── add-ungoogled-flag-headers.patch
│   │       ├── default-webrtc-ip-handling-policy.patch
│   │       ├── disable-ai-search-shortcuts.patch
│   │       ├── disable-chromelabs.patch
│   │       ├── disable-dial-repeating-discovery.patch
│   │       ├── disable-download-quarantine.patch
│   │       ├── disable-fedcm-by-default.patch
│   │       ├── disable-formatting-in-omnibox.patch
│   │       ├── disable-intranet-redirect-detector.patch
│   │       ├── enable-certificate-transparency-and-add-flag.patch
│   │       ├── enable-extra-locales.patch
│   │       ├── enable-menu-on-reload-button.patch
│   │       ├── enable-page-saving-on-more-pages.patch
│   │       ├── enable-paste-and-go-new-tab-button.patch
│   │       ├── first-run-page.patch
│   │       ├── fix-building-without-mdns-and-service-discovery.patch
│   │       ├── fix-distilled-icons.patch
│   │       ├── keep-expired-flags.patch
│   │       ├── prepopulated-search-engines.patch
│   │       ├── remove-disable-setuid-sandbox-as-bad-flag.patch
│   │       ├── remove-pac-size-limit.patch
│   │       └── remove-uneeded-ui.patch
│   ├── series
│   └── upstream-fixes/
│       └── missing-dependencies.patch
├── pruning.list
├── revision.txt
├── shell.nix
└── utils/
    ├── .coveragerc
    ├── __init__.py
    ├── _common.py
    ├── _extraction.py
    ├── clone.py
    ├── depot_tools.patch
    ├── domain_substitution.py
    ├── downloads.py
    ├── filescfg.py
    ├── gsutil.patch
    ├── make_domsub_script.py
    ├── patches.py
    ├── prune_binaries.py
    ├── pytest.ini
    ├── tests/
    │   ├── __init__.py
    │   ├── test_domain_substitution.py
    │   └── test_patches.py
    └── third_party/
        ├── README.md
        ├── __init__.py
        └── schema.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .cirrus.yml
================================================
env:
    CIRRUS_CLONE_DEPTH: 1

container:
    dockerfile: .cirrus_Dockerfile
    cpu: 8
    memory: 32G
    use_in_memory_disk: true

code_check_task:
    pip_cache:
        folder: /usr/local/lib/python3.10/site-packages
        fingerprint_script: cat .cirrus_requirements.txt
        populate_script: pip install -r .cirrus_requirements.txt
    utils_script:
        - python3 -m yapf --style '.style.yapf' -e '*/third_party/*' -rpd utils
        - ./devutils/run_utils_pylint.py --hide-fixme
        - ./devutils/run_utils_tests.sh
    devutils_script:
        - python3 -m yapf --style '.style.yapf' -e '*/third_party/*' -rpd devutils
        - ./devutils/run_devutils_pylint.py --hide-fixme
        - ./devutils/run_devutils_tests.sh

validate_config_task:
    validate_config_script: ./devutils/validate_config.py

validate_with_source_task:
    pip_cache:
        folder: /usr/local/lib/python3.10/site-packages
        fingerprint_script: cat .cirrus_requirements.txt
        populate_script: pip install -r .cirrus_requirements.txt
    chromium_download_script: |
        # These directories will not exist when this is called, unless cache retrieval
        # fails and leaves partially-complete files around.
        rm -rf chromium_src
        rm -rf chromium_download_cache
        mkdir chromium_download_cache
        # Attempt to download tarball
        if ! ./utils/downloads.py retrieve -i downloads.ini -c chromium_download_cache ; then
          # If tarball is not available, attempt a clone
          ./utils/clone.py -o chromium_src
          rm -rf chromium_src/uc_staging
          find chromium_src -type d -name '.git' -exec rm -rf "{}" \; -prune
          tar cf chromium_download_cache/chromium-$(cat chromium_version.txt)-lite.tar.xz \
            --transform s/chromium_src/chromium-$(cat chromium_version.txt)/ chromium_src
        fi
    unpack_source_script: |
        if [ ! -d chromium_src ]; then
          ./utils/downloads.py unpack -i downloads.ini -c chromium_download_cache chromium_src
        fi
    validate_patches_script:
        - ./devutils/validate_patches.py -l chromium_src -v
    validate_lists_script:
        # NOTE: This check is prone to false positives, but not false negatives.
        - ./devutils/check_files_exist.py chromium_src pruning.list domain_substitution.list

# vim: set expandtab shiftwidth=4 softtabstop=4:


================================================
FILE: .cirrus_Dockerfile
================================================
# Dockerfile for Python 3 with xz-utils (for tar.xz unpacking)

FROM python:3.10-slim-bookworm

RUN apt update && apt install -y xz-utils patch axel curl git


================================================
FILE: .cirrus_requirements.txt
================================================
# Based on Python package versions in Debian bookworm
# https://packages.debian.org/bookworm/python/
astroid==2.14.2 # via pylint
pylint==2.16.2
pytest-cov==4.0.0
pytest==7.2.1
httplib2==0.20.4
requests==2.28.1
yapf==0.32.0


================================================
FILE: .github/ISSUE_TEMPLATE/bugreport.yml
================================================
name: Bug Report
description: Report a bug building or running ungoogled-chromium
labels: ["bug"]
body:
  - type: markdown
    attributes:
      value: |
        Before submitting this issue, please confirm if you should submit it to a [platform-specific issue tracker instead](https://github.com/ungoogled-software/ungoogled-chromium/blob/master/SUPPORT.md#platform-specific-support)

        Otherwise, your issue may not be noticed. In addition, please read through the [SUPPORT.md](https://github.com/ungoogled-software/ungoogled-chromium/blob/master/SUPPORT.md) first
  - type: dropdown
    id: os
    attributes:
      label: OS/Platform
      description: OS/Platform you are running ungoogled-chromium on
      options:
          - Android
          - Arch Linux
          - Debian, Ubuntu, and derivatives
          - Fedora and CentOS
          - Gentoo
          - GNU Guix
          - macOS
          - NixOS
          - Other Linux (please specify)
          - Portable Linux
          - Slackware
          - Windows
    validations:
        required: true
  - type: dropdown
    id: install
    attributes:
      label: Installed
      description: How was ungoogled-chromium installed on your OS/Platform
      options:
          - Compiled from source
          - OS/Platform's package manager
          - Flatpak
          - https://ungoogled-software.github.io/ungoogled-chromium-binaries/
          - Other (please use 'Additional context' field below to mention how exactly)
    validations:
        required: true
  - type: input
    id: version
    attributes:
      label: Version
      description: ungoogled-chromium version. Could be copied from chrome://settings/help
    validations:
      required: true
  - type: checkboxes
    attributes:
      label: Have you tested that this is not an upstream issue or an issue with your configuration?
      options:
        - label: I have tried reproducing this issue in Chrome and it could not be reproduced there
        - label: I have tried reproducing this issue in vanilla Chromium and it could not be reproduced there
        - label: I have tried reproducing this issue in ungoogled-chromium with a new and empty profile using `--user-data-dir` command line argument and it could not be reproduced there
  - type: input
    id: description
    attributes:
      label: Description
      description: A clear and concise description (in one line) of what the bug is
    validations:
      required: true
  - type: textarea
    id: repro
    attributes:
      label: How to Reproduce?
      description: Steps to reproduce the behaviour
      placeholder: |
        1. Go to '...'
        2. Click on '....'
        3. Scroll down to '....'
        4. See error
    validations:
      required: true
  - type: textarea
    id: actual
    attributes:
      label: Actual behaviour
      description: A clear and concise description of what happens
    validations:
      required: true
  - type: textarea
    id: expected
    attributes:
      label: Expected behaviour
      description: A clear and concise description of what you expected to happen
    validations:
      required: true
  - type: textarea
    id: logs
    attributes:
      label: Relevant log output
      description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
      render: sh
  - type: textarea
    id: additional
    attributes:
      label: Additional context
      description: Add any other context about the problem here. If applicable, add screenshots to help explain your problem.


================================================
FILE: .github/ISSUE_TEMPLATE/create-an--updating-to-chromium-x-x-x-x-.md
================================================
---
name: Create an "Updating to Chromium x.x.x.x"
about: For letting the community track progress to a new stable Chromium
title: Updating to Chromium {{ env.VERSION }} on {{ env.PLATFORM }}
labels: update
assignees: ''

---

Chromium stable channel for {{ env.PLATFORM }} has been updated to a newer version: {{ env.VERSION }}.

If you are willing to work on updating the patches and lists, please leave a comment in this issue in order to facilitate better coordination and avoid wasted/duplicated efforts.

If you'd like to increase visibility of your progress or get early feedback/advice, consider creating a [Draft Pull Request](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests#draft-pull-requests). Finally, make sure to reference this issue in your PR. Please make sure to read [/docs/developing.md](https://github.com/ungoogled-software/ungoogled-chromium/blob/master/docs/developing.md#updating-patches) for guidance.

Feel free to raise issues or questions throughout the process here. However, please refrain from asking for ETAs unless no visible progress has been made here or in the developer's PR for a while (e.g. 2 weeks).

{{ env.NOTIFY_MAINTAINERS }}


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.yml
================================================
name: Feature request
description: Suggest an idea
labels: ["enhancement", "help wanted"]
body:
  - type: markdown
    attributes:
      value: |
        Before submitting this feature request, please confirm if you should submit it to a [platform-specific issue tracker instead](https://github.com/ungoogled-software/ungoogled-chromium/blob/master/SUPPORT.md#platform-specific-support)

        Otherwise, your issue may not be noticed. In addition, please read through the [SUPPORT.md](https://github.com/ungoogled-software/ungoogled-chromium/blob/master/SUPPORT.md) first
  - type: input
    id: description
    attributes:
      label: Description
      description: A clear and concise description (in one line) of what your suggestion is
    validations:
      required: true
  - type: checkboxes
    attributes:
      label: Who's implementing?
      options:
        - label: I'm willing to implement this feature myself
  - type: textarea
    id: prob
    attributes:
      label: The problem
      description: Please describe the problem you are solving or new feature you're suggesting
      placeholder: I'm always frustrated when [...] happens
    validations:
      required: true
  - type: textarea
    id: sol
    attributes:
      label: Possible solutions
      description: Please describe possible solution(-s) to The Problem
    validations:
      required: true
  - type: textarea
    id: alt
    attributes:
      label: Alternatives
      description: Please describe alternatives you've considered, if any
  - type: textarea
    id: additional
    attributes:
      label: Additional context
      description: Add any other context about the feature request here


================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
*(Please ensure you have read SUPPORT.md and docs/contributing.md before submitting the Pull Request)*


================================================
FILE: .github/workflows/new_version_check.yml
================================================
name: New version check

on:
  # enabling manual trigger
  workflow_dispatch:
  # running every hour
  schedule:
    - cron: '48 * * * *'

jobs:
  check:
    # do not run in forks
    if: github.repository == 'ungoogled-software/ungoogled-chromium'
    runs-on: ubuntu-latest
    steps:
      - name: Set maintainer groups
        id: maintainers
        run: |
          echo "all=@networkException" >> $GITHUB_OUTPUT
          echo "linux=@rany2 @clickot @emilylange" >> $GITHUB_OUTPUT
          echo "windows=" >> $GITHUB_OUTPUT
          echo "macos=" >> $GITHUB_OUTPUT
      - name: Get the latest Chromium version
        id: latest-version
        run: |
          set -eo pipefail
          BASE_URL="https://versionhistory.googleapis.com/v1/chrome/platforms"
          END_URL="channels/stable/versions/all/releases?filter=endtime%3Dnone%2Cfraction%3E%3D0.5&order_by=version%20desc"
          JQ_FILTER='if .releases | select(type=="array") | length > 0 then .releases | first | .version else "null" end'
          for platform in linux win mac; do
            printf %s "${platform}_version=" >> $GITHUB_OUTPUT
            curl -sf "${BASE_URL}/${platform}/${END_URL}" | jq -re "${JQ_FILTER}" >> $GITHUB_OUTPUT
          done
      - uses: actions/checkout@v3
      - name: Create Issue for all platforms
        if: |
          contains(steps.latest-version.outputs.win_version, steps.latest-version.outputs.mac_version) &&
          contains(steps.latest-version.outputs.mac_version, steps.latest-version.outputs.linux_version) &&
          !contains(steps.latest-version.outputs.linux_version, 'null')
        uses: dblock/create-a-github-issue@v3
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          VERSION: ${{ steps.latest-version.outputs.linux_version }}
          PLATFORM: all platforms
          NOTIFY_MAINTAINERS: "${{ steps.maintainers.outputs.all }} ${{ steps.maintainers.outputs.linux }} ${{ steps.maintainers.outputs.windows }} ${{ steps.maintainers.outputs.macos }}"
        with:
          update_existing: false
          search_existing: all
          filename:  .github/ISSUE_TEMPLATE/create-an--updating-to-chromium-x-x-x-x-.md
      - name: Create Issue for Linux
        if: |
          (
            !contains(steps.latest-version.outputs.win_version, steps.latest-version.outputs.mac_version) ||
            !contains(steps.latest-version.outputs.mac_version, steps.latest-version.outputs.linux_version)
          ) && !contains(steps.latest-version.outputs.linux_version, 'null')
        uses: dblock/create-a-github-issue@v3
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          VERSION: ${{ steps.latest-version.outputs.linux_version }}
          PLATFORM: Linux
          NOTIFY_MAINTAINERS: "${{ steps.maintainers.outputs.all }} ${{ steps.maintainers.outputs.linux }}"
        with:
          update_existing: false
          search_existing: all
          filename:  .github/ISSUE_TEMPLATE/create-an--updating-to-chromium-x-x-x-x-.md
      - name: Create Issue for macOS
        if: |
          (
            !contains(steps.latest-version.outputs.win_version, steps.latest-version.outputs.mac_version) ||
            !contains(steps.latest-version.outputs.mac_version, steps.latest-version.outputs.linux_version)
          ) && !contains(steps.latest-version.outputs.mac_version, 'null')
        uses: dblock/create-a-github-issue@v3
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          VERSION: ${{ steps.latest-version.outputs.mac_version }}
          PLATFORM: macOS
          NOTIFY_MAINTAINERS: "${{ steps.maintainers.outputs.all }} ${{ steps.maintainers.outputs.macos }}"
        with:
          update_existing: false
          search_existing: all
          filename:  .github/ISSUE_TEMPLATE/create-an--updating-to-chromium-x-x-x-x-.md
      - name: Create Issue for Windows
        if: |
          (
            !contains(steps.latest-version.outputs.win_version, steps.latest-version.outputs.mac_version) ||
            !contains(steps.latest-version.outputs.mac_version, steps.latest-version.outputs.linux_version)
          ) && !contains(steps.latest-version.outputs.win_version, 'null')
        uses: dblock/create-a-github-issue@v3
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          VERSION: ${{ steps.latest-version.outputs.win_version }}
          PLATFORM: Windows
          NOTIFY_MAINTAINERS: "${{ steps.maintainers.outputs.all }} ${{ steps.maintainers.outputs.windows }}"
        with:
          update_existing: false
          search_existing: all
          filename:  .github/ISSUE_TEMPLATE/create-an--updating-to-chromium-x-x-x-x-.md


================================================
FILE: .github/workflows/release-on-tag.yml
================================================
name: Create new empty release

on:
  push:
    tags:
      - '*'

jobs:
  build:
    name: Create Release
    runs-on: ubuntu-latest
    steps:
      - name: Create Release
        id: create_release
        uses: actions/create-release@latest
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ github.ref }}
          release_name: ${{ github.ref }}
          draft: false
          prerelease: false


================================================
FILE: .github/workflows/stale.yml
================================================
name: 'Close stale issues and PRs'

on:
    schedule:
        - cron: '30 1 * * *'

permissions:
    issues: write
    pull-requests: write

jobs:
    stale:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/stale@v6
            with:
                exempt-all-milestones: true
                any-of-labels: 'need info'
                days-before-stale: 30
                days-before-close: 7
                stale-issue-message: "This issue has been automatically marked as stale as there has been no recent activity in response to our request for more information. Please respond so that we can proceed with this issue."
                close-issue-message: "This issue has been automatically closed as sufficient information hasn't been provided on the issue for further actions to be taken. Feel free to add more information."


================================================
FILE: .gitignore
================================================
# Python files
__pycache__/
*.py[cod]

# Python testing files
.coverage

# Ignore macOS Finder meta
.DS_Store
.tm_properties

# Ignore optional build / cache directory
/build


================================================
FILE: .style.yapf
================================================
[style]
based_on_style = pep8
allow_split_before_dict_value = false
coalesce_brackets = true
column_limit = 100
indent_width = 4
join_multiple_lines = true
spaces_before_comment = 1


================================================
FILE: CODEOWNERS
================================================
* @ungoogled-software/core


================================================
FILE: LICENSE
================================================
BSD 3-Clause License

Copyright (c) 2015-2026, The ungoogled-chromium Authors
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its
   contributors may be used to endorse or promote products derived from
   this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


================================================
FILE: README.md
================================================
# ungoogled-chromium

*A lightweight approach to removing Google web service dependency*

**Help is welcome!** See the [docs/contributing.md](docs/contributing.md) document for more information.

## Objectives

In descending order of significance (i.e. most important objective first):

1. **ungoogled-chromium is Google Chromium, sans dependency on Google web services**.
2. **ungoogled-chromium retains the default Chromium experience as closely as possible**. Unlike other Chromium forks that have their own visions of a web browser, ungoogled-chromium is essentially a drop-in replacement for Chromium.
3. **ungoogled-chromium features tweaks to enhance privacy, control, and transparency**. However, almost all of these features must be manually activated or enabled. For more details, see [Feature Overview](#feature-overview).

In scenarios where the objectives conflict, the objective of higher significance should take precedence.

## Content Overview

* [Objectives](#objectives)
* [Motivation and Philosophy](#motivation-and-philosophy)
* [Feature Overview](#feature-overview)
* [**Downloads**](#downloads)
* [Source Code](#source-code)
* [**FAQ**](#faq)
* [Building Instructions](#building-instructions)
* [Design Documentation](#design-documentation)
* [**Contributing, Reporting, Contacting**](#contributing-reporting-contacting)
* [Credits](#credits)
* [Related Projects](#related-projects)
* [License](#license)

## Motivation and Philosophy

Without signing in to a Google Account, Chromium does pretty well in terms of security and privacy. However, Chromium still has some dependency on Google web services and binaries. In addition, Google designed Chromium to be easy and intuitive for users, which means they compromise on transparency and control of internal operations.

ungoogled-chromium addresses these issues in the following ways:

1. Remove all remaining background requests to any web services while building and running the browser
2. Remove all code specific to Google web services
3. Remove all uses of pre-made binaries from the source code, and replace them with user-provided alternatives when possible.
4. Disable features that inhibit control and transparency, and add or modify features that promote them (these changes will almost always require manual activation or enabling).

These features are implemented as configuration flags, patches, and custom scripts. For more details, consult the [Design Documentation](docs/design.md).

## Feature Overview

*This section overviews the features of ungoogled-chromium. For more detailed information, it is best to consult the source code.*

Contents of this section:

* [Key Features](#key-features)
* [Enhancing Features](#enhancing-features)
* [Borrowed Features](#borrowed-features)
* [Supported Platforms and Distributions](#supported-platforms-and-distributions)

### Key Features

*These are the core features introduced by ungoogled-chromium.*

* Disable functionality specific to Google domains (e.g. Google Host Detector, Google URL Tracker, Google Cloud Messaging, Google Hotwording, etc.)
    * This includes disabling [Safe Browsing](https://en.wikipedia.org/wiki/Google_Safe_Browsing). Consult [the FAQ for the rationale](https://ungoogled-software.github.io/ungoogled-chromium-wiki/faq#why-is-safe-browsing-disabled).
* Block internal requests to Google at runtime. This feature is a fail-safe measure for the above, in case Google changes or introduces new components that our patches do not disable. This feature is implemented by replacing many Google web domains in the source code with non-existent alternatives ending in `qjz9zk` (known as domain substitution; [see docs/design.md](docs/design.md#source-file-processors) for details), then [modifying Chromium to block its own requests with such domains](patches/core/ungoogled-chromium/block-trk-and-subdomains.patch). In other words, no connections are attempted to the `qjz9zk` domain.
* Strip binaries from the source code (known as binary pruning; [see docs/design.md](docs/design.md#source-file-processors) for details)

### Enhancing Features

*These are the non-essential features introduced by ungoogled-chromium.*

* Add many new command-line switches and `chrome://flags` entries to configure new features (which are disabled by default). See [docs/flags.md](docs/flags.md) for the exhaustive list.
* Add *Suggestions URL* text field in the search engine editor (`chrome://settings/searchEngines`) for customizing search engine suggestions.
* Add more URL schemes allowed to save page schemes.
* Add Omnibox search provider "No Search" to allow disabling of searching
* Add a custom cross-platform build configuration and packaging wrapper for Chromium. It currently supports many Linux distributions, macOS, and Windows. (See [docs/design.md](docs/design.md) for details on the system.)
* Force all pop-ups into tabs
* Disable automatic formatting of URLs in Omnibox (e.g. stripping `http://`, hiding certain parameters)
* Disable intranet redirect detector (extraneous DNS requests)
    * This breaks captive portal detection, but captive portals still work.
* (Iridium Browser feature change) Prevent URLs with the `trk:` scheme from connecting to the Internet
    * Also prevents any URLs with the top-level domain `qjz9zk` (as used in domain substitution) from attempting a connection.
* (Windows-specific) Do not set the Zone Identifier on downloaded files

### Borrowed Features

In addition to the features introduced by ungoogled-chromium, ungoogled-chromium selectively borrows many features from the following projects (in approximate order of significance):

* [Inox patchset](https://github.com/gcarq/inox-patchset)
* [Bromite](https://github.com/bromite/bromite)
* [Debian](https://tracker.debian.org/pkg/chromium)
* [Iridium Browser](https://iridiumbrowser.de/)

### Supported Platforms and Distributions

[See docs/platforms.md for a list of supported platforms](docs/platforms.md).

Other platforms are discussed and tracked in this repository's Issue Tracker. Learn more about using the Issue Tracker under the section [Contributing, Reporting, Contacting](#contributing-reporting-contacting).

## Downloads

### Automated or maintained builds

ungoogled-chromium is available in the following **software repositories**:

* Arch: Available in the AUR, [see instructions in ungoogled-chromium-archlinux](https://github.com/ungoogled-software/ungoogled-chromium-archlinux)
* Debian: Available in OBS, find your [distribution specific instructions](https://github.com/ungoogled-software/ungoogled-chromium-debian) in the Installing section
* Ubuntu: Available in the [XtraDeb PPA](https://xtradeb.net/) as [ungoogled-chromium](https://xtradeb.net/apps/ungoogled-chromium/).
* Fedora: Available in [COPR](https://copr.fedorainfracloud.org/coprs/) as [`wojnilowicz/ungoogled-chromium`](https://copr.fedorainfracloud.org/coprs/wojnilowicz/ungoogled-chromium/). Also available in [RPM Fusion](https://rpmfusion.org/Configuration) as `chromium-browser-privacy` (outdated).
* Gentoo: Available in [`::pf4public`](https://github.com/PF4Public/gentoo-overlay) overlay as [`ungoogled-chromium`](https://github.com/PF4Public/gentoo-overlay/tree/master/www-client/ungoogled-chromium) and [`ungoogled-chromium-bin`](https://github.com/PF4Public/gentoo-overlay/tree/master/www-client/ungoogled-chromium-bin) ebuilds
* [OpenMandriva](https://openmandriva.org/) includes ungoogled-chromium as its main browser. The `chromium` package includes all ungoogling patches.
* macOS: Available in [Homebrew](https://brew.sh/) as [`ungoogled-chromium`](https://formulae.brew.sh/cask/ungoogled-chromium). Just run `brew install --cask ungoogled-chromium`. Chromium will appear in your `/Applications` directory.
* FreeBSD: Available in pkg as [`www/ungoogled-chromium`](https://www.freshports.org/www/ungoogled-chromium/).
* [openSUSE](https://www.opensuse.org/): Available as [`ungoogled-chromium`](https://software.opensuse.org//download.html?project=network%3Achromium&package=ungoogled-chromium). 

If your GNU/Linux distribution is not listed, there are distro-independent builds available via the following **package managers**:

* Flatpak: Available [in the Flathub repo](https://flathub.org/apps/details/io.github.ungoogled_software.ungoogled_chromium) as `io.github.ungoogled_software.ungoogled_chromium`
* GNU Guix: Available as `ungoogled-chromium`
* NixOS/nixpkgs: Available as `ungoogled-chromium`

### Third-party binaries

If your operating system is not listed above, you can also try to [**Download binaries from here**](https://ungoogled-software.github.io/ungoogled-chromium-binaries/)

*NOTE: These binaries are provided by anyone who are willing to build and submit them. Because these binaries are not necessarily [reproducible](https://reproducible-builds.org/), authenticity cannot be guaranteed; In other words, there is always a non-zero probability that these binaries may have been tampered with. In the unlikely event that this has happened to you, please [report it in a new issue](#contributing-reporting-contacting).*

These binaries are known as **contributor binaries**.

## Source Code

This repository only contains the common code for all platforms; it does not contain all the configuration and scripts necessary to build ungoogled-chromium. Most users will want to use platform-specific repos, where all the remaining configuration and scripts are provided for specific platforms:

[**Find the repo for a specific platform here**](docs/platforms.md).

If you wish to include ungoogled-chromium code in your own build process, consider using [the tags in this repo](https://github.com/ungoogled-software/ungoogled-chromium/tags). These tags follow the format `{chromium_version}-{revision}` where

* `chromium_version` is the version of Chromium used in `x.x.x.x` format, and
* `revision` is a number indicating the version of ungoogled-chromium for the corresponding Chromium version.

Additionally, most platform-specific repos extend their tag scheme upon this one.

**Building the source code**: [See docs/building.md](docs/building.md)

### Mirrors

List of mirrors:

* [Codeberg](https://codeberg.org): [main repo](https://codeberg.org/ungoogled-software/ungoogled-chromium) and [ungoogled-software](https://codeberg.org/ungoogled-software)

## FAQ

[See the frequently-asked questions (FAQ) on the Wiki](https://ungoogled-software.github.io/ungoogled-chromium-wiki/faq)

## Building Instructions

[See docs/building.md](docs/building.md)

## Design Documentation

[See docs/design.md](docs/design.md)

## Contributing, Reporting, Contacting

* For reporting and contacting, see [SUPPORT.md](SUPPORT.md)
* If you're willing to help, check out the [Issue Tracker](https://github.com/ungoogled-software/ungoogled-chromium/issues) and especially issues, which [need help](https://github.com/ungoogled-software/ungoogled-chromium/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22)
* For contributing (e.g. how to help, submitting changes, criteria for new features), see [docs/contributing.md](docs/contributing.md)
* If you have some small contributions that don't fit our criteria, consider adding them to [ungoogled-software/contrib](https://github.com/ungoogled-software/contrib) or [our Wiki](https://github.com/ungoogled-software/ungoogled-chromium-wiki) instead.

## Credits

* [The Chromium Project](https://www.chromium.org/)
* [Inox patchset](https://github.com/gcarq/inox-patchset)
* [Debian](https://tracker.debian.org/pkg/chromium-browser)
* [Bromite](https://github.com/bromite/bromite)
* [Iridium Browser](https://iridiumbrowser.de/)
* The users for testing and debugging, [contributing code](https://github.com/ungoogled-software/ungoogled-chromium/graphs/contributors), providing feedback, or simply using ungoogled-chromium in some capacity.

## Related Projects

List of known projects that fork or use changes from ungoogled-chromium:

* [Bromite](https://github.com/bromite/bromite) (Borrows some patches. Features builds for Android)
* [ppc64le fork](https://github.com/leo-lb/ungoogled-chromium) (Fork with changes to build for ppc64le CPUs)

## License

BSD-3-clause. See [LICENSE](LICENSE)


================================================
FILE: SUPPORT.md
================================================
# Support

**Before you submit feedback, please ensure you have tried the following**: 

* Read the [FAQ](https://ungoogled-software.github.io/ungoogled-chromium-wiki/faq)
* If you are using a build for an officially-supported platform ([see the list here](docs/platforms.md)), then please refer to the [Platform-specific Support](#platform-specific-support) section first.
* Check if your feedback already exists in the [Issue Tracker](https://github.com/ungoogled-software/ungoogled-chromium/issues) (make sure to search closed issues and use search filters, as applicable)
* If this is a problem, ensure it does *not* occur with regular Chromium or Google Chrome. If it does, then this is *not* a problem with ungoogled-chromium. Instead, please submit your feedback to the [Chromium bug tracker](https://bugs.chromium.org/p/chromium/issues/list) or Google.
* Read the documentation under [docs/](docs/)

There are a few channels for support:

* An issue tracker. Issue trackers are the main hubs for discussions and development activity, and thus the primary means of support. They includes problems, suggestions, and questions. If you are using a build for an officially-supported platform ([see the list here](docs/platforms.md)), then please refer to the [Platform-specific Support](#platform-specific-support) section to find its issue tracker. Otherwise, please use [the main issue tracker](https://github.com/ungoogled-software/ungoogled-chromium/issues). 
* A chat room. There are two options available:
    * [Gitter](https://gitter.im/ungoogled-software/Lobby). It can use your GitHub account as an identity.
    * Matrix.org at [#ungoogled-chromium:matrix.org](https://matrix.to/#/#ungoogled-chromium:matrix.org). It has a bidirectional connection with Gitter.

## Platform-specific Support

For officially-supported platforms, **please visit their issue trackers before using the issue tracker in this repository**:

* Android: [Issue tracker](https://github.com/ungoogled-software/ungoogled-chromium-android/issues)
* Arch Linux and derivatives (e.g. Manjaro): [Issue tracker](https://github.com/ungoogled-software/ungoogled-chromium-archlinux/issues)
* Debian & Ubuntu: [Issue tracker](https://github.com/ungoogled-software/ungoogled-chromium-debian/issues)
* Fedora & CentOS: [Issue tracker](https://github.com/ungoogled-software/ungoogled-chromium-fedora/issues)
* Flatpak: [Issue tracker](https://github.com/ungoogled-software/ungoogled-chromium-flatpak/issues)
* Gentoo ([@PF4Public](https://github.com/PF4Public)'s overlay): [Issue tracker](https://github.com/PF4Public/gentoo-overlay/issues?q=is%3Aissue++ungoogled-chromium+)
* macOS: [Issue tracker](https://github.com/ungoogled-software/ungoogled-chromium-macos/issues)
* Portable Linux: [Issue tracker](https://github.com/ungoogled-software/ungoogled-chromium-portablelinux/issues)
* Windows: [Issue tracker](https://github.com/ungoogled-software/ungoogled-chromium-windows/issues)


================================================
FILE: chromium_version.txt
================================================
146.0.7680.153


================================================
FILE: devutils/.coveragerc
================================================
[run]
branch = True
parallel = True
omit = tests/*

[report]
# Regexes for lines to exclude from consideration
exclude_lines =
    # Have to re-enable the standard pragma
    pragma: no cover

    # Don't complain about missing debug-only code:
    def __repr__
    if self\.debug

    # Don't complain if tests don't hit defensive assertion code:
    raise AssertionError
    raise NotImplementedError

    # Don't complain if non-runnable code isn't run:
    if 0:
    if __name__ == .__main__.:


================================================
FILE: devutils/README.md
================================================
# Developer utilities for ungoogled-chromium

This is a collection of scripts written for developing on ungoogled-chromium. See the descriptions at the top of each script for more information.


================================================
FILE: devutils/__init__.py
================================================


================================================
FILE: devutils/check_all_code.sh
================================================
#!/bin/bash

# Wrapper for devutils and utils formatter, linter, and tester

set -eu

_root_dir=$(dirname $(dirname $(readlink -f $0)))
cd ${_root_dir}/devutils

printf '###### utils yapf ######\n'
./run_utils_yapf.sh
printf '###### utils pylint ######\n'
./run_utils_pylint.py || ./run_utils_pylint.py --hide-fixme
printf '###### utils tests ######\n'
./run_utils_tests.sh

printf '### devutils yapf ######\n'
./run_devutils_yapf.sh
printf '###### devutils pylint ######\n'
./run_devutils_pylint.py || ./run_devutils_pylint.py --hide-fixme
printf '###### devutils tests ######\n'
./run_devutils_tests.sh


================================================
FILE: devutils/check_downloads_ini.py
================================================
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-

# Copyright (c) 2019 The ungoogled-chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Run sanity checking algorithms over downloads.ini files

It checks the following:

    * downloads.ini has the correct format (i.e. conforms to its schema)

Exit codes:
    * 0 if no problems detected
    * 1 if warnings or errors occur
"""

import argparse
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent.parent / 'utils'))
from downloads import DownloadInfo, schema

sys.path.pop(0)


def check_downloads_ini(downloads_ini_iter):
    """
    Combines and checks if the the downloads.ini files provided are valid.

    downloads_ini_iter must be an iterable of strings to downloads.ini files.

    Returns True if errors occured, False otherwise.
    """
    try:
        DownloadInfo(downloads_ini_iter)
    except schema.SchemaError:
        return True
    return False


def main():
    """CLI entrypoint"""

    root_dir = Path(__file__).resolve().parent.parent
    default_downloads_ini = [str(root_dir / 'downloads.ini')]

    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('-d',
                        '--downloads-ini',
                        type=Path,
                        nargs='*',
                        default=default_downloads_ini,
                        help='List of downloads.ini files to check. Default: %(default)s')
    args = parser.parse_args()

    if check_downloads_ini(args.downloads_ini):
        sys.exit(1)
    sys.exit(0)


if __name__ == '__main__':
    main()


================================================
FILE: devutils/check_files_exist.py
================================================
#!/usr/bin/env python3

# Copyright (c) 2019 The ungoogled-chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""
Checks if files in a list exist.

Used for quick validation of lists in CI checks.
"""

import argparse
import sys
from pathlib import Path


def main():
    """CLI entrypoint"""
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('root_dir', type=Path, help='The directory to check from')
    parser.add_argument('input_files', type=Path, nargs='+', help='The files lists to check')
    args = parser.parse_args()

    for input_name in args.input_files:
        file_iter = filter(
            len, map(str.strip,
                     Path(input_name).read_text(encoding='UTF-8').splitlines()))
        for file_name in file_iter:
            if not Path(args.root_dir, file_name).exists():
                print(f'ERROR: Path "{file_name}" from file "{input_name}" does not exist.',
                      file=sys.stderr)
                sys.exit(1)


if __name__ == "__main__":
    main()


================================================
FILE: devutils/check_gn_flags.py
================================================
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-

# Copyright (c) 2019 The ungoogled-chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Run sanity checking algorithms over GN flags

It checks the following:

    * GN flags in flags.gn are sorted and not duplicated

Exit codes:
    * 0 if no problems detected
    * 1 if warnings or errors occur
"""

import argparse
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent.parent / 'utils'))
from _common import ENCODING, get_logger

sys.path.pop(0)


def check_gn_flags(gn_flags_path):
    """
    Checks if GN flags are sorted and not duplicated.

    gn_flags_path is a pathlib.Path to the GN flags file to check

    Returns True if warnings were logged; False otherwise
    """
    keys_seen = set()
    warnings = False
    with gn_flags_path.open(encoding=ENCODING) as file_obj:
        iterator = iter(file_obj.read().splitlines())
    try:
        previous = next(iterator)
    except StopIteration:
        return warnings
    for current in iterator:
        gn_key = current.split('=')[0]
        if gn_key in keys_seen:
            get_logger().warning('In GN flags %s, "%s" appears at least twice', gn_flags_path,
                                 gn_key)
            warnings = True
        else:
            keys_seen.add(gn_key)
        if current < previous:
            get_logger().warning('In GN flags %s, "%s" should be sorted before "%s"', gn_flags_path,
                                 current, previous)
            warnings = True
        previous = current
    return warnings


def main():
    """CLI entrypoint"""

    root_dir = Path(__file__).resolve().parent.parent
    default_flags_gn = root_dir / 'flags.gn'

    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('-f',
                        '--flags-gn',
                        type=Path,
                        default=default_flags_gn,
                        help='Path to the GN flags to use. Default: %(default)s')
    args = parser.parse_args()

    if check_gn_flags(args.flags_gn):
        sys.exit(1)
    sys.exit(0)


if __name__ == '__main__':
    main()


================================================
FILE: devutils/check_patch_files.py
================================================
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-

# Copyright (c) 2019 The ungoogled-chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Run sanity checking algorithms over ungoogled-chromium's patch files

It checks the following:

    * All patches exist
    * All patches are referenced by the patch order

Exit codes:
    * 0 if no problems detected
    * 1 if warnings or errors occur
"""

import argparse
import sys
from pathlib import Path

from third_party import unidiff

sys.path.insert(0, str(Path(__file__).resolve().parent.parent / 'utils'))
from _common import ENCODING, get_logger, parse_series # pylint: disable=wrong-import-order

sys.path.pop(0)

# File suffixes to ignore for checking unused patches
_PATCHES_IGNORE_SUFFIXES = {'.md'}


def _read_series_file(patches_dir, series_file, join_dir=False):
    """
    Returns a generator over the entries in the series file

    patches_dir is a pathlib.Path to the directory of patches
    series_file is a pathlib.Path relative to patches_dir

    join_dir indicates if the patches_dir should be joined with the series entries
    """
    for entry in parse_series(patches_dir / series_file):
        if join_dir:
            yield patches_dir / entry
        else:
            yield entry


def check_patch_readability(patches_dir, series_path=Path('series')):
    """
    Check if the patches from iterable patch_path_iter are readable.
        Patches that are not are logged to stdout.

    Returns True if warnings occured, False otherwise.
    """
    warnings = False
    for patch_path in _read_series_file(patches_dir, series_path, join_dir=True):
        if patch_path.exists():
            with patch_path.open(encoding=ENCODING) as file_obj:
                try:
                    unidiff.PatchSet(file_obj.read())
                except unidiff.errors.UnidiffParseError:
                    get_logger().exception('Could not parse patch: %s', patch_path)
                    warnings = True
                    continue
        else:
            get_logger().warning('Patch not found: %s', patch_path)
            warnings = True
    return warnings


def check_unused_patches(patches_dir, series_path=Path('series')):
    """
    Checks if there are unused patches in patch_dir from series file series_path.
        Unused patches are logged to stdout.

    patches_dir is a pathlib.Path to the directory of patches
    series_path is a pathlib.Path to the series file relative to the patches_dir

    Returns True if there are unused patches; False otherwise.
    """
    unused_patches = set()
    for path in patches_dir.rglob('*'):
        if path.is_dir():
            continue
        if path.suffix in _PATCHES_IGNORE_SUFFIXES:
            continue
        unused_patches.add(str(path.relative_to(patches_dir)))
    unused_patches -= set(_read_series_file(patches_dir, series_path))
    unused_patches.remove(str(series_path))
    logger = get_logger()
    for entry in sorted(unused_patches):
        logger.warning('Unused patch: %s', entry)
    return bool(unused_patches)


def check_series_duplicates(patches_dir, series_path=Path('series')):
    """
    Checks if there are duplicate entries in the series file

    series_path is a pathlib.Path to the series file relative to the patches_dir

    returns True if there are duplicate entries; False otherwise.
    """
    entries_seen = set()
    for entry in _read_series_file(patches_dir, series_path):
        if entry in entries_seen:
            get_logger().warning('Patch appears more than once in series: %s', entry)
            return True
        entries_seen.add(entry)
    return False


def main():
    """CLI entrypoint"""

    root_dir = Path(__file__).resolve().parent.parent
    default_patches_dir = root_dir / 'patches'

    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('-p',
                        '--patches',
                        type=Path,
                        default=default_patches_dir,
                        help='Path to the patches directory to use. Default: %(default)s')
    args = parser.parse_args()

    warnings = False
    warnings |= check_patch_readability(args.patches)
    warnings |= check_series_duplicates(args.patches)
    warnings |= check_unused_patches(args.patches)

    if warnings:
        sys.exit(1)
    sys.exit(0)


if __name__ == '__main__':
    main()


================================================
FILE: devutils/print_tag_version.sh
================================================
_root_dir=$(dirname $(dirname $(readlink -f $0)))
printf '%s-%s' $(cat $_root_dir/chromium_version.txt) $(cat $_root_dir/revision.txt)


================================================
FILE: devutils/pytest.ini
================================================
[pytest]
testpaths = tests
#filterwarnings =
#	error
#	ignore::DeprecationWarning
#addopts = --cov-report term-missing --hypothesis-show-statistics -p no:warnings
# Live logging
#log_cli=true
#log_level=DEBUG
addopts = -capture=all --cov=. --cov-config=.coveragerc --cov-report term-missing -p no:warnings


================================================
FILE: devutils/run_devutils_pylint.py
================================================
#!/usr/bin/env python3

# Copyright (c) 2019 The ungoogled-chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Run Pylint over devutils"""

import argparse
import sys
from pathlib import Path

from run_other_pylint import ChangeDir, run_pylint


def main():
    """CLI entrypoint"""
    parser = argparse.ArgumentParser(description='Run Pylint over devutils')
    parser.add_argument('--hide-fixme', action='store_true', help='Hide "fixme" Pylint warnings.')
    parser.add_argument('--show-locally-disabled',
                        action='store_true',
                        help='Show "locally-disabled" Pylint warnings.')
    args = parser.parse_args()

    disables = [
        'wrong-import-position',
        'duplicate-code',
    ]

    if args.hide_fixme:
        disables.append('fixme')
    if not args.show_locally_disabled:
        disables.append('locally-disabled')

    pylint_options = [
        f"--disable={','.join(disables)}",
        '--jobs=4',
        '--score=n',
        '--persistent=n',
    ]

    ignore_prefixes = [
        ('third_party', ),
    ]

    sys.path.insert(1, str(Path(__file__).resolve().parent.parent / 'utils'))
    sys.path.insert(2, str(Path(__file__).resolve().parent.parent / 'devutils' / 'third_party'))
    with ChangeDir(Path(__file__).parent):
        result = run_pylint(
            Path(),
            pylint_options,
            ignore_prefixes=ignore_prefixes,
        )
    sys.path.pop(2)
    sys.path.pop(1)
    if not result:
        sys.exit(1)
    sys.exit(0)


if __name__ == '__main__':
    main()


================================================
FILE: devutils/run_devutils_tests.sh
================================================
#!/bin/bash

set -eu

_root_dir=$(dirname $(dirname $(readlink -f $0)))
cd ${_root_dir}/devutils
python3 -m pytest -c ${_root_dir}/devutils/pytest.ini


================================================
FILE: devutils/run_devutils_yapf.sh
================================================
#!/bin/bash

set -eu

_current_dir=$(dirname $(readlink -f $0))
_root_dir=$(dirname $_current_dir)
python3 -m yapf --style "$_root_dir/.style.yapf" -e '*/third_party/*' -rpi "$_current_dir"


================================================
FILE: devutils/run_other_pylint.py
================================================
#!/usr/bin/env python3

# Copyright (c) 2019 The ungoogled-chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Run Pylint over any module"""

import argparse
import os
import shutil
import sys
from pathlib import Path

from pylint import lint


class ChangeDir:
    """
    Changes directory to path in with statement
    """

    def __init__(self, path):
        self._path = path
        self._orig_path = os.getcwd()

    def __enter__(self):
        os.chdir(str(self._path))

    def __exit__(self, *_):
        os.chdir(self._orig_path)


def run_pylint(module_path, pylint_options, ignore_prefixes=tuple()):
    """Runs Pylint. Returns a boolean indicating success"""
    pylint_stats = Path(f'/run/user/{os.getuid()}/pylint_stats')
    if not pylint_stats.parent.is_dir(): #pylint: disable=no-member
        pylint_stats = Path('/run/shm/pylint_stats')
    os.environ['PYLINTHOME'] = str(pylint_stats)

    input_paths = []
    if not module_path.exists():
        print('ERROR: Cannot find', module_path)
        sys.exit(1)
    if module_path.is_dir():
        for path in module_path.rglob('*.py'):
            ignore_matched = False
            for prefix in ignore_prefixes:
                if path.parts[:len(prefix)] == prefix:
                    ignore_matched = True
                    break
            if ignore_matched:
                continue
            input_paths.append(str(path))
    else:
        input_paths.append(str(module_path))
    runner = lint.Run((*input_paths, *pylint_options), do_exit=False)

    if pylint_stats.is_dir():
        shutil.rmtree(str(pylint_stats))

    if runner.linter.msg_status != 0:
        print('WARNING: Non-zero exit status:', runner.linter.msg_status)
        return False
    return True


def main():
    """CLI entrypoint"""

    parser = argparse.ArgumentParser(description='Run Pylint over arbitrary module')
    parser.add_argument('--hide-fixme', action='store_true', help='Hide "fixme" Pylint warnings.')
    parser.add_argument('--show-locally-disabled',
                        action='store_true',
                        help='Show "locally-disabled" Pylint warnings.')
    parser.add_argument('module_path', type=Path, help='Path to the module to check')
    args = parser.parse_args()

    if not args.module_path.exists():
        print(f'ERROR: Module path "{args.module_path}" does not exist')
        sys.exit(1)

    disables = [
        'wrong-import-position',
    ]

    if args.hide_fixme:
        disables.append('fixme')
    if not args.show_locally_disabled:
        disables.append('locally-disabled')

    pylint_options = [
        f"--disable={','.join(disables)}",
        '--jobs=4',
        '--score=n',
        '--persistent=n',
    ]

    if not run_pylint(args.module_path, pylint_options):
        sys.exit(1)
    sys.exit(0)


if __name__ == '__main__':
    main()


================================================
FILE: devutils/run_other_yapf.sh
================================================
#!/bin/bash

set -eu

python3 -m yapf --style "$(dirname $(readlink -f $0))/.style.yapf" -rpi $@


================================================
FILE: devutils/run_utils_pylint.py
================================================
#!/usr/bin/env python3

# Copyright (c) 2019 The ungoogled-chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Run Pylint over utils"""

import argparse
import sys
from pathlib import Path

from run_other_pylint import ChangeDir, run_pylint


def main():
    """CLI entrypoint"""
    parser = argparse.ArgumentParser(description='Run Pylint over utils')
    parser.add_argument('--hide-fixme', action='store_true', help='Hide "fixme" Pylint warnings.')
    parser.add_argument('--show-locally-disabled',
                        action='store_true',
                        help='Show "locally-disabled" Pylint warnings.')
    args = parser.parse_args()

    pylint_options = [
        '--jobs=4',
        '--max-args=7',
        '--score=n',
        '--persistent=n',
    ]

    if args.hide_fixme:
        pylint_options.append('--disable=fixme')
    if not args.show_locally_disabled:
        pylint_options.append('--disable=locally-disabled')

    ignore_prefixes = [
        ('third_party', ),
        ('tests', ),
    ]

    sys.path.insert(1, str(Path(__file__).resolve().parent.parent / 'utils' / 'third_party'))
    sys.path.append(Path(__file__).resolve().parent.parent / 'utils')
    with ChangeDir(Path(__file__).resolve().parent.parent / 'utils'):
        result = run_pylint(
            Path(),
            pylint_options,
            ignore_prefixes=ignore_prefixes,
        )
    sys.path.pop(1)
    if not result:
        sys.exit(1)
    sys.exit(0)


if __name__ == '__main__':
    main()


================================================
FILE: devutils/run_utils_tests.sh
================================================
#!/bin/bash

set -eu

_root_dir=$(dirname $(dirname $(readlink -f $0)))
cd ${_root_dir}/utils
python3 -m pytest -c ${_root_dir}/utils/pytest.ini


================================================
FILE: devutils/run_utils_yapf.sh
================================================
#!/bin/bash

set -eu

_root_dir=$(dirname $(dirname $(readlink -f $0)))
python3 -m yapf --style "$_root_dir/.style.yapf" -e '*/third_party/*' -rpi "$_root_dir/utils"


================================================
FILE: devutils/set_quilt_vars.fish
================================================
#!/bin/fish

# Fish variant of set_quilt_vars.sh

alias quilt='quilt --quiltrc -'

set REPO_ROOT (dirname (dirname (readlink -f (status current-filename))))

set -gx QUILT_PATCHES "$REPO_ROOT/patches"

set -gx QUILT_PUSH_ARGS "--color=auto"
set -gx QUILT_DIFF_OPTS "--show-c-function"
set -gx QUILT_PATCH_OPTS "--unified --reject-format=unified"
set -gx QUILT_DIFF_ARGS "-p ab --no-timestamps --no-index --color=auto --sort"
set -gx QUILT_REFRESH_ARGS "-p ab --no-timestamps --no-index --sort --strip-trailing-whitespace"
set -gx QUILT_COLORS "diff_hdr=1;32:diff_add=1;34:diff_rem=1;31:diff_hunk=1;33:diff_ctx=35:diff_cctx=33"
set -gx QUILT_SERIES_ARGS "--color=auto"
set -gx QUILT_PATCHES_ARGS "--color=auto"

set -gx LC_ALL C


================================================
FILE: devutils/set_quilt_vars.sh
================================================
# Sets quilt variables for updating the patches
# Make sure to run this with the shell command "source" in order to inherit the variables into the interactive environment

# There is some problem with the absolute paths in QUILT_PATCHES and QUILT_SERIES breaking quilt
# (refresh and diff don't read QUILT_*_ARGS, and series displays absolute paths instead of relative)
# Specifying a quiltrc file fixes this, so "--quiltrc -" fixes this too.
# One side effect of '--quiltrc -' is that we lose default settings from /etc/quilt.quiltrc, so they are redefined below.
alias quilt='quilt --quiltrc -'

# Assume this script lives within the repository
REPO_ROOT=$(dirname "$(dirname "$(readlink -f "${BASH_SOURCE[0]:-${(%):-%x}}")")")

export QUILT_PATCHES="$REPO_ROOT/patches"
#export QUILT_SERIES=$(readlink -f "$REPO_ROOT/patches/series")

# Options below borrowed from Debian and default quilt options (from /etc/quilt.quiltrc on Debian)
export QUILT_PUSH_ARGS="--color=auto"
export QUILT_DIFF_OPTS="--show-c-function"
export QUILT_PATCH_OPTS="--unified --reject-format=unified"
export QUILT_DIFF_ARGS="-p ab --no-timestamps --no-index --color=auto --sort"
export QUILT_REFRESH_ARGS="-p ab --no-timestamps --no-index --sort --strip-trailing-whitespace"
export QUILT_COLORS="diff_hdr=1;32:diff_add=1;34:diff_rem=1;31:diff_hunk=1;33:diff_ctx=35:diff_cctx=33"
export QUILT_SERIES_ARGS="--color=auto"
export QUILT_PATCHES_ARGS="--color=auto"

export LC_ALL=C
# When non-default less options are used, add the -R option so that less outputs
# ANSI color escape codes "raw".
[ -n "$LESS" -a -z "${QUILT_PAGER+x}" ] && export QUILT_PAGER="less -FRX"


================================================
FILE: devutils/tests/__init__.py
================================================


================================================
FILE: devutils/tests/test_check_patch_files.py
================================================
# -*- coding: UTF-8 -*-

# Copyright (c) 2020 The ungoogled-chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Test check_patch_files.py"""

import logging
import tempfile
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent.parent.parent / 'utils'))
from _common import ENCODING, get_logger, set_logging_level

sys.path.pop(0)

sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from check_patch_files import check_series_duplicates

sys.path.pop(0)


def test_check_series_duplicates():
    """Test check_series_duplicates"""

    set_logging_level(logging.DEBUG)

    with tempfile.TemporaryDirectory() as tmpdirname:
        patches_dir = Path(tmpdirname)
        series_path = Path(tmpdirname, 'series')

        get_logger().info('Check no duplicates')
        series_path.write_text('\n'.join([
            'a.patch',
            'b.patch',
            'c.patch',
        ]), encoding=ENCODING)
        assert not check_series_duplicates(patches_dir)

        get_logger().info('Check duplicates')
        series_path.write_text('\n'.join([
            'a.patch',
            'b.patch',
            'c.patch',
            'a.patch',
        ]),
                               encoding=ENCODING)
        assert check_series_duplicates(patches_dir)


if __name__ == '__main__':
    test_check_series_duplicates()


================================================
FILE: devutils/tests/test_validate_patches.py
================================================
# -*- coding: UTF-8 -*-

# Copyright (c) 2020 The ungoogled-chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Test validate_patches.py"""

import logging
import tempfile
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent.parent.parent / 'utils'))
from _common import ENCODING, get_logger, set_logging_level

sys.path.pop(0)

sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
import validate_patches

sys.path.pop(0)


def test_test_patches():
    """Test _dry_check_patched_file"""

    #pylint: disable=protected-access
    set_logging_level(logging.DEBUG)

    orig_file_content = """bye world"""
    series_iter = ['test.patch']

    def _run_test_patches(patch_content):
        with tempfile.TemporaryDirectory() as tmpdirname:
            Path(tmpdirname, 'foobar.txt').write_text(orig_file_content, encoding=ENCODING)
            Path(tmpdirname, 'test.patch').write_text(patch_content, encoding=ENCODING)
            _, patch_cache = validate_patches._load_all_patches(series_iter, Path(tmpdirname))
            required_files = validate_patches._get_required_files(patch_cache)
            files_under_test = validate_patches._retrieve_local_files(required_files,
                                                                      Path(tmpdirname))
            return validate_patches._test_patches(series_iter, patch_cache, files_under_test)

    get_logger().info('Check valid modification')
    patch_content = """--- a/foobar.txt
+++ b/foobar.txt
@@ -1 +1 @@
-bye world
+hello world
"""
    assert not _run_test_patches(patch_content)

    get_logger().info('Check invalid modification')
    patch_content = """--- a/foobar.txt
+++ b/foobar.txt
@@ -1 +1 @@
-hello world
+olleh world
"""
    assert _run_test_patches(patch_content)

    get_logger().info('Check correct removal')
    patch_content = """--- a/foobar.txt
+++ /dev/null
@@ -1 +0,0 @@
-bye world
"""
    assert not _run_test_patches(patch_content)

    get_logger().info('Check incorrect removal')
    patch_content = """--- a/foobar.txt
+++ /dev/null
@@ -1 +0,0 @@
-this line does not exist in foobar
"""
    assert _run_test_patches(patch_content)


if __name__ == '__main__':
    test_test_patches()


================================================
FILE: devutils/third_party/README.md
================================================
This directory contains third-party libraries used by devutils.

Contents:

* [python-unidiff](https://github.com/matiasb/python-unidiff)
    * For parsing and modifying unified diffs.


================================================
FILE: devutils/third_party/__init__.py
================================================


================================================
FILE: devutils/third_party/unidiff/__init__.py
================================================
# -*- coding: utf-8 -*-

# The MIT License (MIT)
# Copyright (c) 2014-2017 Matias Bordese
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
# OR OTHER DEALINGS IN THE SOFTWARE.


"""Unidiff parsing library."""

from __future__ import unicode_literals

from . import __version__
from .patch import (
    DEFAULT_ENCODING,
    LINE_TYPE_ADDED,
    LINE_TYPE_CONTEXT,
    LINE_TYPE_REMOVED,
    Hunk,
    PatchedFile,
    PatchSet,
    UnidiffParseError,
)

VERSION = __version__.__version__


================================================
FILE: devutils/third_party/unidiff/__version__.py
================================================
# -*- coding: utf-8 -*-

# The MIT License (MIT)
# Copyright (c) 2014-2017 Matias Bordese
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
# OR OTHER DEALINGS IN THE SOFTWARE.

__version__ = '0.5.5'


================================================
FILE: devutils/third_party/unidiff/constants.py
================================================
# -*- coding: utf-8 -*-

# The MIT License (MIT)
# Copyright (c) 2014-2017 Matias Bordese
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
# OR OTHER DEALINGS IN THE SOFTWARE.


"""Useful constants and regexes used by the package."""

from __future__ import unicode_literals

import re


RE_SOURCE_FILENAME = re.compile(
    r'^--- (?P<filename>[^\t\n]+)(?:\t(?P<timestamp>[^\n]+))?')
RE_TARGET_FILENAME = re.compile(
    r'^\+\+\+ (?P<filename>[^\t\n]+)(?:\t(?P<timestamp>[^\n]+))?')

# @@ (source offset, length) (target offset, length) @@ (section header)
RE_HUNK_HEADER = re.compile(
    r"^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))?\ @@[ ]?(.*)")

#    kept line (context)
# \n empty line (treat like context)
# +  added line
# -  deleted line
# \  No newline case
RE_HUNK_BODY_LINE = re.compile(
    r'^(?P<line_type>[- \+\\])(?P<value>.*)', re.DOTALL)
RE_HUNK_EMPTY_BODY_LINE = re.compile(
    r'^(?P<line_type>[- \+\\]?)(?P<value>[\r\n]{1,2})', re.DOTALL)

RE_NO_NEWLINE_MARKER = re.compile(r'^\\ No newline at end of file')

DEFAULT_ENCODING = 'UTF-8'

LINE_TYPE_ADDED = '+'
LINE_TYPE_REMOVED = '-'
LINE_TYPE_CONTEXT = ' '
LINE_TYPE_EMPTY = ''
LINE_TYPE_NO_NEWLINE = '\\'
LINE_VALUE_NO_NEWLINE = ' No newline at end of file'


================================================
FILE: devutils/third_party/unidiff/errors.py
================================================
# -*- coding: utf-8 -*-

# The MIT License (MIT)
# Copyright (c) 2014-2017 Matias Bordese
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
# OR OTHER DEALINGS IN THE SOFTWARE.


"""Errors and exceptions raised by the package."""

from __future__ import unicode_literals


class UnidiffParseError(Exception):
    """Exception when parsing the unified diff data."""


================================================
FILE: devutils/third_party/unidiff/patch.py
================================================
# -*- coding: utf-8 -*-

# The MIT License (MIT)
# Copyright (c) 2014-2017 Matias Bordese
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
# OR OTHER DEALINGS IN THE SOFTWARE.


"""Classes used by the unified diff parser to keep the diff data."""

from __future__ import unicode_literals

import codecs
import sys

from .constants import (
    DEFAULT_ENCODING,
    LINE_TYPE_ADDED,
    LINE_TYPE_CONTEXT,
    LINE_TYPE_EMPTY,
    LINE_TYPE_REMOVED,
    LINE_TYPE_NO_NEWLINE,
    LINE_VALUE_NO_NEWLINE,
    RE_HUNK_BODY_LINE,
    RE_HUNK_EMPTY_BODY_LINE,
    RE_HUNK_HEADER,
    RE_SOURCE_FILENAME,
    RE_TARGET_FILENAME,
    RE_NO_NEWLINE_MARKER,
)
from .errors import UnidiffParseError


PY2 = sys.version_info[0] == 2
if PY2:
    from StringIO import StringIO
    open_file = codecs.open
    make_str = lambda x: x.encode(DEFAULT_ENCODING)

    def implements_to_string(cls):
        cls.__unicode__ = cls.__str__
        cls.__str__ = lambda x: x.__unicode__().encode(DEFAULT_ENCODING)
        return cls
else:
    from io import StringIO
    open_file = open
    make_str = str
    implements_to_string = lambda x: x
    unicode = str
    basestring = str


@implements_to_string
class Line(object):
    """A diff line."""

    def __init__(self, value, line_type,
                 source_line_no=None, target_line_no=None, diff_line_no=None):
        super(Line, self).__init__()
        self.source_line_no = source_line_no
        self.target_line_no = target_line_no
        self.diff_line_no = diff_line_no
        self.line_type = line_type
        self.value = value

    def __repr__(self):
        return make_str("<Line: %s%s>") % (self.line_type, self.value)

    def __str__(self):
        return "%s%s" % (self.line_type, self.value)

    def __eq__(self, other):
        return (self.source_line_no == other.source_line_no and
                self.target_line_no == other.target_line_no and
                self.diff_line_no == other.diff_line_no and
                self.line_type == other.line_type and
                self.value == other.value)

    @property
    def is_added(self):
        return self.line_type == LINE_TYPE_ADDED

    @property
    def is_removed(self):
        return self.line_type == LINE_TYPE_REMOVED

    @property
    def is_context(self):
        return self.line_type == LINE_TYPE_CONTEXT


@implements_to_string
class PatchInfo(list):
    """Lines with extended patch info.

    Format of this info is not documented and it very much depends on
    patch producer.

    """

    def __repr__(self):
        value = "<PatchInfo: %s>" % self[0].strip()
        return make_str(value)

    def __str__(self):
        return ''.join(unicode(line) for line in self)


@implements_to_string
class Hunk(list):
    """Each of the modified blocks of a file."""

    def __init__(self, src_start=0, src_len=0, tgt_start=0, tgt_len=0,
                 section_header=''):
        if src_len is None:
            src_len = 1
        if tgt_len is None:
            tgt_len = 1
        self.added = 0  # number of added lines
        self.removed = 0  # number of removed lines
        self.source = []
        self.source_start = int(src_start)
        self.source_length = int(src_len)
        self.target = []
        self.target_start = int(tgt_start)
        self.target_length = int(tgt_len)
        self.section_header = section_header

    def __repr__(self):
        value = "<Hunk: @@ %d,%d %d,%d @@ %s>" % (self.source_start,
                                                  self.source_length,
                                                  self.target_start,
                                                  self.target_length,
                                                  self.section_header)
        return make_str(value)

    def __str__(self):
        # section header is optional and thus we output it only if it's present
        head = "@@ -%d,%d +%d,%d @@%s\n" % (
            self.source_start, self.source_length,
            self.target_start, self.target_length,
            ' ' + self.section_header if self.section_header else '')
        content = ''.join(unicode(line) for line in self)
        return head + content

    def append(self, line):
        """Append the line to hunk, and keep track of source/target lines."""
        super(Hunk, self).append(line)
        s = str(line)
        if line.is_added:
            self.added += 1
            self.target.append(s)
        elif line.is_removed:
            self.removed += 1
            self.source.append(s)
        elif line.is_context:
            self.target.append(s)
            self.source.append(s)

    def is_valid(self):
        """Check hunk header data matches entered lines info."""
        return (len(self.source) == self.source_length and
                len(self.target) == self.target_length)

    def source_lines(self):
        """Hunk lines from source file (generator)."""
        return (l for l in self if l.is_context or l.is_removed)

    def target_lines(self):
        """Hunk lines from target file (generator)."""
        return (l for l in self if l.is_context or l.is_added)


class PatchedFile(list):
    """Patch updated file, it is a list of Hunks."""

    def __init__(self, patch_info=None, source='', target='',
                 source_timestamp=None, target_timestamp=None):
        super(PatchedFile, self).__init__()
        self.patch_info = patch_info
        self.source_file = source
        self.source_timestamp = source_timestamp
        self.target_file = target
        self.target_timestamp = target_timestamp

    def __repr__(self):
        return make_str("<PatchedFile: %s>") % make_str(self.path)

    def __str__(self):
        # patch info is optional
        info = '' if self.patch_info is None else str(self.patch_info)
        source = "--- %s%s\n" % (
            self.source_file,
            '\t' + self.source_timestamp if self.source_timestamp else '')
        target = "+++ %s%s\n" % (
            self.target_file,
            '\t' + self.target_timestamp if self.target_timestamp else '')
        hunks = ''.join(unicode(hunk) for hunk in self)
        return info + source + target + hunks

    def _parse_hunk(self, header, diff, encoding):
        """Parse hunk details."""
        header_info = RE_HUNK_HEADER.match(header)
        hunk_info = header_info.groups()
        hunk = Hunk(*hunk_info)

        source_line_no = hunk.source_start
        target_line_no = hunk.target_start
        expected_source_end = source_line_no + hunk.source_length
        expected_target_end = target_line_no + hunk.target_length

        for diff_line_no, line in diff:
            if encoding is not None:
                line = line.decode(encoding)

            valid_line = RE_HUNK_EMPTY_BODY_LINE.match(line)
            if not valid_line:
                valid_line = RE_HUNK_BODY_LINE.match(line)

            if not valid_line:
                raise UnidiffParseError('Hunk diff line expected: %s' % line)

            line_type = valid_line.group('line_type')
            if line_type == LINE_TYPE_EMPTY:
                line_type = LINE_TYPE_CONTEXT
            value = valid_line.group('value')
            original_line = Line(value, line_type=line_type)
            if line_type == LINE_TYPE_ADDED:
                original_line.target_line_no = target_line_no
                target_line_no += 1
            elif line_type == LINE_TYPE_REMOVED:
                original_line.source_line_no = source_line_no
                source_line_no += 1
            elif line_type == LINE_TYPE_CONTEXT:
                original_line.target_line_no = target_line_no
                target_line_no += 1
                original_line.source_line_no = source_line_no
                source_line_no += 1
            elif line_type == LINE_TYPE_NO_NEWLINE:
                pass
            else:
                original_line = None

            # stop parsing if we got past expected number of lines
            if (source_line_no > expected_source_end or
                    target_line_no > expected_target_end):
                raise UnidiffParseError('Hunk is longer than expected')

            if original_line:
                original_line.diff_line_no = diff_line_no
                hunk.append(original_line)

            # if hunk source/target lengths are ok, hunk is complete
            if (source_line_no == expected_source_end and
                    target_line_no == expected_target_end):
                break

        # report an error if we haven't got expected number of lines
        if (source_line_no < expected_source_end or
                target_line_no < expected_target_end):
            raise UnidiffParseError('Hunk is shorter than expected')

        self.append(hunk)

    def _add_no_newline_marker_to_last_hunk(self):
        if not self:
            raise UnidiffParseError(
                'Unexpected marker:' + LINE_VALUE_NO_NEWLINE)
        last_hunk = self[-1]
        last_hunk.append(
            Line(LINE_VALUE_NO_NEWLINE + '\n', line_type=LINE_TYPE_NO_NEWLINE))

    def _append_trailing_empty_line(self):
        if not self:
            raise UnidiffParseError('Unexpected trailing newline character')
        last_hunk = self[-1]
        last_hunk.append(Line('\n', line_type=LINE_TYPE_EMPTY))

    @property
    def path(self):
        """Return the file path abstracted from VCS."""
        if (self.source_file.startswith('a/') and
                self.target_file.startswith('b/')):
            filepath = self.source_file[2:]
        elif (self.source_file.startswith('a/') and
              self.target_file == '/dev/null'):
            filepath = self.source_file[2:]
        elif (self.target_file.startswith('b/') and
              self.source_file == '/dev/null'):
            filepath = self.target_file[2:]
        else:
            filepath = self.source_file
        return filepath

    @property
    def added(self):
        """Return the file total added lines."""
        return sum([hunk.added for hunk in self])

    @property
    def removed(self):
        """Return the file total removed lines."""
        return sum([hunk.removed for hunk in self])

    @property
    def is_added_file(self):
        """Return True if this patch adds the file."""
        return (len(self) == 1 and self[0].source_start == 0 and
                self[0].source_length == 0)

    @property
    def is_removed_file(self):
        """Return True if this patch removes the file."""
        return (len(self) == 1 and self[0].target_start == 0 and
                self[0].target_length == 0)

    @property
    def is_modified_file(self):
        """Return True if this patch modifies the file."""
        return not (self.is_added_file or self.is_removed_file)


@implements_to_string
class PatchSet(list):
    """A list of PatchedFiles."""

    def __init__(self, f, encoding=None):
        super(PatchSet, self).__init__()

        # convert string inputs to StringIO objects
        if isinstance(f, basestring):
            f = self._convert_string(f, encoding)

        # make sure we pass an iterator object to parse
        data = iter(f)
        # if encoding is None, assume we are reading unicode data
        self._parse(data, encoding=encoding)

    def __repr__(self):
        return make_str('<PatchSet: %s>') % super(PatchSet, self).__repr__()

    def __str__(self):
        return ''.join(unicode(patched_file) for patched_file in self)

    def _parse(self, diff, encoding):
        current_file = None
        patch_info = None

        diff = enumerate(diff, 1)
        for unused_diff_line_no, line in diff:
            if encoding is not None:
                line = line.decode(encoding)

            # check for source file header
            is_source_filename = RE_SOURCE_FILENAME.match(line)
            if is_source_filename:
                source_file = is_source_filename.group('filename')
                source_timestamp = is_source_filename.group('timestamp')
                # reset current file
                current_file = None
                continue

            # check for target file header
            is_target_filename = RE_TARGET_FILENAME.match(line)
            if is_target_filename:
                if current_file is not None:
                    raise UnidiffParseError('Target without source: %s' % line)
                target_file = is_target_filename.group('filename')
                target_timestamp = is_target_filename.group('timestamp')
                # add current file to PatchSet
                current_file = PatchedFile(
                    patch_info, source_file, target_file,
                    source_timestamp, target_timestamp)
                self.append(current_file)
                patch_info = None
                continue

            # check for hunk header
            is_hunk_header = RE_HUNK_HEADER.match(line)
            if is_hunk_header:
                if current_file is None:
                    raise UnidiffParseError('Unexpected hunk found: %s' % line)
                current_file._parse_hunk(line, diff, encoding)
                continue

            # check for no newline marker
            is_no_newline = RE_NO_NEWLINE_MARKER.match(line)
            if is_no_newline:
                if current_file is None:
                    raise UnidiffParseError('Unexpected marker: %s' % line)
                current_file._add_no_newline_marker_to_last_hunk()
                continue

            # sometimes hunks can be followed by empty lines
            if line == '\n' and current_file is not None:
                current_file._append_trailing_empty_line()
                continue

            # if nothing has matched above then this line is a patch info
            if patch_info is None:
                current_file = None
                patch_info = PatchInfo()
            patch_info.append(line)

    @classmethod
    def from_filename(cls, filename, encoding=DEFAULT_ENCODING, errors=None):
        """Return a PatchSet instance given a diff filename."""
        with open_file(filename, 'r', encoding=encoding, errors=errors) as f:
            instance = cls(f)
        return instance

    @staticmethod
    def _convert_string(data, encoding=None, errors='strict'):
        if encoding is not None:
            # if encoding is given, assume bytes and decode
            data = unicode(data, encoding=encoding, errors=errors)
        return StringIO(data)

    @classmethod
    def from_string(cls, data, encoding=None, errors='strict'):
        """Return a PatchSet instance given a diff string."""
        return cls(cls._convert_string(data, encoding, errors))

    @property
    def added_files(self):
        """Return patch added files as a list."""
        return [f for f in self if f.is_added_file]

    @property
    def removed_files(self):
        """Return patch removed files as a list."""
        return [f for f in self if f.is_removed_file]

    @property
    def modified_files(self):
        """Return patch modified files as a list."""
        return [f for f in self if f.is_modified_file]

    @property
    def added(self):
        """Return the patch total added lines."""
        return sum([f.added for f in self])

    @property
    def removed(self):
        """Return the patch total removed lines."""
        return sum([f.removed for f in self])


================================================
FILE: devutils/update_lists.py
================================================
#!/usr/bin/env python3

# Copyright (c) 2019 The ungoogled-chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""
Update binary pruning and domain substitution lists automatically.

It will download and unpack into the source tree as necessary.
No binary pruning or domain substitution will be applied to the source tree after
the process has finished.
"""

import argparse
import os
import sys

from itertools import repeat
from multiprocessing import Pool
from pathlib import Path, PurePosixPath

sys.path.insert(0, str(Path(__file__).resolve().parent.parent / 'utils'))
from _common import get_logger
from domain_substitution import DomainRegexList, TREE_ENCODINGS
from prune_binaries import CONTINGENT_PATHS

sys.path.pop(0)

# Encoding for output files
_ENCODING = 'UTF-8'

# pylint: disable=line-too-long

# NOTE: Include patterns have precedence over exclude patterns
# pathlib.Path.match() paths to include in binary pruning
PRUNING_INCLUDE_PATTERNS = [
    'components/domain_reliability/baked_in_configs/*',
    # Removals for patches/core/ungoogled-chromium/remove-unused-preferences-fields.patch
    'components/safe_browsing/core/common/safe_browsing_prefs.cc',
    'components/safe_browsing/core/common/safe_browsing_prefs.h',
    'components/signin/public/base/signin_pref_names.cc',
    'components/signin/public/base/signin_pref_names.h',
]

# pathlib.Path.match() paths to exclude from binary pruning
PRUNING_EXCLUDE_PATTERNS = [
    'chrome/common/win/eventlog_messages.mc', # TODO: False positive textfile
    # Exclusions for DOM distiller (contains model data only)
    'components/dom_distiller/core/data/distillable_page_model_new.bin',
    'components/dom_distiller/core/data/long_page_model.bin',
    # Exclusions for GeoLanguage data
    # Details: https://docs.google.com/document/d/18WqVHz5F9vaUiE32E8Ge6QHmku2QSJKvlqB9JjnIM-g/edit
    # Introduced with: https://chromium.googlesource.com/chromium/src/+/6647da61
    'components/language/content/browser/ulp_language_code_locator/geolanguage-data_rank0.bin',
    'components/language/content/browser/ulp_language_code_locator/geolanguage-data_rank1.bin',
    'components/language/content/browser/ulp_language_code_locator/geolanguage-data_rank2.bin',
    # Exclusion for required prebuilt object for Windows arm64 builds
    'third_party/crashpad/crashpad/util/misc/capture_context_win_arm64.obj',
    'third_party/icu/common/icudtl.dat', # Exclusion for ICU data
    # Exclusion for Android
    'build/android/chromium-debug.keystore',
    'third_party/icu/android/icudtl.dat',
    'third_party/icu/common/icudtb.dat',
    # Exclusion for rollup v4.0+
    'third_party/node/node_modules/@rollup/wasm-node/dist/wasm-node/bindings_wasm_bg.wasm',
    # Exclusion for performance tracing
    'third_party/perfetto/src/trace_processor/importers/proto/atoms.descriptor',
    # Exclusion for zoneinfo64
    'third_party/rust/chromium_crates_io/vendor/zoneinfo64-v0_2/src/data/zoneinfo64.res',
    # Exclusions for safe file extensions
    '*.avif',
    '*.ttf',
    '*.png',
    '*.jpg',
    '*.webp',
    '*.gif',
    '*.ico',
    '*.mp3',
    '*.wav',
    '*.flac',
    '*.car',
    '*.icns',
    '*.woff',
    '*.woff2',
    '*makefile',
    '*.profdata',
    '*.xcf',
    '*.cur',
    '*.pdf',
    '*.ai',
    '*.h',
    '*.c',
    '*.cpp',
    '*.cc',
    '*.mk',
    '*.bmp',
    '*.py',
    '*.xml',
    '*.html',
    '*.js',
    '*.json',
    '*.txt',
    '*.binarypb',
    '*.xtb'
]

# NOTE: Domain substitution path prefix exclusion has precedence over inclusion patterns
# Paths to exclude by prefixes of the POSIX representation for domain substitution
DOMAIN_EXCLUDE_PREFIXES = [
    'components/test/',
    'net/http/transport_security_state_static.json',
    'net/http/transport_security_state_static_pins.json',
    # Exclusions for Visual Studio Project generation with GN (PR #445)
    'tools/gn/',
    # Exclusions for files covered with other patches/unnecessary
    'third_party/search_engines_data/resources/definitions/prepopulated_engines.json',
    'third_party/blink/renderer/core/dom/document.cc',
    # Exclusion to allow download of sysroots
    'build/linux/sysroot_scripts/sysroots.json',
    # Licenses and credits
    'tools/licenses/licenses.py',
]

# pylint: enable=line-too-long

# pathlib.Path.match() patterns to include in domain substitution
DOMAIN_INCLUDE_PATTERNS = [
    '*.h', '*.hh', '*.hpp', '*.hxx', '*.cc', '*.cpp', '*.cxx', '*.c', '*.h', '*.json', '*.js',
    '*.html', '*.htm', '*.css', '*.py*', '*.grd*', '*.sql', '*.idl', '*.mk', '*.gyp*', 'makefile',
    '*.ts', '*.txt', '*.xml', '*.mm', '*.jinja*', '*.gn', '*.gni'
]

# Binary-detection constant
_TEXTCHARS = bytearray({7, 8, 9, 10, 12, 13, 27} | set(range(0x20, 0x100)) - {0x7f})


class UnusedPatterns: #pylint: disable=too-few-public-methods
    """Tracks unused prefixes and patterns"""

    _all_names = ('pruning_include_patterns', 'pruning_exclude_patterns', 'domain_include_patterns',
                  'domain_exclude_prefixes')

    def __init__(self):
        # Initialize all tracked patterns and prefixes in sets
        # Users will discard elements that are used
        for name in self._all_names:
            setattr(self, name, set(globals()[name.upper()]))

    def log_unused(self, error=True):
        """
        Logs unused patterns and prefixes

        Returns True if there are unused patterns or prefixes; False otherwise
        """
        have_unused = False
        log = get_logger().error if error else get_logger().info
        for name in self._all_names:
            current_set = getattr(self, name, None)
            if current_set:
                log('Unused from %s: %s', name.upper(), current_set)
                have_unused = True
        return have_unused


def _is_binary(bytes_data):
    """
    Returns True if the data seems to be binary data (i.e. not human readable); False otherwise
    """
    # From: https://stackoverflow.com/a/7392391
    return bool(bytes_data.translate(None, _TEXTCHARS))


def _dir_empty(path):
    """
    Returns True if the directory is empty; False otherwise

    path is a pathlib.Path or string to a directory to test.
    """
    try:
        next(os.scandir(str(path)))
    except StopIteration:
        return True
    return False


def should_prune(path, relative_path, used_pep_set, used_pip_set):
    """
    Returns True if a path should be pruned from the source tree; False otherwise

    path is the pathlib.Path to the file from the current working directory.
    relative_path is the pathlib.Path to the file from the source tree
    used_pep_set is a list of PRUNING_EXCLUDE_PATTERNS that have been matched
    used_pip_set is a list of PRUNING_INCLUDE_PATTERNS that have been matched
    """
    # Match against include patterns
    for pattern in filter(relative_path.match, PRUNING_INCLUDE_PATTERNS):
        used_pip_set.add(pattern)
        return True

    # Match against exclude patterns
    for pattern in filter(Path(str(relative_path).lower()).match, PRUNING_EXCLUDE_PATTERNS):
        used_pep_set.add(pattern)
        return False

    # Do binary data detection
    with path.open('rb') as file_obj:
        if _is_binary(file_obj.read()):
            return True

    # Passed all filtering; do not prune
    return False


def _check_regex_match(file_path, search_regex):
    """
    Returns True if a regex pattern matches a file; False otherwise

    file_path is a pathlib.Path to the file to test
    search_regex is a compiled regex object to search for domain names
    """
    with file_path.open("rb") as file_obj:
        file_bytes = file_obj.read()
        content = None
        for encoding in TREE_ENCODINGS:
            try:
                content = file_bytes.decode(encoding)
                break
            except UnicodeDecodeError:
                continue
        if not search_regex.search(content) is None:
            return True
    return False


def should_domain_substitute(path, relative_path, search_regex, used_dep_set, used_dip_set):
    """
    Returns True if a path should be domain substituted in the source tree; False otherwise

    path is the pathlib.Path to the file from the current working directory.
    relative_path is the pathlib.Path to the file from the source tree.
    used_dep_set is a list of DOMAIN_EXCLUDE_PREFIXES that have been matched
    used_dip_set is a list of DOMAIN_INCLUDE_PATTERNS that have been matched
    """
    relative_path_posix = relative_path.as_posix().lower()
    for include_pattern in DOMAIN_INCLUDE_PATTERNS:
        if PurePosixPath(relative_path_posix).match(include_pattern):
            used_dip_set.add(include_pattern)
            for exclude_prefix in DOMAIN_EXCLUDE_PREFIXES:
                if relative_path_posix.startswith(exclude_prefix):
                    used_dep_set.add(exclude_prefix)
                    return False
            # Skip LICENSE.* files so that they remain untouched.
            for license_path in ['license', 'license.txt', 'license.html']:
                if relative_path_posix.endswith('/' + license_path):
                    return False
            return _check_regex_match(path, search_regex)
    return False


def compute_lists_proc(path, source_tree, search_regex):
    """
    Adds the path to appropriate lists to be used by compute_lists.

    path is the pathlib.Path to the file from the current working directory.
    source_tree is a pathlib.Path to the source tree
    search_regex is a compiled regex object to search for domain names
    """
    used_pep_set = set() # PRUNING_EXCLUDE_PATTERNS
    used_pip_set = set() # PRUNING_INCLUDE_PATTERNS
    used_dep_set = set() # DOMAIN_EXCLUDE_PREFIXES
    used_dip_set = set() # DOMAIN_INCLUDE_PATTERNS
    pruning_set = set()
    domain_substitution_set = set()
    symlink_set = set()
    if path.is_file():
        relative_path = path.relative_to(source_tree)
        if not any(str(relative_path.as_posix()).startswith(cpath) for cpath in CONTINGENT_PATHS):
            if path.is_symlink():
                try:
                    resolved_relative_posix = path.resolve().relative_to(source_tree).as_posix()
                    symlink_set.add((resolved_relative_posix, relative_path.as_posix()))
                except ValueError:
                    # Symlink leads out of the source tree
                    pass
            elif not any(skip in ('.git', '__pycache__', 'uc_staging') for skip in path.parts):
                try:
                    if should_prune(path, relative_path, used_pep_set, used_pip_set):
                        pruning_set.add(relative_path.as_posix())
                    elif should_domain_substitute(path, relative_path, search_regex, used_dep_set,
                                                  used_dip_set):
                        domain_substitution_set.add(relative_path.as_posix())
                except: #pylint: disable=bare-except
                    get_logger().exception('Unhandled exception while processing %s', relative_path)
    return (used_pep_set, used_pip_set, used_dep_set, used_dip_set, pruning_set,
            domain_substitution_set, symlink_set)


def compute_lists(source_tree, search_regex, processes): # pylint: disable=too-many-locals
    """
    Compute the binary pruning and domain substitution lists of the source tree.
    Returns a tuple of three items in the following order:
    1. The sorted binary pruning list
    2. The sorted domain substitution list
    3. An UnusedPatterns object

    source_tree is a pathlib.Path to the source tree
    search_regex is a compiled regex object to search for domain names
    processes is the maximum number of worker processes to create
    """
    pruning_set = set()
    domain_substitution_set = set()
    symlink_set = set() # POSIX resolved path -> set of POSIX symlink paths
    source_tree = source_tree.resolve()
    unused_patterns = UnusedPatterns()

    # Launch multiple processes iterating over the source tree
    with Pool(processes) as procpool:
        returned_data = procpool.starmap(
            compute_lists_proc,
            zip(source_tree.rglob('*'), repeat(source_tree), repeat(search_regex)))

    # Handle the returned data
    for (used_pep_set, used_pip_set, used_dep_set, used_dip_set, returned_pruning_set,
         returned_domain_sub_set, returned_symlink_set) in returned_data:
        # pragma pylint: disable=no-member
        unused_patterns.pruning_exclude_patterns.difference_update(used_pep_set)
        unused_patterns.pruning_include_patterns.difference_update(used_pip_set)
        unused_patterns.domain_exclude_prefixes.difference_update(used_dep_set)
        unused_patterns.domain_include_patterns.difference_update(used_dip_set)
        # pragma pylint: enable=no-member
        pruning_set.update(returned_pruning_set)
        domain_substitution_set.update(returned_domain_sub_set)
        symlink_set.update(returned_symlink_set)

    # Prune symlinks for pruned files
    for (resolved, symlink) in symlink_set:
        if resolved in pruning_set:
            pruning_set.add(symlink)

    return sorted(pruning_set), sorted(domain_substitution_set), unused_patterns


def main(args_list=None):
    """CLI entrypoint"""
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('--pruning',
                        metavar='PATH',
                        type=Path,
                        default='pruning.list',
                        help='The path to store pruning.list. Default: %(default)s')
    parser.add_argument('--domain-substitution',
                        metavar='PATH',
                        type=Path,
                        default='domain_substitution.list',
                        help='The path to store domain_substitution.list. Default: %(default)s')
    parser.add_argument('--domain-regex',
                        metavar='PATH',
                        type=Path,
                        default='domain_regex.list',
                        help='The path to domain_regex.list. Default: %(default)s')
    parser.add_argument('-t',
                        '--tree',
                        metavar='PATH',
                        type=Path,
                        required=True,
                        help='The path to the source tree to use.')
    parser.add_argument(
        '--processes',
        metavar='NUM',
        type=int,
        default=None,
        help=
        'The maximum number of worker processes to create. Defaults to the number of system CPUs.')
    parser.add_argument('--domain-exclude-prefix',
                        metavar='PREFIX',
                        type=str,
                        action='append',
                        help='Additional exclusion for domain_substitution.list.')
    parser.add_argument('--no-error-unused',
                        action='store_false',
                        dest='error_unused',
                        help='Do not treat unused patterns/prefixes as an error.')
    args = parser.parse_args(args_list)
    if args.domain_exclude_prefix is not None:
        DOMAIN_EXCLUDE_PREFIXES.extend(args.domain_exclude_prefix)
    if args.tree.exists() and not _dir_empty(args.tree):
        get_logger().info('Using existing source tree at %s', args.tree)
    else:
        get_logger().error('No source tree found. Aborting.')
        sys.exit(1)
    get_logger().info('Computing lists...')
    pruning_set, domain_substitution_set, unused_patterns = compute_lists(
        args.tree,
        DomainRegexList(args.domain_regex).search_regex, args.processes)
    with args.pruning.open('w', encoding=_ENCODING) as file_obj:
        file_obj.writelines(f'{line}\n' for line in pruning_set)
    with args.domain_substitution.open('w', encoding=_ENCODING) as file_obj:
        file_obj.writelines(f'{line}\n' for line in domain_substitution_set)
    if unused_patterns.log_unused(args.error_unused) and args.error_unused:
        get_logger().error('Please update or remove unused patterns and/or prefixes. '
                           'The lists have still been updated with the remaining valid entries.')
        sys.exit(1)


if __name__ == "__main__":
    main()


================================================
FILE: devutils/update_platform_patches.py
================================================
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Copyright (c) 2019 The ungoogled-chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""
Utility to ease the updating of platform patches against ungoogled-chromium's patches
"""

import argparse
import os
import shutil
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent.parent / 'utils'))
from _common import ENCODING, get_logger
from patches import merge_patches

sys.path.pop(0)

_SERIES = 'series'
_SERIES_ORIG = 'series.orig'
_SERIES_PREPEND = 'series.prepend'
_SERIES_MERGED = 'series.merged'


def merge_platform_patches(platform_patches_dir, prepend_patches_dir):
    '''
    Prepends prepend_patches_dir into platform_patches_dir

    Returns True if successful, False otherwise
    '''
    if not (platform_patches_dir / _SERIES).exists():
        get_logger().error('Unable to find platform series file: %s',
                           platform_patches_dir / _SERIES)
        return False

    # Make series.orig file
    shutil.copyfile(str(platform_patches_dir / _SERIES), str(platform_patches_dir / _SERIES_ORIG))

    # Make series.prepend
    shutil.copyfile(str(prepend_patches_dir / _SERIES), str(platform_patches_dir / _SERIES_PREPEND))

    # Merge patches
    merge_patches([prepend_patches_dir], platform_patches_dir, prepend=True)
    (platform_patches_dir / _SERIES).replace(platform_patches_dir / _SERIES_MERGED)

    return True


def _dir_empty(path):
    '''
    Returns True if the directory exists and is empty; False otherwise
    '''
    try:
        next(os.scandir(str(path)))
    except StopIteration:
        return True
    except FileNotFoundError:
        pass
    return False


def _rename_files_with_dirs(root_dir, source_dir, sorted_file_iter):
    '''
    Moves a list of sorted files back to their original location,
    removing empty directories along the way
    '''
    past_parent = None
    for partial_path in sorted_file_iter:
        complete_path = Path(root_dir, partial_path)
        complete_source_path = Path(source_dir, partial_path)
        try:
            complete_path.rename(complete_source_path)
        except FileNotFoundError:
            get_logger().warning('Could not move prepended patch: %s', complete_path)
        if past_parent != complete_path.parent:
            while past_parent and _dir_empty(past_parent):
                past_parent.rmdir()
                past_parent = past_parent.parent
            past_parent = complete_path.parent
    # Handle last path's directory
    while _dir_empty(complete_path.parent):
        complete_path.parent.rmdir()
        complete_path = complete_path.parent


def unmerge_platform_patches(platform_patches_dir, prepend_patches_dir):
    '''
    Undo merge_platform_patches(), adding any new patches from series.merged as necessary

    Returns True if successful, False otherwise
    '''
    if not (platform_patches_dir / _SERIES_PREPEND).exists():
        get_logger().error('Unable to find series.prepend at: %s',
                           platform_patches_dir / _SERIES_PREPEND)
        return False
    prepend_series = set(
        filter(len,
               (platform_patches_dir / _SERIES_PREPEND).read_text(encoding=ENCODING).splitlines()))

    # Move prepended files back to original location, preserving changes
    _rename_files_with_dirs(platform_patches_dir, prepend_patches_dir, sorted(prepend_series))

    # Determine positions of blank spaces in series.orig
    if not (platform_patches_dir / _SERIES_ORIG).exists():
        get_logger().error('Unable to find series.orig at: %s', platform_patches_dir / _SERIES_ORIG)
        return False
    orig_series = (platform_patches_dir / _SERIES_ORIG).read_text(encoding=ENCODING).splitlines()
    # patch path -> list of lines after patch path and before next patch path
    path_comments = {}
    # patch path -> inline comment for patch
    path_inline_comments = {}
    previous_path = None
    for partial_path in orig_series:
        if not partial_path or partial_path.startswith('#'):
            if partial_path not in path_comments:
                path_comments[previous_path] = []
            path_comments[previous_path].append(partial_path)
        else:
            path_parts = partial_path.split(' #', maxsplit=1)
            previous_path = path_parts[0]
            if len(path_parts) == 2:
                path_inline_comments[path_parts[0]] = path_parts[1]

    # Apply changes on series.merged into a modified version of series.orig
    if not (platform_patches_dir / _SERIES_MERGED).exists():
        get_logger().error('Unable to find series.merged at: %s',
                           platform_patches_dir / _SERIES_MERGED)
        return False
    new_series = filter(len, (platform_patches_dir /
                              _SERIES_MERGED).read_text(encoding=ENCODING).splitlines())
    new_series = filter((lambda x: x not in prepend_series), new_series)
    new_series = list(new_series)
    series_index = 0
    while series_index < len(new_series):
        current_path = new_series[series_index]
        if current_path in path_inline_comments:
            new_series[series_index] = current_path + ' #' + path_inline_comments[current_path]
        if current_path in path_comments:
            new_series.insert(series_index + 1, '\n'.join(path_comments[current_path]))
            series_index += 1
        series_index += 1

    # Write series file
    with (platform_patches_dir / _SERIES).open('w', encoding=ENCODING) as series_file:
        series_file.write('\n'.join(new_series))
        series_file.write('\n')

    # All other operations are successful; remove merging intermediates
    (platform_patches_dir / _SERIES_MERGED).unlink()
    (platform_patches_dir / _SERIES_ORIG).unlink()
    (platform_patches_dir / _SERIES_PREPEND).unlink()

    return True


def main():
    """CLI Entrypoint"""
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('command',
                        choices=('merge', 'unmerge'),
                        help='Merge or unmerge ungoogled-chromium patches with platform patches')
    parser.add_argument('platform_patches',
                        type=Path,
                        help='The path to the platform patches in GNU Quilt format to merge into')
    args = parser.parse_args()

    repo_dir = Path(__file__).resolve().parent.parent

    success = False
    prepend_patches_dir = repo_dir / 'patches'
    if args.command == 'merge':
        success = merge_platform_patches(args.platform_patches, prepend_patches_dir)
    elif args.command == 'unmerge':
        success = unmerge_platform_patches(args.platform_patches, prepend_patches_dir)
    else:
        raise NotImplementedError(args.command)

    if success:
        return 0
    return 1


if __name__ == '__main__':
    sys.exit(main())


================================================
FILE: devutils/validate_config.py
================================================
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-

# Copyright (c) 2019 The ungoogled-chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Run sanity checking algorithms over ungoogled-chromium's config files

NOTE: This script is hardcoded to run over ungoogled-chromium's config files only.
To check other files, use the other scripts imported by this script.

It checks the following:

    * All patches exist
    * All patches are referenced by the patch order
    * Each patch is used only once
    * GN flags in flags.gn are sorted and not duplicated
    * downloads.ini has the correct format (i.e. conforms to its schema)

Exit codes:
    * 0 if no problems detected
    * 1 if warnings or errors occur
"""

import sys
from pathlib import Path

from check_downloads_ini import check_downloads_ini
from check_gn_flags import check_gn_flags
from check_patch_files import (check_patch_readability, check_series_duplicates,
                               check_unused_patches)


def main():
    """CLI entrypoint"""

    warnings = False
    root_dir = Path(__file__).resolve().parent.parent
    patches_dir = root_dir / 'patches'

    # Check patches
    warnings |= check_patch_readability(patches_dir)
    warnings |= check_series_duplicates(patches_dir)
    warnings |= check_unused_patches(patches_dir)

    # Check GN flags
    warnings |= check_gn_flags(root_dir / 'flags.gn')

    # Check downloads.ini
    warnings |= check_downloads_ini([root_dir / 'downloads.ini'])

    if warnings:
        sys.exit(1)
    sys.exit(0)


if __name__ == '__main__':
    if sys.argv[1:]:
        print(__doc__)
    else:
        main()


================================================
FILE: devutils/validate_patches.py
================================================
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Copyright (c) 2020 The ungoogled-chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""
Validates that all patches apply cleanly against the source tree.

The required source tree files can be retrieved from Google directly.
"""

import argparse
import ast
import base64
import email.utils
import json
import logging
import sys
import tempfile
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent / 'third_party'))
import unidiff
from unidiff.constants import LINE_TYPE_EMPTY, LINE_TYPE_NO_NEWLINE

sys.path.pop(0)

sys.path.insert(0, str(Path(__file__).resolve().parent.parent / 'utils'))
from domain_substitution import TREE_ENCODINGS
from _common import ENCODING, get_logger, get_chromium_version, parse_series, add_common_params
from patches import dry_run_check

sys.path.pop(0)

try:
    import requests
    import requests.adapters
    import urllib3.util

    class _VerboseRetry(urllib3.util.Retry):
        """A more verbose version of HTTP Adatper about retries"""

        def sleep_for_retry(self, response=None):
            """Sleeps for Retry-After, and logs the sleep time"""
            if response:
                retry_after = self.get_retry_after(response)
                if retry_after:
                    get_logger().info(
                        'Got HTTP status %s with Retry-After header. Retrying after %s seconds...',
                        response.status, retry_after)
                else:
                    get_logger().info(
                        'Could not find Retry-After header for HTTP response %s. Status reason: %s',
                        response.status, response.reason)
            return super().sleep_for_retry(response)

        def _sleep_backoff(self):
            """Log info about backoff sleep"""
            get_logger().info('Running HTTP request sleep backoff')
            super()._sleep_backoff()

    def _get_requests_session():
        session = requests.Session()
        http_adapter = requests.adapters.HTTPAdapter(
            max_retries=_VerboseRetry(total=10,
                                      read=10,
                                      connect=10,
                                      backoff_factor=8,
                                      status_forcelist=urllib3.Retry.RETRY_AFTER_STATUS_CODES,
                                      raise_on_status=False))
        session.mount('http://', http_adapter)
        session.mount('https://', http_adapter)
        return session
except ImportError:

    def _get_requests_session():
        raise RuntimeError('The Python module "requests" is required for remote'
                           'file downloading. It can be installed from PyPI.')


_ROOT_DIR = Path(__file__).resolve().parent.parent
_SRC_PATH = Path('src')


class _PatchValidationError(Exception):
    """Raised when patch validation fails"""


class _UnexpectedSyntaxError(RuntimeError):
    """Raised when unexpected syntax is used in DEPS"""


class _NotInRepoError(RuntimeError):
    """Raised when the remote file is not present in the given repo"""


class _DepsNodeVisitor(ast.NodeVisitor):
    _valid_syntax_types = (ast.mod, ast.expr_context, ast.boolop, ast.Assign, ast.Add, ast.Name,
                           ast.Dict, ast.Constant, ast.List, ast.BinOp)
    _allowed_callables = ('Var', )

    def visit_Call(self, node): #pylint: disable=invalid-name
        """Override Call syntax handling"""
        if node.func.id not in self._allowed_callables:
            raise _UnexpectedSyntaxError(f'Unexpected call of "{node.func.id}" '
                                         f'at line {node.lineno}, column {node.col_offset}')

    def generic_visit(self, node):
        for ast_type in self._valid_syntax_types:
            if isinstance(node, ast_type):
                super().generic_visit(node)
                return
        raise _UnexpectedSyntaxError(f'Unexpected {type(node).__name__} '
                                     f'at line {node.lineno}, column {node.col_offset}')


def _validate_deps(deps_text):
    """Returns True if the DEPS file passes validation; False otherwise"""
    try:
        _DepsNodeVisitor().visit(ast.parse(deps_text))
    except _UnexpectedSyntaxError as exc:
        get_logger().error('%s', exc)
        return False
    return True


def _deps_var(deps_globals):
    """Return a function that implements DEPS's Var() function"""

    def _var_impl(var_name):
        """Implementation of Var() in DEPS"""
        return deps_globals['vars'][var_name]

    return _var_impl


def _parse_deps(deps_text):
    """Returns a dict of parsed DEPS data"""
    deps_globals = {'__builtins__': None}
    deps_globals['Var'] = _deps_var(deps_globals)
    exec(deps_text, deps_globals) #pylint: disable=exec-used
    return deps_globals


def _download_googlesource_file(download_session, repo_url, version, relative_path):
    """
    Returns the contents of the text file with path within the given
    googlesource.com repo as a string.
    """
    if 'googlesource.com' not in repo_url:
        raise ValueError(f'Repository URL is not a googlesource.com URL: {repo_url}')
    full_url = repo_url + f'/+/{version}/{str(relative_path)}?format=TEXT'
    get_logger().debug('Downloading: %s', full_url)
    response = download_session.get(full_url)
    if response.status_code == 404:
        raise _NotInRepoError()
    response.raise_for_status()
    # Assume all files that need patching are compatible with UTF-8
    return base64.b64decode(response.text, validate=True).decode('UTF-8')


def _get_dep_value_url(deps_globals, dep_value):
    """Helper for _process_deps_entries"""
    if isinstance(dep_value, str):
        url = dep_value
    elif isinstance(dep_value, dict):
        if 'url' not in dep_value:
            # Ignore other types like CIPD since
            # it probably isn't necessary
            return None
        url = dep_value['url']
    else:
        raise NotImplementedError()
    if '{' in url:
        # Probably a Python format string
        url = url.format(**deps_globals['vars'])
    if url.count('@') != 1:
        raise _PatchValidationError(f'Invalid number of @ symbols in URL: {url}')
    return url


def _process_deps_entries(deps_globals, child_deps_tree, child_path, deps_use_relative_paths):
    """Helper for _get_child_deps_tree"""
    for dep_path_str, dep_value in deps_globals.get('deps', {}).items():
        url = _get_dep_value_url(deps_globals, dep_value)
        if url is None:
            continue
        dep_path = Path(dep_path_str)
        if not deps_use_relative_paths:
            try:
                dep_path = Path(dep_path_str).relative_to(child_path)
            except ValueError:
                # Not applicable to the current DEPS tree path
                continue
        grandchild_deps_tree = None # Delaying creation of dict() until it's needed
        for recursedeps_item in deps_globals.get('recursedeps', tuple()):
            if isinstance(recursedeps_item, str):
                if recursedeps_item == str(dep_path):
                    grandchild_deps_tree = 'DEPS'
            else: # Some sort of iterable
                recursedeps_item_path, recursedeps_item_depsfile = recursedeps_item
                if recursedeps_item_path == str(dep_path):
                    grandchild_deps_tree = recursedeps_item_depsfile
        if grandchild_deps_tree is None:
            # This dep is not recursive; i.e. it is fully loaded
            grandchild_deps_tree = {}
        child_deps_tree[dep_path] = (*url.split('@'), grandchild_deps_tree)


def _get_child_deps_tree(download_session, current_deps_tree, child_path, deps_use_relative_paths):
    """Helper for _download_source_file"""
    repo_url, version, child_deps_tree = current_deps_tree[child_path]
    if isinstance(child_deps_tree, str):
        # Load unloaded DEPS
        deps_globals = _parse_deps(
            _download_googlesource_file(download_session, repo_url, version, child_deps_tree))
        child_deps_tree = {}
        current_deps_tree[child_path] = (repo_url, version, child_deps_tree)
        deps_use_relative_paths = deps_globals.get('use_relative_paths', False)
        _process_deps_entries(deps_globals, child_deps_tree, child_path, deps_use_relative_paths)
    return child_deps_tree, deps_use_relative_paths


def _get_last_chromium_modification():
    """Returns the last modification date of the chromium-browser-official tar file"""
    with _get_requests_session() as session:
        response = session.head('https://storage.googleapis.com/chromium-browser-official/'
                                f'chromium-{get_chromium_version()}.tar.xz')
        response.raise_for_status()
        return email.utils.parsedate_to_datetime(response.headers['Last-Modified'])


def _get_gitiles_git_log_date(log_entry):
    """Helper for _get_gitiles_git_log_date"""
    return email.utils.parsedate_to_datetime(log_entry['committer']['time'])


def _get_gitiles_commit_before_date(repo_url, target_branch, target_datetime):
    """Returns the hexadecimal hash of the closest commit before target_datetime"""
    json_log_url = f'{repo_url}/+log/{target_branch}?format=JSON'
    with _get_requests_session() as session:
        response = session.get(json_log_url)
        response.raise_for_status()
        git_log = json.loads(response.text[5:]) # Trim closing delimiters for various structures
    assert len(git_log) == 2 # 'log' and 'next' entries
    assert 'log' in git_log
    assert git_log['log']
    git_log = git_log['log']
    # Check boundary conditions
    if _get_gitiles_git_log_date(git_log[0]) < target_datetime:
        # Newest commit is older than target datetime
        return git_log[0]['commit']
    if _get_gitiles_git_log_date(git_log[-1]) > target_datetime:
        # Oldest commit is newer than the target datetime; assume oldest is close enough.
        get_logger().warning('Oldest entry in gitiles log for repo "%s" is newer than target; '
                             'continuing with oldest entry...')
        return git_log[-1]['commit']
    # Do binary search
    low_index = 0
    high_index = len(git_log) - 1
    mid_index = high_index
    while low_index != high_index:
        mid_index = low_index + (high_index - low_index) // 2
        if _get_gitiles_git_log_date(git_log[mid_index]) > target_datetime:
            low_index = mid_index + 1
        else:
            high_index = mid_index
    return git_log[mid_index]['commit']


class _FallbackRepoManager:
    """Retrieves fallback repos and caches data needed for determining repos"""

    _GN_REPO_URL = 'https://gn.googlesource.com/gn.git'

    def __init__(self):
        self._cache_gn_version = None

    @property
    def gn_version(self):
        """
        Returns the version of the GN repo for the Chromium version used by this code
        """
        if not self._cache_gn_version:
            # Because there seems to be no reference to the logic for generating the
            # chromium-browser-official tar file, it's possible that it is being generated
            # by an internal script that manually injects the GN repository files.
            # Therefore, assume that the GN version used in the chromium-browser-official tar
            # files correspond to the latest commit in the master branch of the GN repository
            # at the time of the tar file's generation. We can get an approximation for the
            # generation time by using the last modification date of the tar file on
            # Google's file server.
            self._cache_gn_version = _get_gitiles_commit_before_date(
                self._GN_REPO_URL, 'master', _get_last_chromium_modification())
        return self._cache_gn_version

    def get_fallback(self, current_relative_path, current_node, root_deps_tree):
        """
        Helper for _download_source_file

        It returns a new (repo_url, version, new_relative_path) to attempt a file download with
        """
        assert len(current_node) == 3
        # GN special processing
        try:
            new_relative_path = current_relative_path.relative_to('tools/gn')
        except ValueError:
            pass
        else:
            if current_node is root_deps_tree[_SRC_PATH]:
                get_logger().info('Redirecting to GN repo version %s for path: %s', self.gn_version,
                                  current_relative_path)
                return (self._GN_REPO_URL, self.gn_version, new_relative_path)
        return None, None, None


def _get_target_file_deps_node(download_session, root_deps_tree, target_file):
    """
    Helper for _download_source_file

    Returns the corresponding repo containing target_file based on the DEPS tree
    """
    # The "deps" from the current DEPS file
    current_deps_tree = root_deps_tree
    current_node = None
    # Path relative to the current node (i.e. DEPS file)
    current_relative_path = Path('src', target_file)
    previous_relative_path = None
    deps_use_relative_paths = False
    child_path = None
    while current_relative_path != previous_relative_path:
        previous_relative_path = current_relative_path
        for child_path in current_deps_tree:
            try:
                current_relative_path = previous_relative_path.relative_to(child_path)
            except ValueError:
                # previous_relative_path does not start with child_path
                continue
            current_node = current_deps_tree[child_path]
            # current_node will match with current_deps_tree after the following statement
            current_deps_tree, deps_use_relative_paths = _get_child_deps_tree(
                download_session, current_deps_tree, child_path, deps_use_relative_paths)
            break
    assert not current_node is None
    return current_node, current_relative_path


def _download_source_file(download_session, root_deps_tree, fallback_repo_manager, target_file):
    """
    Downloads the source tree file from googlesource.com

    download_session is an active requests.Session() object
    deps_dir is a pathlib.Path to the directory containing a DEPS file.
    """
    current_node, current_relative_path = _get_target_file_deps_node(download_session,
                                                                     root_deps_tree, target_file)
    # Attempt download with potential fallback logic
    repo_url, version, _ = current_node
    try:
        # Download with DEPS-provided repo
        return _download_googlesource_file(download_session, repo_url, version,
                                           current_relative_path)
    except _NotInRepoError:
        pass
    get_logger().debug(
        'Path "%s" (relative: "%s") not found using DEPS tree; finding fallback repo...',
        target_file, current_relative_path)
    repo_url, version, current_relative_path = fallback_repo_manager.get_fallback(
        current_relative_path, current_node, root_deps_tree)
    if not repo_url:
        get_logger().error('No fallback repo found for "%s" (relative: "%s")', target_file,
                           current_relative_path)
        raise _NotInRepoError()
    try:
        # Download with fallback repo
        return _download_googlesource_file(download_session, repo_url, version,
                                           current_relative_path)
    except _NotInRepoError:
        pass
    get_logger().error('File "%s" (relative: "%s") not found in fallback repo "%s", version "%s"',
                       target_file, current_relative_path, repo_url, version)
    raise _NotInRepoError()


def _initialize_deps_tree():
    """
    Initializes and returns a dependency tree for DEPS files

    The DEPS tree is a dict has the following format:
    key - pathlib.Path relative to the DEPS file's path
    value - tuple(repo_url, version, recursive dict here)
        repo_url is the URL to the dependency's repository root
        If the recursive dict is a string, then it is a string to the DEPS file to load
            if needed

    download_session is an active requests.Session() object
    """
    root_deps_tree = {
        _SRC_PATH: ('https://chromium.googlesource.com/chromium/src.git', get_chromium_version(),
                    'DEPS')
    }
    return root_deps_tree


def _retrieve_remote_files(file_iter):
    """
    Retrieves all file paths in file_iter from Google

    file_iter is an iterable of strings that are relative UNIX paths to
        files in the Chromium source.

    Returns a dict of relative UNIX path strings to a list of lines in the file as strings
    """

    files = {}

    root_deps_tree = _initialize_deps_tree()

    try:
        total_files = len(file_iter)
    except TypeError:
        total_files = None

    logger = get_logger()
    if total_files is None:
        logger.info('Downloading remote files...')
    else:
        logger.info('Downloading %d remote files...', total_files)
    last_progress = 0
    file_count = 0
    fallback_repo_manager = _FallbackRepoManager()
    with _get_requests_session() as download_session:
        download_session.stream = False # To ensure connection to Google can be reused
        for file_path in file_iter:
            if total_files:
                file_count += 1
                current_progress = file_count * 100 // total_files // 5 * 5
                if current_progress != last_progress:
                    last_progress = current_progress
                    logger.info('%d%% downloaded', current_progress)
            else:
                current_progress = file_count // 20 * 20
                if current_progress != last_progress:
                    last_progress = current_progress
                    logger.info('%d files downloaded', current_progress)
            try:
                files[file_path] = _download_source_file(download_session, root_deps_tree,
                                                         fallback_repo_manager,
                                                         file_path).split('\n')
            except _NotInRepoError:
                get_logger().warning('Could not find "%s" remotely. Skipping...', file_path)
    return files


def _retrieve_local_files(file_iter, source_dir):
    """
    Retrieves all file paths in file_iter from the local source tree

    file_iter is an iterable of strings that are relative UNIX paths to
        files in the Chromium source.

    Returns a dict of relative UNIX path strings to a list of lines in the file as strings
    """
    files = {}
    for file_path in file_iter:
        try:
            raw_content = (source_dir / file_path).read_bytes()
        except FileNotFoundError:
            get_logger().warning('Missing file from patches: %s', file_path)
            continue
        for encoding in TREE_ENCODINGS:
            try:
                content = raw_content.decode(encoding)
                break
            except UnicodeDecodeError:
                continue
        if not content:
            raise UnicodeDecodeError(f'Unable to decode with any encoding: {file_path}')
        files[file_path] = content.split('\n')
    if not files:
        get_logger().error('All files used by patches are missing!')
    return files


def _modify_file_lines(patched_file, file_lines):
    """Helper for _apply_file_unidiff"""
    # Cursor for keeping track of the current line during hunk application
    # NOTE: The cursor is based on the line list index, not the line number!
    line_cursor = None
    for hunk in patched_file:
        # Validate hunk will match
        if not hunk.is_valid():
            raise _PatchValidationError(f'Hunk is not valid: {repr(hunk)}')
        line_cursor = hunk.target_start - 1
        for line in hunk:
            normalized_line = line.value.rstrip('\n')
            if line.is_added:
                file_lines[line_cursor:line_cursor] = (normalized_line, )
                line_cursor += 1
            elif line.is_removed:
                if normalized_line != file_lines[line_cursor]:
                    raise _PatchValidationError(f"Line '{file_lines[line_cursor]}' does not match "
                                                f"removal line '{normalized_line}' from patch")
                del file_lines[line_cursor]
            elif line.is_context:
                if not normalized_line and line_cursor == len(file_lines):
                    # We reached the end of the file
                    break
                if normalized_line != file_lines[line_cursor]:
                    raise _PatchValidationError(f"Line '{file_lines[line_cursor]}' does not match "
                                                f"context line '{normalized_line}' from patch")
                line_cursor += 1
            else:
                assert line.line_type in (LINE_TYPE_EMPTY, LINE_TYPE_NO_NEWLINE)


def _apply_file_unidiff(patched_file, files_under_test):
    """Applies the unidiff.PatchedFile to the source files under testing"""
    patched_file_path = Path(patched_file.path)
    if patched_file.is_added_file:
        if patched_file_path in files_under_test:
            assert files_under_test[patched_file_path] is None
        assert len(patched_file) == 1 # Should be only one hunk
        assert patched_file[0].removed == 0
        assert patched_file[0].target_start == 1
        files_under_test[patched_file_path] = [x.value.rstrip('\n') for x in patched_file[0]]
    elif patched_file.is_removed_file:
        # Remove lines to see if file to be removed matches patch
        _modify_file_lines(patched_file, files_under_test[patched_file_path])
        files_under_test[patched_file_path] = None
    else: # Patching an existing file
        assert patched_file.is_modified_file
        _modify_file_lines(patched_file, files_under_test[patched_file_path])


def _dry_check_patched_file(patched_file, orig_file_content):
    """Run "patch --dry-check" on a unidiff.PatchedFile for diagnostics"""
    with tempfile.TemporaryDirectory() as tmpdirname:
        tmp_dir = Path(tmpdirname)
        # Write file to patch
        patched_file_path = tmp_dir / patched_file.path
        patched_file_path.parent.mkdir(parents=True, exist_ok=True)
        patched_file_path.write_text(orig_file_content)
        # Write patch
        patch_path = tmp_dir / 'broken_file.patch'
        patch_path.write_text(str(patched_file))
        # Dry run
        _, dry_stdout, _ = dry_run_check(patch_path, tmp_dir)
        return dry_stdout


def _test_patches(series_iter, patch_cache, files_under_test):
    """
    Tests the patches specified in the iterable series_iter

    Returns a boolean indicating if any of the patches have failed
    """
    for patch_path_str in series_iter:
        for patched_file in patch_cache[patch_path_str]:
            orig_file_content = None
            if get_logger().isEnabledFor(logging.DEBUG):
                orig_file_content = files_under_test.get(Path(patched_file.path))
                if orig_file_content:
                    orig_file_content = ' '.join(orig_file_content)
            try:
                _apply_file_unidiff(patched_file, files_under_test)
            except _PatchValidationError as exc:
                get_logger().warning('Patch failed validation: %s', patch_path_str)
                get_logger().debug('Specifically, file "%s" failed validation: %s',
                                   patched_file.path, exc)
                if get_logger().isEnabledFor(logging.DEBUG):
                    # _PatchValidationError cannot be thrown when a file is added
                    assert patched_file.is_modified_file or patched_file.is_removed_file
                    assert orig_file_content is not None
                    get_logger().debug(
                        'Output of "patch --dry-run" for this patch on this file:\n%s',
                        _dry_check_patched_file(patched_file, orig_file_content))
                return True
            except: #pylint: disable=bare-except
                get_logger().warning('Patch failed validation: %s', patch_path_str)
                get_logger().debug('Specifically, file "%s" caused exception while applying:',
                                   patched_file.path,
                                   exc_info=True)
                return True
    return False


def _load_all_patches(series_iter, patches_dir):
    """
    Returns a tuple of the following:
    - boolean indicating success or failure of reading files
    - dict of relative UNIX path strings to unidiff.PatchSet
    """
    had_failure = False
    unidiff_dict = {}
    for relative_path in series_iter:
        if relative_path in unidiff_dict:
            continue
        unidiff_dict[relative_path] = unidiff.PatchSet.from_filename(str(patches_dir /
                                                                         relative_path),
                                                                     encoding=ENCODING)
        if not (patches_dir / relative_path).read_text(encoding=ENCODING).endswith('\n'):
            had_failure = True
            get_logger().warning('Patch file does not end with newline: %s',
                                 str(patches_dir / relative_path))
    return had_failure, unidiff_dict


def _get_required_files(patch_cache):
    """Returns an iterable of pathlib.Path files needed from the source tree for patching"""
    new_files = set() # Files introduced by patches
    file_set = set()
    for patch_set in patch_cache.values():
        for patched_file in patch_set:
            if patched_file.is_added_file:
                new_files.add(patched_file.path)
            elif patched_file.path not in new_files:
                file_set.add(Path(patched_file.path))
    return file_set


def _get_files_under_test(args, required_files, parser):
    """
    Helper for main to get files_under_test

    Exits the program if --cache-remote debugging option is used
    """
    if args.local:
        files_under_test = _retrieve_local_files(required_files, args.local)
    else: # --remote and --cache-remote
        files_under_test = _retrieve_remote_files(required_files)
        if args.cache_remote:
            for file_path, file_content in files_under_test.items():
                if not (args.cache_remote / file_path).parent.exists():
                    (args.cache_remote / file_path).parent.mkdir(parents=True)
                with (args.cache_remote / file_path).open('w', encoding=ENCODING) as cache_file:
                    cache_file.write('\n'.join(file_content))
            parser.exit()
    return files_under_test


def main():
    """CLI Entrypoint"""
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('-s',
                        '--series',
                        type=Path,
                        metavar='FILE',
                        default=str(Path('patches', 'series')),
                        help='The series file listing patches to apply. Default: %(default)s')
    parser.add_argument('-p',
                        '--patches',
                        type=Path,
                        metavar='DIRECTORY',
                        default='patches',
                        help='The patches directory to read from. Default: %(default)s')
    add_common_params(parser)

    file_source_group = parser.add_mutually_exclusive_group(required=True)
    file_source_group.add_argument(
        '-l',
        '--local',
        type=Path,
        metavar='DIRECTORY',
        help=
        'Use a local source tree. It must be UNMODIFIED, otherwise the results will not be valid.')
    file_source_group.add_argument(
        '-r',
        '--remote',
        action='store_true',
        help=('Download the required source tree files from Google. '
              'This feature requires the Python module "requests". If you do not want to '
              'install this, consider using --local instead.'))
    file_source_group.add_argument(
        '-c',
        '--cache-remote',
        type=Path,
        metavar='DIRECTORY',
        help='(For debugging) Store the required remote files in an empty local directory')
    args = parser.parse_args()
    if args.cache_remote and not args.cache_remote.exists():
        if args.cache_remote.parent.exists():
            args.cache_remote.mkdir()
        else:
            parser.error(f'Parent of cache path {args.cache_remote} does not exist')

    if not args.series.is_file():
        parser.error(f'--series path is not a file or not found: {args.series}')
    if not args.patches.is_dir():
        parser.error(f'--patches path is not a directory or not found: {args.patches}')

    series_iterable = tuple(parse_series(args.series))
    had_failure, patch_cache = _load_all_patches(series_iterable, args.patches)
    required_files = _get_required_files(patch_cache)
    files_under_test = _get_files_under_test(args, required_files, parser)
    had_failure |= _test_patches(series_iterable, patch_cache, files_under_test)
    if had_failure:
        get_logger().error('***FAILED VALIDATION; SEE ABOVE***')
        if not args.verbose:
            get_logger().info('(For more error details, re-run with the "-v" flag)')
        parser.exit(status=1)
    else:
        get_logger().info('Passed validation (%d patches total)', len(series_iterable))


if __name__ == '__main__':
    main()


================================================
FILE: docs/building.md
================================================
# Building ungoogled-chromium

The recommended way to build ungoogled-chromium is by consulting [the repository for your supported platform (links here)](platforms.md).

* *Linux users*: If your distribution is not listed, you will need to use Portable Linux.

If you want to add ungoogled-chromium to your existing Chromium build process, see the next section. Additionally, you may reference the repositories for supported platforms for inspiration.

## Integrating ungoogled-chromium into your Chromium build process

Typically, ungoogled-chromium is built from [code in platform-specific repositories](platforms.md). However, ungoogled-chromium can also be included in part or in whole into any custom Chromium build. In this section, **we will assume you already have a process to make your own Chromium builds**.

**NOTE**: You may need additional patches and/or build configuration for [your supported platform](platforms.md) because this repository does not contain all the code necessary for all platforms.

Before continuing, you may find it helpful to have a look through [the design documentation](design.md).

The following procedure outline the essential steps to build Chromium will all of ungoogled-chromium's features. **They are not sufficient to build ungoogled-chromium on their own**.

1. Ensure Chromium is downloaded, such as by `depot_tools`. In most of our supported platforms, we instead use a custom tool to do this.

```sh
mkdir -p build/download_cache
./utils/downloads.py retrieve -c build/download_cache -i downloads.ini
./utils/downloads.py unpack -c build/download_cache -i downloads.ini -- build/src
```

2. Prune binaries

```sh
./utils/prune_binaries.py build/src pruning.list
```

3. Apply patches

```sh
./utils/patches.py apply build/src patches
```

4. Substitute domains

```sh
./utils/domain_substitution.py apply -r domain_regex.list -f domain_substitution.list -c build/domsubcache.tar.gz build/src
```

5. Build GN. If you are using `depot_tools` to checkout Chromium or you already have a GN binary, you should skip this step.

```sh
mkdir -p build/src/out/Default
cd build/src
./tools/gn/bootstrap/bootstrap.py --skip-generate-buildfiles -j4 -o out/Default/
```

6. Invoke the build:

```
mkdir -p build/src/out/Default
# NOTE: flags.gn contains only a subset of what is needed to run the build.
cp flags.gn build/src/out/Default/args.gn
cd build/src
# If you have additional GN flags to add, make sure to add them now.
./out/Default/gn gen out/Default --fail-on-unused-args
ninja -C out/Default chrome chromedriver chrome_sandbox
```

## Building FAQ

### My build keeps crashing because I run out of RAM! How can I fix it?

Here are several ways to address this, in decreasing order of preference:

1. Decrease the number of parallel threads to Ninja (the `-j` flag)
2. Add swap space


================================================
FILE: docs/contributing.md
================================================
# Contributing

This document contains our criteria and guidelines for contributing to ungoogled-chromium.

If you have **small contributions that don't fit our criteria**, consider adding them to [ungoogled-software/contrib](https://github.com/ungoogled-software/contrib) or [our Wiki](https://github.com/ungoogled-software/ungoogled-chromium-wiki) instead.

If you are a developer of an **officially-supported platform**, be sure to check out the [Platform Repository Standards and Guidelines](repo_management.md).

List of contents:

* [How to help](#how-to-help)
* [Submitting changes](#submitting-changes)
* [Criteria for new features](#criteria-for-new-features)

### How to help

Generally, ungoogled-chromium needs contributors to help:

* Keep up-to-date with the latest stable Chromium, and any problematic changes in the new version that needs modification.
* Help with issues marked with the `help wanted` tag (usually either questions for other users, or request for help from other developers)
* Review Pull Requests from other contributors
* Implement feature requests ("enhancements" in the Issue Tracker), large or small.
* Implement issues marked with the `backlog` tag (that are closed).
	* If it requires new code, please read through the [Submitting changes](#submitting-changes) section below.

In addition, anyone is free to help others in need of support in the Issue Tracker.

If there are fixes, tweaks, or additions you want to make, continue onto the following section.

### Submitting changes

Please submit all changes via Pull Requests.

Guidelines:

* You are welcome to submit minor changes, such as bug fixes, documentation fixes, and tweaks.
* If your change has an associated issue, please let others know that you are working on it.
* If you want to submit a new feature, please read through the [Criteria for new features](#criteria-for-new-features) below.
* When in doubt about the acceptance of a change, you are welcome to ask via an issue first.

### Criteria for new features

1. New features should not detract from the default Chromium experience, unless it falls under the project's main objectives (i.e. removing Google integration and enhancing privacy).

    * For larger features, please propose them via an issue first.

2. New features should live behind a setting that is **off by default**.

    * Settings are usually added via a command-line flag and `chrome://flags` entries. See [the relevant section in docs/developing.md](developing.md#adding-command-line-flags-and-chromeflags-options) for more information.
    * Unless there are significant benefits, adding the setting to `chrome://settings` is *not recommended* due to the additional maintenance required (caused by the infrastructure that backs preferences).

**NOTE**: In the event that the codebase changes significantly for a non-essential patch (i.e. a patch that does not contribute to the main objectives of ungoogled-chromium), it will be removed until someone updates it.


================================================
FILE: docs/default_settings.md
================================================
# Default Settings

Vanilla Chromium settings are not often changed by ungoogled-chromium, however there are some exceptions.  
Below is a list of the documented changes to the default Chromium settings.

Setting | New State | Location
-- | -- | --
Allow sites to check if you have payment methods saved | Disabled | chrome://settings/payments
Ask where to save each file before downloading | Enabled | chrome://settings/downloads
Auto sign-in | Disabled | chrome://settings/passwords
Block third-party cookies | Enabled | chrome://settings/cookies
Continue running background apps when Chromium is closed | Disabled | chrome://settings/system
Hyperlink auditing (\<a ping>) | Disabled | NA
Link Doctor | Disabled | NA
Offer to save passwords | Disabled | chrome://settings/passwords
Payment autofill | Disabled | chrome://settings/payments
Preload pages | Disabled | chrome://settings/performance
Search suggestions | Disabled | chrome://settings/syncSetup
Show bookmark bar | Enabled | chrome://settings/appearance
Third-party sign-in | Disabled | `--enable-features=FedCm`
WebRTC IP handling policy | Disable Non-Proxied UDP | `--webrtc-ip-handling-policy`


================================================
FILE: docs/design.md
================================================
# Design

This document contains a high-level technical description of ungoogled-chromium and its components.

## Overview

ungoogled-chromium consists of the following major components:

* [Configuration](#configuration)
    * [Configuration files](#configuration-files)
    * [Source file processors](#source-file-processors)
    * [Patches](#patches)
* [Packaging](#packaging)

The following sections describe each component.

## Configuration

Configuration is a broad term that refers to patches, build flags, and metadata about Chromium source code. It consists of the following components:

* [Configuration files](#configuration-files)
* [Source file processors](#source-file-processors)
* [Patches](#patches)

The following sections describe each component in more depth.

### Configuration Files

Configuration files (or config files) are files that store build configuration and source code changes for a build.

**IMPORTANT**: For consistency, all config files must be encoded in UTF-8.

List of configuration files:

* `chromium_version.txt` - The Chromium version used by ungoogled-chromium
* `revision.txt` - The revision of the changes on top of the given Chromium version.
* `pruning.list` - [See the Source File Processors section](#source-file-processors)
* `domain_regex.list` - [See the Source File Processors section](#source-file-processors)
* `domain_substitution.list` - [See the Source File Processors section](#source-file-processors)
* `downloads.ini` - Archives to download and unpack into the buildspace tree. This includes code not bundled in the Chromium source code archive that is specific to a non-Linux platform. On platforms such as macOS, this also includes a pre-built LLVM toolchain for convenience (which can be removed and built from source if desired).
* `flags.gn` - GN arguments to set before building.

### Source File Processors

Source file processors are utilities that directly manipulate the Chromium source tree before building. Currently, there are two such utilities: binary pruning, and domain substitution.

**Binary Pruning**: Strips binaries from the source code. This includes pre-built executables, shared libraries, and other forms of machine code. Most are substituted with system or user-provided equivalents, or are built from source; those binaries that cannot be removed do not contain machine code.

The list of files to remove are determined by the config file `pruning.list`. This config file is generated by `devutils/update_lists.py`.

**Domain Substitution**: Replaces Google and several other web domain names in the Chromium source code with non-existent alternatives ending in `qjz9zk`. These changes are mainly used as a backup measure to detect potentially unpatched requests to Google. Note that domain substitution is a crude process, and *may not be easily undone*.

With a few patches from ungoogled-chromium, any requests with these domain names sent via `net::URLRequest` in the Chromium code are blocked and notify the user via a info bar.

Similar to binary pruning, the list of files to modify are listed in `domain_substitution.list`; it is also updated with `devutils/update_lists.py`.

The regular expressions to use are listed in `domain_regex.list`; the search and replacement expressions are delimited with a pound (`#`) symbol. The restrictions for the entries are as follows:
* All replacement expressions must end in the TLD `qjz9zk`.
* The search and replacement expressions must have a one-to-one correspondence: no two search expressions can match the same string, and no two replacement expressions can result in the same string.

### Patches

All of ungoogled-chromium's patches for the Chromium source code are located in `patches/`. This directory conforms to the default GNU Quilt format. That is:

* All patches must reside inside `patches/`
* There is a `patches/series` text file that defines the order to apply all the patches. These patches are listed as a relative path from the `patches` directory.
    * Lines starting with the pound symbol (`#`) are ignored
    * For lines with patch paths: If there is a space followed by a pound symbol, the text after the patch path will be ignored.

All patch files in ungoogled-chromium must satisfy these formatting requirements:

* Patch filenames must end with the extension `.patch`
* The content must be in [unified format](https://en.wikipedia.org/wiki/Diff_utility#Unified_format).
* All paths in the hunk headers must begin after the first slash (which corresponds to the argument `-p1` for GNU patch).
* All patches must apply cleanly (i.e. no fuzz).
* It is recommended that hunk paths have the `a/` and `b/` prefixes, and a context of 3 (like the git default).
* All patches must be encoded in UTF-8 (i.e. same encoding as config files).

Patches are categorized into two directories directly under `patches/`:

1. **core**: Changes regarding background requests, code specific to Google web services, or code using pre-made binaries. They must be kept up-to-date with all of the changes in Chromium.
2. **extra**: Changes to features regarding control and transparency. They are not guaranteed to persist across updates to Chromium.

Within each category, patches are grouped by the following:

* `debian/` - Patches from Debian's Chromium
    * Patches are not modified unless they conflict with Inox's patches
    * These patches are not Debian-specific. For those, see the `debian/patches` directory
* `inox-patchset/` - Contains a modified subset of patches from Inox patchset.
    * Some patches such as those that change branding are omitted
    * Patches are not modified unless they do not apply cleanly onto the version of Chromium being built
    * Patches are from [inox-patchset's GitHub](https://github.com/gcarq/inox-patchset)
    * [Inox patchset's license](https://github.com/gcarq/inox-patchset/blob/master/LICENSE)
* `bromite/` - Patches from [Bromite](https://github.com/bromite/bromite)
* `iridium-browser/` - Contains a modified subset of patches from Iridium Browser.
    * Some patches such as those that change branding or URLs to point to Iridium's own servers are omitted
    * Patches are not modified unless they conflict with Debian's or Inox's patches
    * Patches are from the `patchview` branch of Iridium's Git repository. [Git webview of the patchview branch](https://git.iridiumbrowser.de/cgit.cgi/iridium-browser/?h=patchview)
* `opensuse/` - Patches from openSUSE's Chromium
* `ubuntu/` -  Patches from Ubuntu's Chromium
* `ungoogled-chromium/` - Patches by ungoogled-chromium developers

## Packaging

Packaging is the process of downloading, building, and producing a distributable package of ungoogled-chromium.

Packaging files use the code from this repository to build ungoogled-chromium. Each platform and configuration has an associated packaging repository under the [ungoogled-software](https://github.com/ungoogled-software) organization. For more information about each packaging repository, see the [building documentation](building.md).

Packaging generally consists of the major steps:

1. Download and unpack the source tree
2. Prune binaries
3. Apply patches
4. Substitute domains
5. Build GN via `tools/gn/bootstrap/bootstrap.py`
6. Run `gn gen` with the GN flags
7. Build Chromium via `ninja`
8. Create package(s) of build output (usually in `out/Default`)


================================================
FILE: docs/developing.md
================================================
# Development notes and procedures

This document contains an assortment of information for those who want to develop ungoogled-chromium.

Information targeted towards developers *and* other users live in [the Wiki](https://ungoogled-software.github.io/ungoogled-chromium-wiki/).

Contents:

* [Branches](#branches)
* [Adding command-line flags and chrome://flags options](#adding-command-line-flags-and-chromeflags-options)
* [Workflow of updating to a new Chromium version](#workflow-of-updating-to-a-new-chromium-version)

## Branches

Development is focused on `master`, and any changes in there should not break anything unless platforms break during a Chromium version rebase.

Larger feature changes or hotfixes must be done in a separate branch. Once they are ready, then a Pull Request can be made onto `master` (for contributors with write access, merging directly via a git client is fine). After the branch is merged, it should be removed.

## Adding command-line flags and `chrome://flags` options

See `docs/how_to_add_your_feature_flag.md` in the Chromium source tree for the steps needed. Note that updating `tools/metrics/histograms/enums.xml` is not required.

For new flags, first add a constant to `third_party/ungoogled/ungoogled_switches.cc` (by modifying patch `resources/patches/ungoogled-chromium/add-third-party-ungoogled.patch`). Then, use this constant in the steps outlined above.

## Workflow of updating to a new Chromium version

Tested on Debian 12 (bookworm). Exact instructions should work on any other Linux or macOS system with the proper dependencies.

To gain a deeper understanding of this updating process, have a read through [docs/design.md](design.md).

### Dependencies

* [`quilt`](http://savannah.nongnu.org/projects/quilt)
    * This is available in most (if not all) Linux distributions, and also Homebrew on macOS.
    * This utility facilitates most of the updating process, so it is important to learn how to use this. The manpage for quilt (as of early 2017) lacks an example of a workflow. There are multiple guides online, but [this guide from Debian](https://wiki.debian.org/UsingQuilt) and [the referenced guide on that page](https://raphaelhertzog.com/2012/08/08/how-to-use-quilt-to-manage-patches-in-debian-packages/) are the ones referenced in developing the current workflow.
* Python 3.10 or newer
    * `httplib2` and `six` are also required if you wish to utilize a source clone instead of the source tarball. `pysocks` is required for versions of `httplib2` v0.30.0 or later.

### Downloading the source code

#### Source tarball download (recommended):
```sh
mkdir -p build/download_cache
./utils/downloads.py retrieve -i downloads.ini -c build/download_cache
./utils/downloads.py unpack -i downloads.ini -c build/download_cache build/src
```

#### Source clone:
```sh
./utils/clone.py -o build/src
```

### Updating lists

The utility `devutils/update_lists.py` automates this process. By default, it will update the files in the local repo. Pass in `-h` or `--help` for available options.

```sh
./devutils/update_lists.py -t build/src
```

The resulting source tree in `build/src` *will not* have binaries pruned or domains substituted.

### Updating patches

**IMPORTANT**: Make sure domain substitution has not been applied before updating patches.

1. Run `source devutils/set_quilt_vars.sh` (or `source devutils/set_quilt_vars.fish` if you are using the fish shell)
    * This will setup quilt to modify patches directly in `patches/`
2. Go into the source tree: `cd build/src`
3. Use `quilt` to refresh all patches: `quilt push -a --refresh`
	* If an error occurs, go to the next step. Otherwise, skip to Step 5.
4. Use `quilt` to fix the broken patch:
    1. Run `quilt push -f`
    2. Edit the broken files as necessary by adding (`quilt edit ...` or `quilt add ...`) or removing (`quilt remove ...`) files as necessary
        * When removing large chunks of code, remove each line instead of using language features to hide or remove the code. This makes the patches less susceptible to breakages when using quilt's refresh command (e.g. quilt refresh updates the line numbers based on the patch context, so it's possible for new but desirable code in the middle of the block comment to be excluded.). It also helps with readability when someone wants to see the changes made based on the patch alone.
    3. Refresh the patch: `quilt refresh`
    4. Go back to Step 3.
5. Run `quilt pop -a`
6. Go back to ungoogled-chromium repo: `cd ../..`
7. Run `devutils/validate_config.py`. If any warnings are printed, address them; otherwise, continue to Step 8.
8. Run `devutils/validate_patches.py -l build/src`. If errors occur, go back to Step 3.

This should leave unstaged changes in the git repository to be reviewed, added, and committed.

### Steps for fixing patches after a failed build attempt

If domain substitution is not applied, then the steps from the previous section will work for revising patches.

If domain substitution is applied, then the steps for the initial update will not apply since that would create patches which depend on domain substitution. Here is a method of dealing with this:

1. Revert domain substitution: `./utils/domain_substitution.py revert -c CACHE_PATH_HERE build/src`
2. Follow the patch updating section above
3. Reapply domain substitution: `./utils/domain_substitution.py apply -r domain_regex.list -f domain_substitution.list -c CACHE_PATH_HERE build/src`
4. Reattempt build. Repeat steps as necessary.

### Next steps

* Submit a Pull Request of these changes to the ungoogled-chromium repo.
* Once the PR is merged, update the repositories of each platform repository that you are maintaining under the `ungoogled-software` organization.


================================================
FILE: docs/flags.md
================================================
# List of flags and switches

This is an exhaustive list of command-line switches and flags introduced by ungoogled-chromium.
Each switch has a corresponding entry on the `chrome://flags` page which can be filtered by searching for `ungoogled-chromium`.

If a switch requires a value, you must specify it with an `=` sign; e.g. flag `--foo` with value `bar` should be written as `--foo=bar`.

> **NOTE**: If you add a command-line argument that is also in `chrome://flags`, the flag's state will not be indicated in `chrome://flags`. There is no universal way to ensure command-line flags are taking effect, but you can find if they're being seen by checking `chrome://version`.

- ### Available on all platforms

  <code>Switch&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code> | Description
  -- | --
  `--disable-beforeunload` | Disables JavaScript dialog boxes triggered by `beforeunload`
  `--disable-grease-tls` | Disables GREASE for TLS. Combined with `--http-accept-header` allows browser to look more like a tor-browser. See https://github.com/ungoogled-software/ungoogled-chromium/issues/783 for more details.
  `--disable-search-engine-collection` | Disable automatic search engine scraping from webpages.
  `--extension-mime-request-handling` | Change how extension MIME types (CRX and user scripts) are handled. Acceptable values are `download-as-regular-file` or `always-prompt-for-install`. Leave unset to use normal behavior.
  `--fingerprinting-canvas-image-data-noise` | (Added flag to Bromite feature) Implements fingerprinting deception for Canvas image data retrieved via JS APIs. In the data, at most 10 pixels are slightly modified.
  `--fingerprinting-canvas-measuretext-noise` | (Added flag to Bromite feature) Scale the output values of Canvas::measureText() with a randomly selected factor in the range -0.0003% to 0.0003%, which are recomputed on every document initialization.
  `--fingerprinting-client-rects-noise` | (Added flag to Bromite feature) Implements fingerprinting deception of JS APIs `getClientRects()` and `getBoundingClientRect()` by scaling their output values with a random factor in the range -0.0003% to 0.0003%, which are recomputed for every document instantiation.
  `--force-punycode-hostnames` | Convert all Internationalized Domain Names to punycode (ASCII representation of Unicode). See https://github.com/ungoogled-software/ungoogled-chromium/issues/370 for more details.
  `--hide-crashed-bubble` | Hides the bubble box with the message "Restore Pages? Chromium didn't shut down correctly." that shows on startup after the browser did not exit cleanly.
  `--http-accept-header` | Changes the default value of the `Accept` HTTP header sent with HTTP requests. Combined with `--disable-grease-tls` allows browser to look more like a tor-browser. See https://github.com/ungoogled-software/ungoogled-chromium/issues/783 for more details.
  `--keep-old-history` | Disables deletion of local browser history after 90 days
  `--max-connections-per-host` | (from Bromite) Configure the maximum allowed connections per host. Valid values are `6` and `15`
  `--omnibox-autocomplete-filtering` | Restrict omnibox autocomplete results to a combination of search suggestions (if enabled), bookmarks, and internal chrome pages. Accepts `search`, `search-bookmarks`, `search-chrome`, and `search-bookmarks-chrome`.
  `--popups-to-tabs` | Makes popups open in new tabs.

- ### Available only on desktop

  <code>Switch&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code> | Description
  -- | --
  `--bookmark-bar-ntp` | Sets the visibility of the bookmark bar on the New Tab Page. Only takes the value `never`.
  `--close-confirmation` | Show a warning prompt when closing the browser window. Accepts `last` (prompt when closing the last window with several tabs) and `multiple` (prompt only if more than one window is open). 
  `--close-window-with-last-tab` | Determines whether a window should close once the last tab is closed. Only takes the value `never`.
  `--custom-ntp` | Allows setting a custom URL for the new tab page. Value can be internal (e.g. `about:blank` or `chrome://new-tab-page`), external (e.g. `example.com`), or local (e.g. `file:///tmp/startpage.html`). This applies for incognito windows as well when not set to a `chrome://` internal page.
  `--disable-sharing-hub` | Disables the sharing hub button.
  `--enable-incognito-themes` | Allows themes to change the appearance of Incognito windows.
  `--hide-extensions-menu` | Hides the extensions container. This includes the puzzle piece icon as well as any pinned extensions.
  `--hide-fullscreen-exit-ui` | Hides the "X" that appears when the mouse cursor is moved towards the top of the window in fullscreen mode. Additionally, this hides the "Press F11 to exit full screen" popup.
  `--hide-tab-close-buttons` | Hides the close buttons on tabs.
  `--remove-grab-handle` | Removes the reserved empty space in the tabstrip for moving the window.
  `--remove-tabsearch-button` | Removes the tabsearch button from the tabstrip.
  `--scroll-tabs` | Determines if scrolling will cause a switch to a neighboring tab if the cursor hovers over the tabs, or the empty space beside the tabs. The flag requires one the values: `always`, `never`, `incognito-and-guest`. When omitted, the default is to use platform-specific behavior, which is currently enabled only on desktop Linux.
  `--show-avatar-button` | Sets visibility of the avatar button. The flag requires one of the values: `always`, `incognito-and-guest` (only show Incognito or Guest modes), or `never`.
  `--tab-hover-cards` | Allows removing the tab hover cards or using a tooltip as a replacement. This can be set with the values `none` or `tooltip`.

  - #### Available only on Windows

    <code>Switch&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code> | Description
    -- | --
    `--disable-encryption` | Disable encryption of cookies, passwords, and settings which uses a generated machine-specific encryption key. This is used to enable portable user data directories.
    `--disable-machine-id` | Disables use of a generated machine-specific ID to lock the user data directory to that machine. This is used to enable portable user data directories.


## Feature flags

Feature flags are similar to switches with the difference being that they are passed as values for the `--enable-features` switch. Multiple features can be passed at the same time by separating them with a comma, e.g. `--enable-features=flag1,flag2,flag3`.
These are also available on the `chrome://flags` page.

- ### Available on all platforms

  Feature | Description
  -- | --
  `MinimalReferrers` | Removes all cross-origin referrers and strips same-origin referrers down to the origin. Has lower precedence than `NoCrossOriginReferrers`.
  `NoCrossOriginReferrers` | Removes all cross-origin referrers. Has lower precedence than `NoReferrers`.
  `NoReferrers` | Removes all referrers.
  `ReducedSystemInfo` | Reduces the amount of system information obatainable through headers and javascript, also causes hardwareConcurrency to respond with two cores.
  `RemoveClientHints` | Removes client hints (information sent to servers about your system, similar to a user agent).
  `SetIpv6ProbeFalse` | Forces the result of the browser's IPv6 probing (i.e. IPv6 connectivity test) to be unsuccessful. This causes IPv4 addresses to be prioritized over IPv6 addresses. Without this flag, the probing result is set to be successful, which causes IPv6 to be used over IPv4 when possible.
  `SpoofWebGLInfo` | Return generic values for WebGLDebugRendererInfo to remove a potential data leak while preventing potential website breakage.
  `IncreaseIncognitoStorageQuota` | Makes Incognito and Guest profiles compute the storage quota with the same algorithm that regular profiles use. This makes it harder for websites to detect Incognito mode, but may allow sites to induce heavy memory pressure.

- ### Available only on desktop

  Feature | Description
  -- | --
  `ClearDataOnExit` | Clears all browsing data on exit.
  `DisableLinkDrag` | Prevents dragging of links and selected text. Allows selecting text from a middle of a link. Also allows starting selection without first clearing the existing one. This behaviour is similar to the one from older Opera. See https://github.com/ungoogled-software/ungoogled-chromium/pull/2080 and https://github.com/ungoogled-software/ungoogled-chromium/discussions/2055 for more information.
  `DisableQRGenerator` | Disables the QR generator for sharing page links.


## Flags for existing switches

Chromium contains switches that do no have corresponding entries in `chrome://flags`. For convenience, ungoogled-chromium has created entries for some of the commonly-used switches.

- ### Available on all platforms
  <code>Switch&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code> | Description
  -- | --
  `--disable-top-sites` | Disables the top sites and most visited entries on the new tab page.
  `--disable-webgl` | Disable all versions of WebGL.
  `--enable-low-end-device-mode` | Force low-end device mode when set.
  `--force-dark-mode` | Forces dark mode in UI for platforms that support it.
  `--no-default-browser-check` | Disables the default browser check.
  `--no-pings` | Don't send hyperlink auditing pings.
  `--webrtc-ip-handling-policy` | Restrict which IP addresses and interfaces WebRTC uses.
- ### Available only on desktop
  <code>Switch&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code> | Description
  -- | --
  `--incognito` | Start in Incognito.
  `--start-maximized` | Starts the browser maximized, regardless of any previous settings.


================================================
FILE: docs/platforms.md
================================================
# Supported Platforms

This page lists platforms officially supported by ungoogled-chromium, and their associated repositories.

* Android: [ungoogled-chromium-android](https://github.com/ungoogled-software/ungoogled-chromium-android)
* Arch Linux: [ungoogled-chromium-archlinux](https://github.com/ungoogled-software/ungoogled-chromium-archlinux)
* Debian, Ubuntu, and derivatives: [ungoogled-chromium-debian](https://github.com/ungoogled-software/ungoogled-chromium-debian)
* Fedora and CentOS: [ungoogled-chromium-fedora](https://github.com/ungoogled-software/ungoogled-chromium-fedora)
* Portable Linux (for any Linux distro): [ungoogled-chromium-portablelinux](https://github.com/ungoogled-software/ungoogled-chromium-portablelinux)
* Windows: [ungoogled-chromium-windows](https://github.com/ungoogled-software/ungoogled-chromium-windows)
* macOS: [ungoogled-chromium-macos](https://github.com/ungoogled-software/ungoogled-chromium-macos)


================================================
FILE: docs/repo_management.md
================================================
# Platform Repository Standards and Guidelines

*This document is new, and its structure and content may change. If you have suggestions, please create an issue!*

ungoogled-chromium is comprised of anonymous developers who volunteer their efforts. Some of these developers may choose to provide long-term support for [an officially-supported platform](platforms.md), or bring support to a new platform. For such developers, this document consists of standards and management guidelines for platform repos.

We will refer to this git repository as "the main repo", and refer to repositories that add platform-specific code to build ungoogled-chromium as "platform repos". An "officially-supported platform" is a platform with a platform repo in [the ungoogled-software organization](https://github.com/ungoogled-software) and noted in [docs/platforms.md](platforms.md).

## Standards

An officially-supported platform repo:

* Must not modify or remove existing patches, GN flags, domain substitution, or binary pruning in the main repo. Instead, you can add new patches or add more files/rules to domain substitution or binary pruning. (If you think a change is needed in the main repo, please make an issue!)
* Must have a tagging/versioning scheme that includes the ungoogled-chromium version.
* Must not require an Internet connection during compilation (before compilation is OK).
* Should allow the user to download all build requirements before building.
* Must not require external services to build, aside from repos in the ungoogled-software organization and repos provided by or used by Chromium.
* Should have a reproducible build for all versions (currently, there is no formal process to enforce/verify reproducibility of binaries)

Each deviation must be clearly noted in the platform repo's documentation (such as the repo's README), and have an associated issue in the platform repo. 

## Teams in the ungoogled-software organization

Each officially-supported platform has one or more teams in the ungoogled-software organization. These teams provide additional means for collaborating with other developers, such as issue triaging and private discussions (see section "How to communicate" below).

If you are a regular contributor and would like to provide long-term support for a platform, you can request to be included in the ungoogled-software organization team for your platform. Since the number of developers is low, there is no formal process to do this; just ask in an issue.

## How to communicate

In the interest of transparency, it is recommended to discuss work in public spaces like issues or PRs. If a discussion should not involve outsiders, you can lock the issue or PR to collaborators only.

You must use team discussions if you are discussing or sharing information that can affect the security of the repository. Otherwise, you may use team discussions at your discretion.

## Issues

Each platform repo should have a team in ungoogled-software with the Triage permission level. All members should feel free to manage issues.

TODO: More details?

## Pull Requests

TODO

## Repository Settings and Shared Resources

Shared resources includes:

* CI services like CirrusCI, GitHub Actions, etc.
* Build services like OpenSUSE Build Service (OBS)

These need to be handled with care, as they can cause a wide variety of issues from security and privacy leaks all the way to data loss.

There are several ways to handle shared resources:

* Assign one person to manage a certain set of settings (i.e. grant them "ownership" of those settings). If you want to change a setting, you should request a change in a team discussion.
* TODO: More ways to manage settings?


================================================
FILE: domain_regex.list
================================================
fonts(\\*?)\.googleapis(\\*?)\.com#f0ntz\g<1>.9oo91e8p1\g<2>.qjz9zk
google([A-Za-z\-]*?\\*?)\.com(?!mon)#9oo91e\g<1>.qjz9zk
gstatic([A-Za-z\-]*?\\*?)\.com#95tat1c\g<1>.qjz9zk
chrome([A-Za-z\-]*?\\*?)\.com(?!ponent)#ch40me\g<1>.qjz9zk
chromium([A-Za-z\-]*?\\*?)\.org#ch40m1um\g<1>.qjz9zk
mozilla([A-Za-z\-]*?\\*?)\.org#m0z111a\g<1>.qjz9zk
facebook([A-Za-z\-]*?\\*?)\.com#f8c3b00k\g<1>.qjz9zk
appspot([A-Za-z\-]*?\\*?)\.com#8pp2p8t\g<1>.qjz9zk
youtube([A-Za-z\-]*?\\*?)\.com#y0u1ub3\g<1>.qjz9zk
ytimg([A-Za-z\-]*?\\*?)\.com#yt1mg\g<1>.qjz9zk
gmail([A-Za-z\-]*?\\*?)\.com#9ma1l\g<1>.qjz9zk
doubleclick([A-Za-z\-]*?\\*?)\.net#60u613cl1c4\g<1>.n3t.qjz9zk
doubleclick([A-Za-z\-]*?\\*?)\.com#60u613cl1c4\g<1>.c0m.qjz9zk
googlezip(\\*?)\.net#9oo91e21p\g<1>.qjz9zk
beacons([1-9]?\\*?)\.gvt([1-9]?\\*?)\.com#b3ac0n2\g<1>.9vt\g<2>.qjz9zk
ggpht(\\*?)\.com#99pht\g<1>.qjz9zk
microsoft(\\*?)\.com#m1cr050ft\g<1>.qjz9zk
1e100(\\*?)\.net#l3lOO\g<1>.qjz9zk
(?<!http://schemas.)android(\\*?)\.com#8n6r01d\g<1>.qjz9zk
goo\.gl(e?)#goo.gl\g<1>.qjz9zk
privacysandbox([A-Za-z\-]*?\\*?)\.com#pr1v4cy54ndb0x\g<1>.qjz9zk


================================================
FILE: domain_substitution.list
================================================
.gemini/commands/PRESUBMIT.py
BUILD.gn
PRESUBMIT.py
PRESUBMIT_test.py
agents/PRESUBMIT.py
agents/extensions/PRESUBMIT.py
agents/extensions/perf/gemini-extension.json
agents/prompts/PRESUBMIT.py
agents/testing/PRESUBMIT.py
agents/testing/skia_perf.py
agents/testing/skia_perf_unittest.py
ash/accelerators/accelerator_notifications.cc
ash/accelerators/ash_accelerator_configuration_unittest.cc
ash/ambient/ambient_controller.cc
ash/ambient/ambient_controller_unittest.cc
ash/ambient/backdrop/ambient_backend_controller_impl.cc
ash/ambient/managed/screensaver_image_downloader.cc
ash/ambient/metrics/ambient_metrics.cc
ash/api/tasks/fake_tasks_client.cc
ash/api/tasks/tasks_types.h
ash/app_list/PRESUBMIT.py
ash/app_list/app_list_controller_impl_unittest.cc
ash/app_list/app_list_feature_usage_metrics_unittest.cc
ash/app_list/views/app_list_nudge_controller_unittest.cc
ash/app_list/views/app_list_view_pixeltest.cc
ash/ash_strings.grd
ash/auth/views/auth_header_view_pixeltest.cc
ash/birch/birch_item.cc
ash/birch/birch_item_unittest.cc
ash/birch/birch_model_unittest.cc
ash/capture_mode/capture_mode_camera_unittest.cc
ash/capture_mode/capture_mode_controller.cc
ash/capture_mode/sunfish_unittest.cc
ash/capture_mode/test_capture_mode_delegate.cc
ash/child_accounts/parent_access_controller_impl_unittest.cc
ash/clipboard/clipboard_history_controller_unittest.cc
ash/clipboard/clipboard_history_menu_model_adapter_unittest.cc
ash/constants/ash_features.cc
ash/constants/ash_switches.cc
ash/constants/url_constants.cc
ash/constants/url_constants.h
ash/constants/web_app_id_constants.h
ash/controls/contextual_nudge.cc
ash/fast_ink/laser/laser_pointer_view.cc
ash/game_dashboard/game_dashboard_main_menu_view.cc
ash/glanceables/classroom/fake_glanceables_classroom_client.cc
ash/glanceables/classroom/glanceables_classroom_item_view_unittest.cc
ash/glanceables/classroom/glanceables_classroom_student_view.cc
ash/glanceables/classroom/glanceables_classroom_student_view_unittest.cc
ash/glanceables/classroom/glanceables_classroom_types.h
ash/glanceables/glanceables_pixeltest.cc
ash/glanceables/glanceables_unittest.cc
ash/glanceables/tasks/glanceables_task_view.cc
ash/glanceables/tasks/glanceables_tasks_view.cc
ash/glanceables/tasks/glanceables_tasks_view_unittest.cc
ash/glanceables/tasks/test/glanceables_tasks_test_util.cc
ash/login/ui/auth_icon_view.cc
ash/login/ui/login_remove_account_dialog_unittest.cc
ash/metrics/demo_session_metrics_recorder_unittest.cc
ash/projector/projector_metadata_model.cc
ash/public/cpp/system_notification_builder_unittest.cc
ash/quick_insert/model/quick_insert_link_suggester.cc
ash/quick_insert/model/quick_insert_link_suggester_unittest.cc
ash/quick_insert/quick_insert_asset_fetcher_impl.cc
ash/quick_insert/quick_insert_controller.cc
ash/quick_insert/quick_insert_controller_unittest.cc
ash/quick_insert/quick_insert_insert_media.cc
ash/quick_insert/search/quick_insert_search_aggregator_unittest.cc
ash/quick_insert/search/quick_insert_search_controller.cc
ash/quick_insert/search/quick_insert_search_controller_unittest.cc
ash/quick_insert/search/quick_insert_search_request.cc
ash/quick_insert/search/quick_insert_search_request_unittest.cc
ash/quick_pair/companion_app/companion_app_broker_impl_unittest.cc
ash/quick_pair/fast_pair_handshake/fast_pair_data_encryptor.h
ash/quick_pair/fast_pair_handshake/fast_pair_data_encryptor_impl_unittest.cc
ash/quick_pair/fast_pair_handshake/fast_pair_encryption.h
ash/quick_pair/fast_pair_handshake/fast_pair_encryption_unittest.cc
ash/quick_pair/fast_pair_handshake/fast_pair_gatt_service_client_impl.cc
ash/quick_pair/fast_pair_handshake/fast_pair_handshake.h
ash/quick_pair/keyed_service/quick_pair_mediator.cc
ash/quick_pair/message_stream/message_stream.h
ash/quick_pair/pairing/retroactive_pairing_detector_impl.cc
ash/quick_pair/repository/fake_device_metadata_http_fetcher.cc
ash/quick_pair/repository/fast_pair/device_metadata_fetcher.cc
ash/quick_pair/repository/fast_pair/device_metadata_fetcher_unittest.cc
ash/quick_pair/repository/fast_pair/footprints_fetcher_impl.cc
ash/quick_pair/scanning/fast_pair/fast_pair_discoverable_scanner.h
ash/quick_pair/scanning/fast_pair/fast_pair_not_discoverable_scanner.h
ash/quick_pair/scanning/fast_pair/fast_pair_not_discoverable_scanner_impl.h
ash/quick_pair/ui/fast_pair/fast_pair_notification_controller_unittest.cc
ash/quick_pair/ui/fast_pair/fast_pair_presenter_impl.cc
ash/resources/PRESUBMIT.py
ash/scanner/scanner_action_handler.cc
ash/scanner/scanner_action_handler_unittest.cc
ash/scanner/scanner_controller_unittest.cc
ash/session/fullscreen_controller_unittest.cc
ash/shelf/home_to_overview_nudge_controller_unittest.cc
ash/shelf/launcher_nudge_controller_unittest.cc
ash/shelf/scrollable_shelf_view_pixeltest.cc
ash/shelf/shelf_controller_unittest.cc
ash/shelf/shelf_layout_manager_unittest.cc
ash/style/color_palette_controller_unittest.cc
ash/system/audio/audio_effects_controller_unittest.cc
ash/system/camera/camera_effects_controller_unittest.cc
ash/system/diagnostics/diagnostics_log_controller_unittest.cc
ash/system/eche/eche_tray.cc
ash/system/eche/eche_tray_unittest.cc
ash/system/focus_mode/focus_mode_controller.cc
ash/system/focus_mode/focus_mode_countdown_view_unittest.cc
ash/system/focus_mode/focus_mode_feature_pod_controller_unittest.cc
ash/system/focus_mode/focus_mode_util.cc
ash/system/focus_mode/focus_mode_util_unittest.cc
ash/system/focus_mode/sounds/focus_mode_sounds_controller.cc
ash/system/focus_mode/sounds/soundscape/soundscapes_downloader.cc
ash/system/focus_mode/sounds/youtube_music/youtube_music_client.cc
ash/system/focus_mode/sounds/youtube_music/youtube_music_types.h
ash/system/human_presence/snooping_protection_controller_unittest.cc
ash/system/input_device_settings/device_image_downloader.cc
ash/system/input_device_settings/device_image_downloader_unittest.cc
ash/system/input_device_settings/input_device_settings_metrics_manager_unittest.cc
ash/system/input_device_settings/input_device_settings_notification_controller.cc
ash/system/input_device_settings/input_device_settings_notification_controller_unittest.cc
ash/system/mahi/mahi_content_source_button_unittest.cc
ash/system/mahi/mahi_panel_view_unittest.cc
ash/system/notification_center/message_center_test_util.cc
ash/system/notification_center/notification_grouping_controller_unittest.cc
ash/system/notification_center/views/ash_notification_view_unittest.cc
ash/system/pcie_peripheral/pcie_peripheral_notification_controller.cc
ash/system/pcie_peripheral/pcie_peripheral_notification_controller_unittest.cc
ash/system/phonehub/phone_hub_tray_unittest.cc
ash/system/phonehub/phone_hub_ui_controller_unittest.cc
ash/system/power/power_event_observer_unittest.cc
ash/system/privacy_hub/privacy_hub_notification_controller.cc
ash/system/status_area_widget_unittest.cc
ash/system/time/calendar_event_fetch_unittest.cc
ash/system/time/calendar_event_list_item_view_unittest.cc
ash/system/time/calendar_list_model_unittest.cc
ash/system/time/calendar_up_next_pixeltest.cc
ash/system/time/calendar_up_next_view_unittest.cc
ash/system/time/calendar_view_pixeltest.cc
ash/system/unified/date_tray_unittest.cc
ash/system/unified/notification_icons_controller_unittest.cc
ash/system/unified/power_button_unittest.cc
ash/system/unified/quick_settings_footer_pixeltest.cc
ash/system/unified/quick_settings_header_unittest.cc
ash/system/unified/user_chooser_detailed_view_controller_unittest.cc
ash/system/usb_peripheral/usb_peripheral_notification_controller.cc
ash/system/video_conference/bubble/bubble_view_pixeltest.cc
ash/system/video_conference/bubble/return_to_app_panel_unittest.cc
ash/system/video_conference/fake_video_conference_tray_controller.cc
ash/system/video_conference/video_conference_tray_unittest.cc
ash/wallpaper/test_wallpaper_controller_client.cc
ash/wallpaper/wallpaper_image_downloader.cc
ash/webui/boca_ui/boca_app_page_handler_unittest.cc
ash/webui/boca_ui/provider/classroom_page_handler_impl.cc
ash/webui/boca_ui/webview_auth_handler_unittest.cc
ash/webui/camera_app_ui/camera_app_untrusted_ui.cc
ash/webui/camera_app_ui/resources/js/externs/types.d.ts
ash/webui/camera_app_ui/resources/js/geometry.ts
ash/webui/camera_app_ui/resources/js/i18n_string.ts
ash/webui/camera_app_ui/resources/js/metrics.ts
ash/webui/camera_app_ui/resources/js/untrusted_ga_helper.ts
ash/webui/camera_app_ui/resources/js/views/camera_intent.ts
ash/webui/camera_app_ui/resources/js/views/document_fix_mode.ts
ash/webui/camera_app_ui/resources/js/views/settings/primary.ts
ash/webui/camera_app_ui/resources/strings/camera_strings.grd
ash/webui/camera_app_ui/resources/utils/cca/commands/check_color_tokens.py
ash/webui/camera_app_ui/resources/utils/cca/commands/upload.py
ash/webui/common/resources/PRESUBMIT.py
ash/webui/common/resources/cr_picture/png.js
ash/webui/common/resources/keyboard_layouts.js
ash/webui/common/resources/quick_unlock/pin_keyboard_icons.html
ash/webui/common/resources/sea_pen/constants.ts
ash/webui/common/resources/sea_pen/constants_generated.ts
ash/webui/common/resources/sea_pen/surface_effects/sparkle.ts
ash/webui/diagnostics_ui/backend/input/input_data_provider_keyboard_unittest.cc
ash/webui/diagnostics_ui/diagnostics_ui.cc
ash/webui/diagnostics_ui/resources/PRESUBMIT.py
ash/webui/diagnostics_ui/resources/network_card.ts
ash/webui/diagnostics_ui/resources/routine_section.ts
ash/webui/diagnostics_ui/resources/touchpad_tester.ts
ash/webui/eche_app_ui/eche_alert_generator.cc
ash/webui/eche_app_ui/eche_tray_stream_status_observer_unittest.cc
ash/webui/firmware_update_ui/resources/PRESUBMIT.py
ash/webui/graduation/graduation_ui_handler_unittest.cc
ash/webui/graduation/resources/js/graduation_takeout_ui.ts
ash/webui/graduation/url_constants.cc
ash/webui/graduation/webview_auth_handler_unittest.cc
ash/webui/help_app_ui/help_app_kids_magazine_untrusted_ui.cc
ash/webui/help_app_ui/resources/js/help_app.d.ts
ash/webui/media_app_ui/media_app_guest_ui.cc
ash/webui/media_app_ui/media_app_page_handler.cc
ash/webui/media_app_ui/resources/js/launch.ts
ash/webui/media_app_ui/resources/js/media_app.d.ts
ash/webui/os_feedback_ui/backend/feedback_service_provider_unittest.cc
ash/webui/os_feedback_ui/backend/help_content_provider.cc
ash/webui/os_feedback_ui/backend/help_content_provider_unittest.cc
ash/webui/os_feedback_ui/resources/confirmation_page.ts
ash/webui/os_feedback_ui/resources/fake_data.ts
ash/webui/os_feedback_ui/resources/feedback_constants.ts
ash/webui/os_feedback_ui/resources/search_page.ts
ash/webui/personalization_app/personalization_app_ui.cc
ash/webui/personalization_app/personalization_app_url_constants.cc
ash/webui/personalization_app/resources/PRESUBMIT.py
ash/webui/personalization_app/resources/js/ambient/ambient_preview_large_element.html
ash/webui/personalization_app/resources/js/wallpaper/google_photos_zero_state_element.ts
ash/webui/print_management/resources/PRESUBMIT.py
ash/webui/projector_app/projector_xhr_sender.cc
ash/webui/projector_app/projector_xhr_sender.h
ash/webui/projector_app/test/mock_app_client.cc
ash/webui/projector_app/test/projector_oauth_token_fetcher_unittest.cc
ash/webui/projector_app/test/projector_xhr_sender_unittest.cc
ash/webui/projector_app/test/untrusted_projector_page_handler_impl_unittest.cc
ash/webui/projector_app/untrusted_projector_ui.cc
ash/webui/recorder_app_ui/resources/components/unescapable-dialog.ts
ash/webui/recorder_app_ui/resources/core/externs.d.ts
ash/webui/recorder_app_ui/resources/core/platform_handler.ts
ash/webui/recorder_app_ui/resources/core/recording_session.ts
ash/webui/recorder_app_ui/resources/core/url_constants.ts
ash/webui/recorder_app_ui/resources/pages/record-page.ts
ash/webui/recorder_app_ui/resources/platforms/dev/handler.ts
ash/webui/recorder_app_ui/resources/static/audio_worklet.js
ash/webui/recorder_app_ui/resources/static/style.css
ash/webui/scanning/resources/PRESUBMIT.py
ash/webui/scanning/resources/scanning_app.html
ash/webui/scanning/resources/scanning_app.ts
ash/webui/shimless_rma/resources/onboarding_enter_rsu_wp_disable_code_page.ts
ash/webui/shortcut_customization_ui/resources/PRESUBMIT.py
ash/webui/shortcut_customization_ui/url_constants.cc
ash/webui/system_apps/public/system_web_app_type.h
ash/wm/coral/coral_controller_unittest.cc
ash/wm/desks/templates/admin_template_unittest.cc
ash/wm/desks/templates/saved_desk_unittest.cc
ash/wm/overview/birch/birch_bar_unittest.cc
ash/wm/overview/birch/birch_chip_context_menu_model.cc
ash/wm/screen_pinning_controller.h
ash/wm/window_restore/informed_restore_controller.cc
base/BUILD.gn
base/PRESUBMIT.py
base/allocator/partition_alloc_support.cc
base/allocator/partition_allocator/PRESUBMIT.py
base/allocator/partition_allocator/partition_alloc.gni
base/allocator/partition_allocator/src/partition_alloc/BUILD.gn
base/allocator/partition_allocator/src/partition_alloc/address_space_randomization.h
base/allocator/partition_allocator/src/partition_alloc/bucket_lookup.h
base/allocator/partition_allocator/src/partition_alloc/build_config.h
base/allocator/partition_allocator/src/partition_alloc/in_slot_metadata.h
base/allocator/partition_allocator/src/partition_alloc/oom.cc
base/allocator/partition_allocator/src/partition_alloc/oom.h
base/allocator/partition_allocator/src/partition_alloc/page_allocator.h
base/allocator/partition_allocator/src/partition_alloc/page_allocator_constants.h
base/allocator/partition_allocator/src/partition_alloc/page_allocator_internals_win.h
base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/compiler_specific.h
base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/debug/stack_trace.cc
base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/logging.h
base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/memory/scoped_refptr.h
base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/rand_util_posix.cc
base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/rand_util_win.cc
base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/threading/platform_thread_win_for_testing.cc
base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/time/time.h
base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/time/time_win.cc
base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/win/windows_handle_util.h
base/allocator/partition_allocator/src/partition_alloc/partition_alloc_config.h
base/allocator/partition_allocator/src/partition_alloc/partition_alloc_unittest.cc
base/allocator/partition_allocator/src/partition_alloc/partition_tls.h
base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr.h
base/allocator/partition_allocator/src/partition_alloc/shim/allocator_shim_default_dispatch_to_partition_alloc.cc
base/allocator/partition_allocator/src/partition_alloc/stack/asm/x64/push_registers_asm.cc
base/allocator/partition_allocator/src/partition_alloc/stack/stack.cc
base/allocator/partition_allocator/src/partition_alloc/tagging.h
base/allocator/partition_allocator/src/partition_alloc/use_death_tests.h
base/android/jni_android.cc
base/android/jni_string.cc
base/android/linker/ashmem.cc
base/android/meminfo_dump_provider.cc
base/android/requires_api.h
base/android/resource_exclusions.gni
base/android/virtual_document_path.h
base/apple/foundation_util_unittest.mm
base/apple/owned_objc.h
base/apple/owned_objc_types.h
base/barrier_callback.h
base/base_paths_win.cc
base/base_switches.cc
base/command_line.cc
base/command_line.h
base/compiler_specific.h
base/containers/auto_spanification_helper.h
base/containers/checked_iterators_unittest.cc
base/debug/debugger_posix.cc
base/debug/stack_trace.cc
base/debug/stack_trace_fuchsia.cc
base/debug/stack_trace_posix.cc
base/debug/stack_trace_win.cc
base/file_version_info.h
base/files/file_enumerator_posix.cc
base/files/file_path.cc
base/files/file_path_watcher_unittest.cc
base/files/file_path_watcher_win.cc
base/files/file_unittest.cc
base/files/file_util_posix.cc
base/files/file_util_unittest.cc
base/files/file_util_win.cc
base/files/file_win.cc
base/functional/bind_internal.h
base/functional/callback.h
base/hash/hash.cc
base/i18n/break_iterator_unittest.cc
base/i18n/file_util_icu.cc
base/i18n/rtl_unittest.cc
base/i18n/timezone_unittest.cc
base/ios/device_util.mm
base/lazy_instance_helpers.h
base/logging.cc
base/logging.h
base/mac/close_nocancel.cc
base/memory/aligned_memory.cc
base/memory/discardable_shared_memory.cc
base/memory/platform_shared_memory_mapper_android.cc
base/memory/raw_ptr_asan_service.cc
base/memory/scoped_refptr.h
base/memory/weak_ptr.h
base/message_loop/message_pump_android.h
base/message_loop/message_pump_win.cc
base/metrics/field_trial_list_including_low_anonymity.h
base/metrics/histogram_functions.h
base/metrics/histogram_functions_internal_overloads.h
base/metrics/histogram_macros.h
base/metrics/statistics_recorder.cc
base/metrics/user_metrics.h
base/observer_list.h
base/power_monitor/battery_level_provider_win.cc
base/power_monitor/cpu_frequency_utils.cc
base/power_monitor/energy_monitor_android.h
base/power_monitor/power_monitor_device_source_android.cc
base/power_monitor/speed_limit_observer_win.cc
base/process/launch.h
base/process/launch_mac.cc
base/process/launch_posix.cc
base/process/process_handle_win.cc
base/process/process_info.h
base/process/process_metrics.h
base/profiler/chrome_unwind_info_android_32.h
base/profiler/libunwindstack_unwinder_android.cc
base/profiler/libunwindstack_unwinder_android_unittest.cc
base/profiler/metadata_recorder.h
base/rand_util_win.cc
base/strings/pattern_unittest.cc
base/strings/string_number_conversions_unittest.cc
base/synchronization/lock_impl_posix.cc
base/system/sys_info.h
base/task/sequence_manager/sequence_manager_impl.cc
base/task/sequenced_task_runner.h
base/task/single_thread_task_executor_unittest.cc
base/task/single_thread_task_runner.h
base/task/task_traits.h
base/task/thread_type.h
base/test/file_path_reparse_point_win.cc
base/test/gtest_links_unittest.cc
base/test/gtest_xml_unittest_result_printer_unittest.cc
base/test/launcher/test_launcher.cc
base/test/launcher/test_results_tracker_unittest.cc
base/test/metrics/action_variants_reader.h
base/test/metrics/action_variants_reader_unittest.cc
base/test/sequenced_task_runner_test_template.h
base/test/task_environment_unittest.cc
base/test/test_file_util.h
base/test/test_file_util_win.cc
base/test/test_suite.cc
base/third_party/cityhash/city.h
base/third_party/nspr/prtime.cc
base/third_party/nspr/prtime.h
base/third_party/symbolize/symbolize.cc
base/threading/platform_thread_android.cc
base/threading/platform_thread_unittest.cc
base/threading/platform_thread_win.cc
base/threading/sequence_local_storage_slot_unittest.cc
base/time/time_delta_from_string.cc
base/time/time_delta_from_string_unittest.cc
base/time/time_win.cc
base/trace_event/etw_interceptor_win.cc
base/trace_event/malloc_dump_provider.cc
base/trace_event/process_memory_dump.h
base/trace_event/trace_logging_minimal_win.h
base/tracing/stdlib/chrome/page_loads.sql
base/tracing/stdlib/chrome/scroll_jank_v4.sql
base/tracing/test/test_data.py
base/values_unittest.cc
base/win/delayload_helpers.h
base/win/elevation_util.cc
base/win/event_trace_consumer.h
base/win/object_watcher.cc
base/win/pdh_shim.h
base/win/pe_image.h
base/win/registry.cc
base/win/registry.h
base/win/registry_unittest.cc
base/win/scoped_com_initializer.cc
base/win/scoped_pdh_query.h
base/win/scoped_variant.cc
base/win/shortcut.cc
base/win/win_util.cc
base/win/win_util.h
base/win/wincrypt_shim.h
base/win/windows_handle_util.h
base/win/windows_version.h
base/win/windows_version_unittest.cc
base/win/wmi.cc
base/win/wmi.h
build/3pp_common/common.py
build/3pp_common/maven.py
build/BUILD.gn
build/android/BUILD.gn
build/android/PRESUBMIT.py
build/android/apk_operations.py
build/android/dump_apk_resource_strings.py
build/android/fast_local_dev_server.py
build/android/generate_wrap_sh.py
build/android/gradle/generate_gradle.py
build/android/gyp/assert_static_initializers.py
build/android/gyp/bundletool.py
build/android/gyp/create_app_bundle.py
build/android/gyp/create_java_binary_script.py
build/android/gyp/dex.py
build/android/gyp/errorprone.py
build/android/gyp/lint.py
build/android/gyp/merge_manifest.py
build/android/gyp/util/build_utils.py
build/android/gyp/util/diff_utils.py
build/android/gyp/util/protoresources.py
build/android/gyp/util/resources_parser.py
build/android/gyp/write_build_config.py
build/android/incremental_install/generate_android_manifest.py
build/android/method_count.py
build/android/pylib/constants/__init__.py
build/android/pylib/device_settings.py
build/android/pylib/dex/dex_parser.py
build/android/pylib/instrumentation/instrumentation_parser.py
build/android/pylib/instrumentation/instrumentation_test_instance.py
build/android/pylib/instrumentation/render_test.html.jinja
build/android/pylib/local/device/local_device_environment.py
build/android/pylib/local/device/local_device_gtest_run.py
build/android/pylib/local/device/local_device_monkey_test_run.py
build/android/pylib/local/emulator/avd.py
build/android/pylib/results/flakiness_dashboard/json_results_generator.py
build/android/pylib/results/presentation/test_results_presentation.py
build/android/pylib/utils/maven_downloader.py
build/android/pylib/utils/simpleperf.py
build/android/resource_sizes.py
build/android/test_runner.py
build/android/test_wra
Download .txt
gitextract_kl8f_uin/

├── .cirrus.yml
├── .cirrus_Dockerfile
├── .cirrus_requirements.txt
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bugreport.yml
│   │   ├── create-an--updating-to-chromium-x-x-x-x-.md
│   │   └── feature_request.yml
│   ├── PULL_REQUEST_TEMPLATE.md
│   └── workflows/
│       ├── new_version_check.yml
│       ├── release-on-tag.yml
│       └── stale.yml
├── .gitignore
├── .style.yapf
├── CODEOWNERS
├── LICENSE
├── README.md
├── SUPPORT.md
├── chromium_version.txt
├── devutils/
│   ├── .coveragerc
│   ├── README.md
│   ├── __init__.py
│   ├── check_all_code.sh
│   ├── check_downloads_ini.py
│   ├── check_files_exist.py
│   ├── check_gn_flags.py
│   ├── check_patch_files.py
│   ├── print_tag_version.sh
│   ├── pytest.ini
│   ├── run_devutils_pylint.py
│   ├── run_devutils_tests.sh
│   ├── run_devutils_yapf.sh
│   ├── run_other_pylint.py
│   ├── run_other_yapf.sh
│   ├── run_utils_pylint.py
│   ├── run_utils_tests.sh
│   ├── run_utils_yapf.sh
│   ├── set_quilt_vars.fish
│   ├── set_quilt_vars.sh
│   ├── tests/
│   │   ├── __init__.py
│   │   ├── test_check_patch_files.py
│   │   └── test_validate_patches.py
│   ├── third_party/
│   │   ├── README.md
│   │   ├── __init__.py
│   │   └── unidiff/
│   │       ├── __init__.py
│   │       ├── __version__.py
│   │       ├── constants.py
│   │       ├── errors.py
│   │       └── patch.py
│   ├── update_lists.py
│   ├── update_platform_patches.py
│   ├── validate_config.py
│   └── validate_patches.py
├── docs/
│   ├── building.md
│   ├── contributing.md
│   ├── default_settings.md
│   ├── design.md
│   ├── developing.md
│   ├── flags.md
│   ├── platforms.md
│   └── repo_management.md
├── domain_regex.list
├── domain_substitution.list
├── downloads.ini
├── flags.gn
├── patches/
│   ├── core/
│   │   ├── bromite/
│   │   │   └── disable-fetching-field-trials.patch
│   │   ├── inox-patchset/
│   │   │   ├── 0001-fix-building-without-safebrowsing.patch
│   │   │   ├── 0003-disable-autofill-download-manager.patch
│   │   │   ├── 0005-disable-default-extensions.patch
│   │   │   ├── 0015-disable-update-pings.patch
│   │   │   └── 0021-disable-rlz.patch
│   │   ├── iridium-browser/
│   │   │   ├── all-add-trk-prefixes-to-possibly-evil-connections.patch
│   │   │   ├── safe_browsing-disable-incident-reporting.patch
│   │   │   └── safe_browsing-disable-reporting-of-safebrowsing-over.patch
│   │   └── ungoogled-chromium/
│   │       ├── block-requests.patch
│   │       ├── block-trk-and-subdomains.patch
│   │       ├── build-with-wasm-rollup.patch
│   │       ├── disable-crash-reporter.patch
│   │       ├── disable-domain-reliability.patch
│   │       ├── disable-fonts-googleapis-references.patch
│   │       ├── disable-gaia.patch
│   │       ├── disable-gcm.patch
│   │       ├── disable-google-host-detection.patch
│   │       ├── disable-mei-preload.patch
│   │       ├── disable-network-time-tracker.patch
│   │       ├── disable-privacy-sandbox.patch
│   │       ├── disable-profile-avatar-downloading.patch
│   │       ├── disable-untraceable-urls.patch
│   │       ├── disable-webrtc-log-uploader.patch
│   │       ├── disable-webstore-urls.patch
│   │       ├── doh-changes.patch
│   │       ├── extensions-manifestv2.patch
│   │       ├── fix-building-with-prunned-binaries.patch
│   │       ├── fix-building-without-safebrowsing.patch
│   │       ├── fix-learn-doubleclick-hsts.patch
│   │       ├── move-js-optimizer-unfamiliar-sites.patch
│   │       ├── remove-f1-shortcut.patch
│   │       ├── remove-unused-preferences-fields.patch
│   │       ├── replace-google-search-engine-with-nosearch.patch
│   │       └── toggle-translation-via-switch.patch
│   ├── extra/
│   │   ├── bromite/
│   │   │   ├── fingerprinting-flags-client-rects-and-measuretext.patch
│   │   │   ├── flag-fingerprinting-canvas-image-data-noise.patch
│   │   │   └── flag-max-connections-per-host.patch
│   │   ├── debian/
│   │   │   └── disable/
│   │   │       └── google-api-warning.patch
│   │   ├── inox-patchset/
│   │   │   ├── 0006-modify-default-prefs.patch
│   │   │   ├── 0008-restore-classic-ntp.patch
│   │   │   ├── 0013-disable-missing-key-warning.patch
│   │   │   ├── 0016-chromium-sandbox-pie.patch
│   │   │   └── 0019-disable-battery-status-service.patch
│   │   ├── iridium-browser/
│   │   │   ├── Remove-EV-certificates.patch
│   │   │   ├── browser-disable-profile-auto-import-on-first-run.patch
│   │   │   ├── mime_util-force-text-x-suse-ymp-to-be-downloaded.patch
│   │   │   ├── prefs-always-prompt-for-download-directory-by-defaul.patch
│   │   │   └── updater-disable-auto-update.patch
│   │   └── ungoogled-chromium/
│   │       ├── add-components-ungoogled.patch
│   │       ├── add-credits.patch
│   │       ├── add-extra-channel-info.patch
│   │       ├── add-flag-for-bookmark-bar-ntp.patch
│   │       ├── add-flag-for-close-confirmation.patch
│   │       ├── add-flag-for-custom-ntp.patch
│   │       ├── add-flag-for-disabling-link-drag.patch
│   │       ├── add-flag-for-grab-handle.patch
│   │       ├── add-flag-for-incognito-themes.patch
│   │       ├── add-flag-for-omnibox-autocomplete-filtering.patch
│   │       ├── add-flag-for-qr-generator.patch
│   │       ├── add-flag-for-search-engine-collection.patch
│   │       ├── add-flag-for-tab-hover-cards.patch
│   │       ├── add-flag-for-tabsearch-button.patch
│   │       ├── add-flag-to-change-http-accept-header.patch
│   │       ├── add-flag-to-clear-data-on-exit.patch
│   │       ├── add-flag-to-close-window-with-last-tab.patch
│   │       ├── add-flag-to-configure-extension-downloading.patch
│   │       ├── add-flag-to-convert-popups-to-tabs.patch
│   │       ├── add-flag-to-disable-beforeunload.patch
│   │       ├── add-flag-to-disable-local-history-expiration.patch
│   │       ├── add-flag-to-disable-sharing-hub.patch
│   │       ├── add-flag-to-disable-tls-grease.patch
│   │       ├── add-flag-to-force-punycode-hostnames.patch
│   │       ├── add-flag-to-hide-crashed-bubble.patch
│   │       ├── add-flag-to-hide-extensions-menu.patch
│   │       ├── add-flag-to-hide-fullscreen-exit-ui.patch
│   │       ├── add-flag-to-hide-tab-close-buttons.patch
│   │       ├── add-flag-to-increase-incognito-storage-quota.patch
│   │       ├── add-flag-to-reduce-system-info.patch
│   │       ├── add-flag-to-remove-client-hints.patch
│   │       ├── add-flag-to-scroll-tabs.patch
│   │       ├── add-flag-to-show-avatar-button.patch
│   │       ├── add-flag-to-spoof-webgl-renderer-info.patch
│   │       ├── add-flags-for-existing-switches.patch
│   │       ├── add-flags-for-referrer-customization.patch
│   │       ├── add-ipv6-probing-option.patch
│   │       ├── add-suggestions-url-field.patch
│   │       ├── add-ungoogled-flag-headers.patch
│   │       ├── default-webrtc-ip-handling-policy.patch
│   │       ├── disable-ai-search-shortcuts.patch
│   │       ├── disable-chromelabs.patch
│   │       ├── disable-dial-repeating-discovery.patch
│   │       ├── disable-download-quarantine.patch
│   │       ├── disable-fedcm-by-default.patch
│   │       ├── disable-formatting-in-omnibox.patch
│   │       ├── disable-intranet-redirect-detector.patch
│   │       ├── enable-certificate-transparency-and-add-flag.patch
│   │       ├── enable-extra-locales.patch
│   │       ├── enable-menu-on-reload-button.patch
│   │       ├── enable-page-saving-on-more-pages.patch
│   │       ├── enable-paste-and-go-new-tab-button.patch
│   │       ├── first-run-page.patch
│   │       ├── fix-building-without-mdns-and-service-discovery.patch
│   │       ├── fix-distilled-icons.patch
│   │       ├── keep-expired-flags.patch
│   │       ├── prepopulated-search-engines.patch
│   │       ├── remove-disable-setuid-sandbox-as-bad-flag.patch
│   │       ├── remove-pac-size-limit.patch
│   │       └── remove-uneeded-ui.patch
│   ├── series
│   └── upstream-fixes/
│       └── missing-dependencies.patch
├── pruning.list
├── revision.txt
├── shell.nix
└── utils/
    ├── .coveragerc
    ├── __init__.py
    ├── _common.py
    ├── _extraction.py
    ├── clone.py
    ├── depot_tools.patch
    ├── domain_substitution.py
    ├── downloads.py
    ├── filescfg.py
    ├── gsutil.patch
    ├── make_domsub_script.py
    ├── patches.py
    ├── prune_binaries.py
    ├── pytest.ini
    ├── tests/
    │   ├── __init__.py
    │   ├── test_domain_substitution.py
    │   └── test_patches.py
    └── third_party/
        ├── README.md
        ├── __init__.py
        └── schema.py
Download .txt
SYMBOL INDEX (253 symbols across 27 files)

FILE: devutils/check_downloads_ini.py
  function check_downloads_ini (line 28) | def check_downloads_ini(downloads_ini_iter):
  function main (line 43) | def main():

FILE: devutils/check_files_exist.py
  function main (line 17) | def main():

FILE: devutils/check_gn_flags.py
  function check_gn_flags (line 28) | def check_gn_flags(gn_flags_path):
  function main (line 60) | def main():

FILE: devutils/check_patch_files.py
  function _read_series_file (line 34) | def _read_series_file(patches_dir, series_file, join_dir=False):
  function check_patch_readability (line 50) | def check_patch_readability(patches_dir, series_path=Path('series')):
  function check_unused_patches (line 73) | def check_unused_patches(patches_dir, series_path=Path('series')):
  function check_series_duplicates (line 98) | def check_series_duplicates(patches_dir, series_path=Path('series')):
  function main (line 115) | def main():

FILE: devutils/run_devutils_pylint.py
  function main (line 15) | def main():

FILE: devutils/run_other_pylint.py
  class ChangeDir (line 17) | class ChangeDir:
    method __init__ (line 22) | def __init__(self, path):
    method __enter__ (line 26) | def __enter__(self):
    method __exit__ (line 29) | def __exit__(self, *_):
  function run_pylint (line 33) | def run_pylint(module_path, pylint_options, ignore_prefixes=tuple()):
  function main (line 67) | def main():

FILE: devutils/run_utils_pylint.py
  function main (line 15) | def main():

FILE: devutils/tests/test_check_patch_files.py
  function test_check_series_duplicates (line 24) | def test_check_series_duplicates():

FILE: devutils/tests/test_validate_patches.py
  function test_test_patches (line 24) | def test_test_patches():

FILE: devutils/third_party/unidiff/errors.py
  class UnidiffParseError (line 30) | class UnidiffParseError(Exception):

FILE: devutils/third_party/unidiff/patch.py
  function implements_to_string (line 56) | def implements_to_string(cls):
  class Line (line 70) | class Line(object):
    method __init__ (line 73) | def __init__(self, value, line_type,
    method __repr__ (line 82) | def __repr__(self):
    method __str__ (line 85) | def __str__(self):
    method __eq__ (line 88) | def __eq__(self, other):
    method is_added (line 96) | def is_added(self):
    method is_removed (line 100) | def is_removed(self):
    method is_context (line 104) | def is_context(self):
  class PatchInfo (line 109) | class PatchInfo(list):
    method __repr__ (line 117) | def __repr__(self):
    method __str__ (line 121) | def __str__(self):
  class Hunk (line 126) | class Hunk(list):
    method __init__ (line 129) | def __init__(self, src_start=0, src_len=0, tgt_start=0, tgt_len=0,
    method __repr__ (line 145) | def __repr__(self):
    method __str__ (line 153) | def __str__(self):
    method append (line 162) | def append(self, line):
    method is_valid (line 176) | def is_valid(self):
    method source_lines (line 181) | def source_lines(self):
    method target_lines (line 185) | def target_lines(self):
  class PatchedFile (line 190) | class PatchedFile(list):
    method __init__ (line 193) | def __init__(self, patch_info=None, source='', target='',
    method __repr__ (line 202) | def __repr__(self):
    method __str__ (line 205) | def __str__(self):
    method _parse_hunk (line 217) | def _parse_hunk(self, header, diff, encoding):
    method _add_no_newline_marker_to_last_hunk (line 281) | def _add_no_newline_marker_to_last_hunk(self):
    method _append_trailing_empty_line (line 289) | def _append_trailing_empty_line(self):
    method path (line 296) | def path(self):
    method added (line 312) | def added(self):
    method removed (line 317) | def removed(self):
    method is_added_file (line 322) | def is_added_file(self):
    method is_removed_file (line 328) | def is_removed_file(self):
    method is_modified_file (line 334) | def is_modified_file(self):
  class PatchSet (line 340) | class PatchSet(list):
    method __init__ (line 343) | def __init__(self, f, encoding=None):
    method __repr__ (line 355) | def __repr__(self):
    method __str__ (line 358) | def __str__(self):
    method _parse (line 361) | def _parse(self, diff, encoding):
    method from_filename (line 422) | def from_filename(cls, filename, encoding=DEFAULT_ENCODING, errors=None):
    method _convert_string (line 429) | def _convert_string(data, encoding=None, errors='strict'):
    method from_string (line 436) | def from_string(cls, data, encoding=None, errors='strict'):
    method added_files (line 441) | def added_files(self):
    method removed_files (line 446) | def removed_files(self):
    method modified_files (line 451) | def modified_files(self):
    method added (line 456) | def added(self):
    method removed (line 461) | def removed(self):

FILE: devutils/update_lists.py
  class UnusedPatterns (line 137) | class UnusedPatterns: #pylint: disable=too-few-public-methods
    method __init__ (line 143) | def __init__(self):
    method log_unused (line 149) | def log_unused(self, error=True):
  function _is_binary (line 165) | def _is_binary(bytes_data):
  function _dir_empty (line 173) | def _dir_empty(path):
  function should_prune (line 186) | def should_prune(path, relative_path, used_pep_set, used_pip_set):
  function _check_regex_match (line 214) | def _check_regex_match(file_path, search_regex):
  function should_domain_substitute (line 235) | def should_domain_substitute(path, relative_path, search_regex, used_dep...
  function compute_lists_proc (line 260) | def compute_lists_proc(path, source_tree, search_regex):
  function compute_lists (line 298) | def compute_lists(source_tree, search_regex, processes): # pylint: disab...
  function main (line 343) | def main(args_list=None):

FILE: devutils/update_platform_patches.py
  function merge_platform_patches (line 29) | def merge_platform_patches(platform_patches_dir, prepend_patches_dir):
  function _dir_empty (line 53) | def _dir_empty(path):
  function _rename_files_with_dirs (line 66) | def _rename_files_with_dirs(root_dir, source_dir, sorted_file_iter):
  function unmerge_platform_patches (line 90) | def unmerge_platform_patches(platform_patches_dir, prepend_patches_dir):
  function main (line 160) | def main():

FILE: devutils/validate_config.py
  function main (line 34) | def main():

FILE: devutils/validate_patches.py
  class _VerboseRetry (line 41) | class _VerboseRetry(urllib3.util.Retry):
    method sleep_for_retry (line 44) | def sleep_for_retry(self, response=None):
    method _sleep_backoff (line 58) | def _sleep_backoff(self):
  function _get_requests_session (line 63) | def _get_requests_session():
  function _get_requests_session (line 77) | def _get_requests_session():
  class _PatchValidationError (line 86) | class _PatchValidationError(Exception):
  class _UnexpectedSyntaxError (line 90) | class _UnexpectedSyntaxError(RuntimeError):
  class _NotInRepoError (line 94) | class _NotInRepoError(RuntimeError):
  class _DepsNodeVisitor (line 98) | class _DepsNodeVisitor(ast.NodeVisitor):
    method visit_Call (line 103) | def visit_Call(self, node): #pylint: disable=invalid-name
    method generic_visit (line 109) | def generic_visit(self, node):
  function _validate_deps (line 118) | def _validate_deps(deps_text):
  function _deps_var (line 128) | def _deps_var(deps_globals):
  function _parse_deps (line 138) | def _parse_deps(deps_text):
  function _download_googlesource_file (line 146) | def _download_googlesource_file(download_session, repo_url, version, rel...
  function _get_dep_value_url (line 163) | def _get_dep_value_url(deps_globals, dep_value):
  function _process_deps_entries (line 183) | def _process_deps_entries(deps_globals, child_deps_tree, child_path, dep...
  function _get_child_deps_tree (line 211) | def _get_child_deps_tree(download_session, current_deps_tree, child_path...
  function _get_last_chromium_modification (line 225) | def _get_last_chromium_modification():
  function _get_gitiles_git_log_date (line 234) | def _get_gitiles_git_log_date(log_entry):
  function _get_gitiles_commit_before_date (line 239) | def _get_gitiles_commit_before_date(repo_url, target_branch, target_date...
  class _FallbackRepoManager (line 272) | class _FallbackRepoManager:
    method __init__ (line 277) | def __init__(self):
    method gn_version (line 281) | def gn_version(self):
    method get_fallback (line 298) | def get_fallback(self, current_relative_path, current_node, root_deps_...
  function _get_target_file_deps_node (line 318) | def _get_target_file_deps_node(download_session, root_deps_tree, target_...
  function _download_source_file (line 349) | def _download_source_file(download_session, root_deps_tree, fallback_rep...
  function _initialize_deps_tree (line 386) | def _initialize_deps_tree():
  function _retrieve_remote_files (line 406) | def _retrieve_remote_files(file_iter):
  function _retrieve_local_files (line 456) | def _retrieve_local_files(file_iter, source_dir):
  function _modify_file_lines (line 486) | def _modify_file_lines(patched_file, file_lines):
  function _apply_file_unidiff (line 518) | def _apply_file_unidiff(patched_file, files_under_test):
  function _dry_check_patched_file (line 537) | def _dry_check_patched_file(patched_file, orig_file_content):
  function _test_patches (line 553) | def _test_patches(series_iter, patch_cache, files_under_test):
  function _load_all_patches (line 589) | def _load_all_patches(series_iter, patches_dir):
  function _get_required_files (line 610) | def _get_required_files(patch_cache):
  function _get_files_under_test (line 623) | def _get_files_under_test(args, required_files, parser):
  function main (line 643) | def main():

FILE: utils/_common.py
  class PlatformEnum (line 24) | class PlatformEnum(enum.Enum):
  class ExtractorEnum (line 30) | class ExtractorEnum: #pylint: disable=too-few-public-methods
  class SetLogLevel (line 37) | class SetLogLevel(argparse.Action): #pylint: disable=too-few-public-methods
    method __init__ (line 40) | def __init__(self, option_strings, dest, nargs=None, **kwargs):
    method __call__ (line 43) | def __call__(self, parser, namespace, value, option_string=None):
  function get_logger (line 63) | def get_logger(initial_level=logging.INFO):
  function set_logging_level (line 83) | def set_logging_level(logging_level):
  function get_running_platform (line 99) | def get_running_platform():
  function get_chromium_version (line 113) | def get_chromium_version():
  function parse_series (line 118) | def parse_series(series_path):
  function add_common_params (line 135) | def add_common_params(parser):

FILE: utils/_extraction.py
  function _find_7z_by_registry (line 25) | def _find_7z_by_registry():
  function _find_winrar_by_registry (line 43) | def _find_winrar_by_registry():
  function _find_extractor_by_cmd (line 61) | def _find_extractor_by_cmd(extractor_cmd):
  function _process_relative_to (line 70) | def _process_relative_to(unpack_root, relative_to):
  function _extract_tar_with_7z (line 90) | def _extract_tar_with_7z(binary, archive_path, output_dir, relative_to):
  function _extract_tar_with_tar (line 113) | def _extract_tar_with_tar(binary, archive_path, output_dir, relative_to):
  function _extract_tar_with_winrar (line 128) | def _extract_tar_with_winrar(binary, archive_path, output_dir, relative_...
  function _extract_tar_with_python (line 141) | def _extract_tar_with_python(archive_path, output_dir, relative_to):
  function extract_tar_file (line 192) | def extract_tar_file(archive_path, output_dir, relative_to, extractors=N...
  function extract_with_7z (line 241) | def extract_with_7z(archive_path, output_dir, relative_to, extractors=No...
  function extract_with_winrar (line 281) | def extract_with_winrar(archive_path, output_dir, relative_to, extractor...

FILE: utils/clone.py
  function clone (line 48) | def clone(args): # pylint: disable=too-many-branches, too-many-locals, t...
  function main (line 254) | def main():

FILE: utils/domain_substitution.py
  class DomainRegexList (line 39) | class DomainRegexList:
    method __init__ (line 46) | def __init__(self, path):
    method _compile_regex (line 52) | def _compile_regex(self, line):
    method regex_pairs (line 58) | def regex_pairs(self):
    method search_regex (line 67) | def search_regex(self):
  function _substitute_path (line 78) | def _substitute_path(path, regex_iter):
  function _validate_file_index (line 123) | def _validate_file_index(index_file, resolved_tree, cache_index_files):
  function _update_timestamp (line 161) | def _update_timestamp(path: os.PathLike, set_new: bool) -> None:
  function apply_substitution (line 182) | def apply_substitution(regex_path, files_path, source_tree, domainsub_ca...
  function revert_substitution (line 246) | def revert_substitution(domainsub_cache, source_tree):
  function _callback (line 315) | def _callback(args):
  function main (line 323) | def main():

FILE: utils/downloads.py
  class HashesURLEnum (line 34) | class HashesURLEnum(str, enum.Enum):
  class HashMismatchError (line 39) | class HashMismatchError(BaseException):
  class DownloadInfo (line 43) | class DownloadInfo: #pylint: disable=too-few-public-methods
    method _is_hash_url (line 59) | def _is_hash_url(value):
    class _DownloadsProperties (line 77) | class _DownloadsProperties: #pylint: disable=too-few-public-methods
      method __init__ (line 79) | def __init__(self, section_dict, passthrough_properties, hashes):
      method has_hash_url (line 84) | def has_hash_url(self):
      method __getattr__ (line 90) | def __getattr__(self, name):
    method _parse_data (line 104) | def _parse_data(self, path):
    method __init__ (line 128) | def __init__(self, ini_paths):
    method __getitem__ (line 134) | def __getitem__(self, section):
    method __contains__ (line 142) | def __contains__(self, item):
    method __iter__ (line 148) | def __iter__(self):
    method properties_iter (line 152) | def properties_iter(self):
    method check_sections_exist (line 157) | def check_sections_exist(self, section_names):
  class _UrlRetrieveReportHook (line 166) | class _UrlRetrieveReportHook: #pylint: disable=too-few-public-methods
    method __init__ (line 169) | def __init__(self):
    method __call__ (line 173) | def __call__(self, block_count, block_size, total_size):
  function _download_via_urllib (line 196) | def _download_via_urllib(url, file_path, show_progress, disable_ssl_veri...
  function _download_if_needed (line 214) | def _download_if_needed(file_path, url, show_progress, disable_ssl_verif...
  function _chromium_hashes_generator (line 248) | def _chromium_hashes_generator(hashes_path):
  function _get_hash_pairs (line 258) | def _get_hash_pairs(download_properties, cache_dir):
  function retrieve_downloads (line 271) | def retrieve_downloads(download_info,
  function check_downloads (line 308) | def check_downloads(download_info, cache_dir, components, chunk_bytes=26...
  function unpack_downloads (line 339) | def unpack_downloads(download_info, cache_dir, components, output_dir, e...
  function _add_common_args (line 379) | def _add_common_args(parser):
  function _retrieve_callback (line 393) | def _retrieve_callback(args):
  function _unpack_callback (line 405) | def _unpack_callback(args):
  function main (line 419) | def main():

FILE: utils/filescfg.py
  function filescfg_generator (line 22) | def filescfg_generator(cfg_path, build_outputs, cpu_arch, excluded_files...
  function _get_archive_writer (line 52) | def _get_archive_writer(output_path, timestamp=None):
  function create_archive (line 120) | def create_archive(file_iter, include_iter, build_outputs, output_path, ...
  function _files_generator_by_args (line 137) | def _files_generator_by_args(args):
  function _list_callback (line 152) | def _list_callback(args):
  function _archive_callback (line 157) | def _archive_callback(args):
  function main (line 165) | def main():

FILE: utils/make_domsub_script.py
  function make_domain_substitution_script (line 17) | def make_domain_substitution_script(regex_path, files_path, backup_type,...
  function _callback (line 97) | def _callback(args):
  function main (line 102) | def main():

FILE: utils/patches.py
  function _find_patch_from_env (line 18) | def _find_patch_from_env():
  function _find_patch_from_which (line 35) | def _find_patch_from_which():
  function find_and_check_patch (line 43) | def find_and_check_patch(patch_bin_path=None):
  function dry_run_check (line 81) | def dry_run_check(patch_path, tree_path, patch_bin_path=None):
  function apply_patches (line 106) | def apply_patches(patch_path_iter, tree_path, reverse=False, patch_bin_p...
  function generate_patches_from_series (line 141) | def generate_patches_from_series(patches_dir, resolve=False):
  function _copy_files (line 150) | def _copy_files(path_iter, source, destination):
  function merge_patches (line 157) | def merge_patches(source_iter, destination, prepend=False):
  function _apply_callback (line 188) | def _apply_callback(args, parser_error):
  function _merge_callback (line 207) | def _merge_callback(args, _):
  function main (line 211) | def main():

FILE: utils/prune_binaries.py
  function prune_files (line 153) | def prune_files(unpack_root, prune_list):
  function _prune_path (line 175) | def _prune_path(path, unpack_root=None):
  function prune_dirs (line 200) | def prune_dirs(unpack_root, keep_contingent_paths, sysroot):
  function _callback (line 222) | def _callback(args):
  function main (line 241) | def main():

FILE: utils/tests/test_domain_substitution.py
  function test_update_timestamp (line 14) | def test_update_timestamp():

FILE: utils/tests/test_patches.py
  function test_find_and_check_patch (line 16) | def test_find_and_check_patch():
  function test_patch_from_which (line 27) | def test_patch_from_which():
  function test_patch_from_env (line 32) | def test_patch_from_env():

FILE: utils/third_party/schema.py
  class SchemaError (line 17) | class SchemaError(Exception):
    method __init__ (line 20) | def __init__(self, autos, errors=None):
    method code (line 26) | def code(self):
  class SchemaWrongKeyError (line 46) | class SchemaWrongKeyError(SchemaError):
  class SchemaMissingKeyError (line 52) | class SchemaMissingKeyError(SchemaError):
  class SchemaForbiddenKeyError (line 58) | class SchemaForbiddenKeyError(SchemaError):
  class SchemaUnexpectedTypeError (line 64) | class SchemaUnexpectedTypeError(SchemaError):
  class And (line 70) | class And:
    method __init__ (line 74) | def __init__(self, *args, **kw):
    method __repr__ (line 82) | def __repr__(self):
    method validate (line 86) | def validate(self, data):
  class Or (line 100) | class Or(And):
    method validate (line 103) | def validate(self, data):
  class Regex (line 123) | class Regex:
    method __init__ (line 131) | def __init__(self, pattern_str, flags=0, error=None):
    method __repr__ (line 144) | def __repr__(self):
    method validate (line 149) | def validate(self, data):
  class Use (line 166) | class Use:
    method __init__ (line 171) | def __init__(self, callable_, error=None):
    method __repr__ (line 176) | def __repr__(self):
    method validate (line 179) | def validate(self, data):
  function _priority (line 196) | def _priority(s):
  class Schema (line 212) | class Schema:
    method __init__ (line 217) | def __init__(self, schema, error=None, ignore_extra_keys=False):
    method __repr__ (line 222) | def __repr__(self):
    method _dict_key_priority (line 226) | def _dict_key_priority(s):
    method validate (line 234) | def validate(self, data):
  class Optional (line 341) | class Optional(Schema):
    method __init__ (line 345) | def __init__(self, *args, **kwargs):
    method __hash__ (line 358) | def __hash__(self):
    method __eq__ (line 361) | def __eq__(self, other):
  class Forbidden (line 368) | class Forbidden(Schema):
    method __init__ (line 369) | def __init__(self, *args, **kwargs):
  class Const (line 374) | class Const(Schema):
    method validate (line 375) | def validate(self, data):
  function _callable_str (line 380) | def _callable_str(callable_):
Condensed preview — 197 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (3,690K chars).
[
  {
    "path": ".cirrus.yml",
    "chars": 2383,
    "preview": "env:\n    CIRRUS_CLONE_DEPTH: 1\n\ncontainer:\n    dockerfile: .cirrus_Dockerfile\n    cpu: 8\n    memory: 32G\n    use_in_memo"
  },
  {
    "path": ".cirrus_Dockerfile",
    "chars": 158,
    "preview": "# Dockerfile for Python 3 with xz-utils (for tar.xz unpacking)\n\nFROM python:3.10-slim-bookworm\n\nRUN apt update && apt in"
  },
  {
    "path": ".cirrus_requirements.txt",
    "chars": 224,
    "preview": "# Based on Python package versions in Debian bookworm\n# https://packages.debian.org/bookworm/python/\nastroid==2.14.2 # v"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bugreport.yml",
    "chars": 3617,
    "preview": "name: Bug Report\ndescription: Report a bug building or running ungoogled-chromium\nlabels: [\"bug\"]\nbody:\n  - type: markdo"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/create-an--updating-to-chromium-x-x-x-x-.md",
    "chars": 1221,
    "preview": "---\nname: Create an \"Updating to Chromium x.x.x.x\"\nabout: For letting the community track progress to a new stable Chrom"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.yml",
    "chars": 1690,
    "preview": "name: Feature request\ndescription: Suggest an idea\nlabels: [\"enhancement\", \"help wanted\"]\nbody:\n  - type: markdown\n    a"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 103,
    "preview": "*(Please ensure you have read SUPPORT.md and docs/contributing.md before submitting the Pull Request)*\n"
  },
  {
    "path": ".github/workflows/new_version_check.yml",
    "chars": 4692,
    "preview": "name: New version check\n\non:\n  # enabling manual trigger\n  workflow_dispatch:\n  # running every hour\n  schedule:\n    - c"
  },
  {
    "path": ".github/workflows/release-on-tag.yml",
    "chars": 455,
    "preview": "name: Create new empty release\n\non:\n  push:\n    tags:\n      - '*'\n\njobs:\n  build:\n    name: Create Release\n    runs-on: "
  },
  {
    "path": ".github/workflows/stale.yml",
    "chars": 857,
    "preview": "name: 'Close stale issues and PRs'\n\non:\n    schedule:\n        - cron: '30 1 * * *'\n\npermissions:\n    issues: write\n    p"
  },
  {
    "path": ".gitignore",
    "chars": 175,
    "preview": "# Python files\n__pycache__/\n*.py[cod]\n\n# Python testing files\n.coverage\n\n# Ignore macOS Finder meta\n.DS_Store\n.tm_proper"
  },
  {
    "path": ".style.yapf",
    "chars": 182,
    "preview": "[style]\nbased_on_style = pep8\nallow_split_before_dict_value = false\ncoalesce_brackets = true\ncolumn_limit = 100\nindent_w"
  },
  {
    "path": "CODEOWNERS",
    "chars": 27,
    "preview": "* @ungoogled-software/core\n"
  },
  {
    "path": "LICENSE",
    "chars": 1543,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2015-2026, The ungoogled-chromium Authors\nAll rights reserved.\n\nRedistribution and u"
  },
  {
    "path": "README.md",
    "chars": 12219,
    "preview": "# ungoogled-chromium\n\n*A lightweight approach to removing Google web service dependency*\n\n**Help is welcome!** See the ["
  },
  {
    "path": "SUPPORT.md",
    "chars": 2958,
    "preview": "# Support\n\n**Before you submit feedback, please ensure you have tried the following**: \n\n* Read the [FAQ](https://ungoog"
  },
  {
    "path": "chromium_version.txt",
    "chars": 15,
    "preview": "146.0.7680.153\n"
  },
  {
    "path": "devutils/.coveragerc",
    "chars": 498,
    "preview": "[run]\nbranch = True\nparallel = True\nomit = tests/*\n\n[report]\n# Regexes for lines to exclude from consideration\nexclude_l"
  },
  {
    "path": "devutils/README.md",
    "chars": 193,
    "preview": "# Developer utilities for ungoogled-chromium\n\nThis is a collection of scripts written for developing on ungoogled-chromi"
  },
  {
    "path": "devutils/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "devutils/check_all_code.sh",
    "chars": 605,
    "preview": "#!/bin/bash\n\n# Wrapper for devutils and utils formatter, linter, and tester\n\nset -eu\n\n_root_dir=$(dirname $(dirname $(re"
  },
  {
    "path": "devutils/check_downloads_ini.py",
    "chars": 1705,
    "preview": "#!/usr/bin/env python3\n# -*- coding: UTF-8 -*-\n\n# Copyright (c) 2019 The ungoogled-chromium Authors. All rights reserved"
  },
  {
    "path": "devutils/check_files_exist.py",
    "chars": 1127,
    "preview": "#!/usr/bin/env python3\n\n# Copyright (c) 2019 The ungoogled-chromium Authors. All rights reserved.\n# Use of this source c"
  },
  {
    "path": "devutils/check_gn_flags.py",
    "chars": 2260,
    "preview": "#!/usr/bin/env python3\n# -*- coding: UTF-8 -*-\n\n# Copyright (c) 2019 The ungoogled-chromium Authors. All rights reserved"
  },
  {
    "path": "devutils/check_patch_files.py",
    "chars": 4486,
    "preview": "#!/usr/bin/env python3\n# -*- coding: UTF-8 -*-\n\n# Copyright (c) 2019 The ungoogled-chromium Authors. All rights reserved"
  },
  {
    "path": "devutils/print_tag_version.sh",
    "chars": 135,
    "preview": "_root_dir=$(dirname $(dirname $(readlink -f $0)))\nprintf '%s-%s' $(cat $_root_dir/chromium_version.txt) $(cat $_root_dir"
  },
  {
    "path": "devutils/pytest.ini",
    "chars": 306,
    "preview": "[pytest]\ntestpaths = tests\n#filterwarnings =\n#\terror\n#\tignore::DeprecationWarning\n#addopts = --cov-report term-missing -"
  },
  {
    "path": "devutils/run_devutils_pylint.py",
    "chars": 1659,
    "preview": "#!/usr/bin/env python3\n\n# Copyright (c) 2019 The ungoogled-chromium Authors. All rights reserved.\n# Use of this source c"
  },
  {
    "path": "devutils/run_devutils_tests.sh",
    "chars": 151,
    "preview": "#!/bin/bash\n\nset -eu\n\n_root_dir=$(dirname $(dirname $(readlink -f $0)))\ncd ${_root_dir}/devutils\npython3 -m pytest -c ${"
  },
  {
    "path": "devutils/run_devutils_yapf.sh",
    "chars": 190,
    "preview": "#!/bin/bash\n\nset -eu\n\n_current_dir=$(dirname $(readlink -f $0))\n_root_dir=$(dirname $_current_dir)\npython3 -m yapf --sty"
  },
  {
    "path": "devutils/run_other_pylint.py",
    "chars": 2963,
    "preview": "#!/usr/bin/env python3\n\n# Copyright (c) 2019 The ungoogled-chromium Authors. All rights reserved.\n# Use of this source c"
  },
  {
    "path": "devutils/run_other_yapf.sh",
    "chars": 97,
    "preview": "#!/bin/bash\n\nset -eu\n\npython3 -m yapf --style \"$(dirname $(readlink -f $0))/.style.yapf\" -rpi $@\n"
  },
  {
    "path": "devutils/run_utils_pylint.py",
    "chars": 1600,
    "preview": "#!/usr/bin/env python3\n\n# Copyright (c) 2019 The ungoogled-chromium Authors. All rights reserved.\n# Use of this source c"
  },
  {
    "path": "devutils/run_utils_tests.sh",
    "chars": 145,
    "preview": "#!/bin/bash\n\nset -eu\n\n_root_dir=$(dirname $(dirname $(readlink -f $0)))\ncd ${_root_dir}/utils\npython3 -m pytest -c ${_ro"
  },
  {
    "path": "devutils/run_utils_yapf.sh",
    "chars": 166,
    "preview": "#!/bin/bash\n\nset -eu\n\n_root_dir=$(dirname $(dirname $(readlink -f $0)))\npython3 -m yapf --style \"$_root_dir/.style.yapf\""
  },
  {
    "path": "devutils/set_quilt_vars.fish",
    "chars": 728,
    "preview": "#!/bin/fish\n\n# Fish variant of set_quilt_vars.sh\n\nalias quilt='quilt --quiltrc -'\n\nset REPO_ROOT (dirname (dirname (read"
  },
  {
    "path": "devutils/set_quilt_vars.sh",
    "chars": 1642,
    "preview": "# Sets quilt variables for updating the patches\n# Make sure to run this with the shell command \"source\" in order to inhe"
  },
  {
    "path": "devutils/tests/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "devutils/tests/test_check_patch_files.py",
    "chars": 1463,
    "preview": "# -*- coding: UTF-8 -*-\n\n# Copyright (c) 2020 The ungoogled-chromium Authors. All rights reserved.\n# Use of this source "
  },
  {
    "path": "devutils/tests/test_validate_patches.py",
    "chars": 2330,
    "preview": "# -*- coding: UTF-8 -*-\n\n# Copyright (c) 2020 The ungoogled-chromium Authors. All rights reserved.\n# Use of this source "
  },
  {
    "path": "devutils/third_party/README.md",
    "chars": 185,
    "preview": "This directory contains third-party libraries used by devutils.\n\nContents:\n\n* [python-unidiff](https://github.com/matias"
  },
  {
    "path": "devutils/third_party/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "devutils/third_party/unidiff/__init__.py",
    "chars": 1459,
    "preview": "# -*- coding: utf-8 -*-\n\n# The MIT License (MIT)\n# Copyright (c) 2014-2017 Matias Bordese\n#\n# Permission is hereby grant"
  },
  {
    "path": "devutils/third_party/unidiff/__version__.py",
    "chars": 1170,
    "preview": "# -*- coding: utf-8 -*-\n\n# The MIT License (MIT)\n# Copyright (c) 2014-2017 Matias Bordese\n#\n# Permission is hereby grant"
  },
  {
    "path": "devutils/third_party/unidiff/constants.py",
    "chars": 2202,
    "preview": "# -*- coding: utf-8 -*-\n\n# The MIT License (MIT)\n# Copyright (c) 2014-2017 Matias Bordese\n#\n# Permission is hereby grant"
  },
  {
    "path": "devutils/third_party/unidiff/errors.py",
    "chars": 1335,
    "preview": "# -*- coding: utf-8 -*-\n\n# The MIT License (MIT)\n# Copyright (c) 2014-2017 Matias Bordese\n#\n# Permission is hereby grant"
  },
  {
    "path": "devutils/third_party/unidiff/patch.py",
    "chars": 16415,
    "preview": "# -*- coding: utf-8 -*-\n\n# The MIT License (MIT)\n# Copyright (c) 2014-2017 Matias Bordese\n#\n# Permission is hereby grant"
  },
  {
    "path": "devutils/update_lists.py",
    "chars": 16340,
    "preview": "#!/usr/bin/env python3\n\n# Copyright (c) 2019 The ungoogled-chromium Authors. All rights reserved.\n# Use of this source c"
  },
  {
    "path": "devutils/update_platform_patches.py",
    "chars": 6980,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n# Copyright (c) 2019 The ungoogled-chromium Authors. All rights reserved"
  },
  {
    "path": "devutils/validate_config.py",
    "chars": 1721,
    "preview": "#!/usr/bin/env python3\n# -*- coding: UTF-8 -*-\n\n# Copyright (c) 2019 The ungoogled-chromium Authors. All rights reserved"
  },
  {
    "path": "devutils/validate_patches.py",
    "chars": 29652,
    "preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n# Copyright (c) 2020 The ungoogled-chromium Authors. All rights reserved"
  },
  {
    "path": "docs/building.md",
    "chars": 2834,
    "preview": "# Building ungoogled-chromium\n\nThe recommended way to build ungoogled-chromium is by consulting [the repository for your"
  },
  {
    "path": "docs/contributing.md",
    "chars": 2997,
    "preview": "# Contributing\n\nThis document contains our criteria and guidelines for contributing to ungoogled-chromium.\n\nIf you have "
  },
  {
    "path": "docs/default_settings.md",
    "chars": 1160,
    "preview": "# Default Settings\n\nVanilla Chromium settings are not often changed by ungoogled-chromium, however there are some except"
  },
  {
    "path": "docs/design.md",
    "chars": 7377,
    "preview": "# Design\n\nThis document contains a high-level technical description of ungoogled-chromium and its components.\n\n## Overvi"
  },
  {
    "path": "docs/developing.md",
    "chars": 5762,
    "preview": "# Development notes and procedures\n\nThis document contains an assortment of information for those who want to develop un"
  },
  {
    "path": "docs/flags.md",
    "chars": 10128,
    "preview": "# List of flags and switches\n\nThis is an exhaustive list of command-line switches and flags introduced by ungoogled-chro"
  },
  {
    "path": "docs/platforms.md",
    "chars": 944,
    "preview": "# Supported Platforms\n\nThis page lists platforms officially supported by ungoogled-chromium, and their associated reposi"
  },
  {
    "path": "docs/repo_management.md",
    "chars": 3704,
    "preview": "# Platform Repository Standards and Guidelines\n\n*This document is new, and its structure and content may change. If you "
  },
  {
    "path": "domain_regex.list",
    "chars": 1095,
    "preview": "fonts(\\\\*?)\\.googleapis(\\\\*?)\\.com#f0ntz\\g<1>.9oo91e8p1\\g<2>.qjz9zk\ngoogle([A-Za-z\\-]*?\\\\*?)\\.com(?!mon)#9oo91e\\g<1>.qjz"
  },
  {
    "path": "domain_substitution.list",
    "chars": 1160991,
    "preview": ".gemini/commands/PRESUBMIT.py\nBUILD.gn\nPRESUBMIT.py\nPRESUBMIT_test.py\nagents/PRESUBMIT.py\nagents/extensions/PRESUBMIT.py"
  },
  {
    "path": "downloads.ini",
    "chars": 556,
    "preview": "# Official Chromium source code archive\n# NOTE: Substitutions beginning with underscore are provided by utils\n[chromium]"
  },
  {
    "path": "flags.gn",
    "chars": 500,
    "preview": "build_with_tflite_lib=false\nchrome_pgo_phase=0\nclang_use_chrome_plugins=false\ndisable_fieldtrial_testing_config=true\nena"
  },
  {
    "path": "patches/core/bromite/disable-fetching-field-trials.patch",
    "chars": 3138,
    "preview": "# NOTE: Modified to remove usage of compiler #if macros\nFrom: csagan5 <32685696+csagan5@users.noreply.github.com>\nDate: "
  },
  {
    "path": "patches/core/inox-patchset/0001-fix-building-without-safebrowsing.patch",
    "chars": 13352,
    "preview": "--- a/chrome/browser/BUILD.gn\n+++ b/chrome/browser/BUILD.gn\n@@ -3749,8 +3749,6 @@ static_library(\"browser\") {\n       \"do"
  },
  {
    "path": "patches/core/inox-patchset/0003-disable-autofill-download-manager.patch",
    "chars": 4407,
    "preview": "--- a/components/autofill/core/browser/crowdsourcing/autofill_crowdsourcing_manager.cc\n+++ b/components/autofill/core/br"
  },
  {
    "path": "patches/core/inox-patchset/0005-disable-default-extensions.patch",
    "chars": 3478,
    "preview": "--- a/chrome/browser/extensions/component_extensions_allowlist/allowlist.cc\n+++ b/chrome/browser/extensions/component_ex"
  },
  {
    "path": "patches/core/inox-patchset/0015-disable-update-pings.patch",
    "chars": 376,
    "preview": "--- a/chrome/updater/configurator.cc\n+++ b/chrome/updater/configurator.cc\n@@ -133,7 +133,7 @@ base::TimeDelta Configurat"
  },
  {
    "path": "patches/core/inox-patchset/0021-disable-rlz.patch",
    "chars": 789,
    "preview": "# Disable rlz\n\n--- a/BUILD.gn\n+++ b/BUILD.gn\n@@ -456,14 +456,6 @@ group(\"gn_all\") {\n       ]\n     }\n \n-    if (is_chrome"
  },
  {
    "path": "patches/core/iridium-browser/all-add-trk-prefixes-to-possibly-evil-connections.patch",
    "chars": 33677,
    "preview": "From 06f6141610cb2aa562c94dbb9f1f4355e4b34c5d Mon Sep 17 00:00:00 2001\nFrom: Jan Engelhardt <jengelh@inai.de>\nDate: Mon,"
  },
  {
    "path": "patches/core/iridium-browser/safe_browsing-disable-incident-reporting.patch",
    "chars": 3831,
    "preview": "From 4dfa8ed0814040317cb82d8545502186daa0a204 Mon Sep 17 00:00:00 2001\nFrom: Jan Engelhardt <jengelh@inai.de>\nDate: Tue,"
  },
  {
    "path": "patches/core/iridium-browser/safe_browsing-disable-reporting-of-safebrowsing-over.patch",
    "chars": 1316,
    "preview": "From c89ce946e5328ca8a7df923d421e904bb6bfe9b6 Mon Sep 17 00:00:00 2001\nFrom: Jan Engelhardt <jengelh@inai.de>\nDate: Tue,"
  },
  {
    "path": "patches/core/ungoogled-chromium/block-requests.patch",
    "chars": 6885,
    "preview": "## Prevent request attempts\n# chrome://discards/ attempts to use d3 to display the graph\n# New tab page tries to downloa"
  },
  {
    "path": "patches/core/ungoogled-chromium/block-trk-and-subdomains.patch",
    "chars": 8995,
    "preview": "# Block all connection requests with 'qjz9zk' in the domain name or with a 'trk:' scheme.\n# This patch is based on Iridi"
  },
  {
    "path": "patches/core/ungoogled-chromium/build-with-wasm-rollup.patch",
    "chars": 4703,
    "preview": "--- a/third_party/devtools-frontend/src/front_end/Images/BUILD.gn\n+++ b/third_party/devtools-frontend/src/front_end/Imag"
  },
  {
    "path": "patches/core/ungoogled-chromium/disable-crash-reporter.patch",
    "chars": 1830,
    "preview": "# Disable some background communication with clients2.google.com\n\n--- a/components/crash/core/app/crash_reporter_client."
  },
  {
    "path": "patches/core/ungoogled-chromium/disable-domain-reliability.patch",
    "chars": 29173,
    "preview": "## Disable domain reliability component\n# Many of these changes are for thoroughness,\n# the most significant changes are"
  },
  {
    "path": "patches/core/ungoogled-chromium/disable-fonts-googleapis-references.patch",
    "chars": 2791,
    "preview": "# Disables references to fonts.googleapis.com\n\n--- a/components/dom_distiller/content/browser/dom_distiller_viewer_sourc"
  },
  {
    "path": "patches/core/ungoogled-chromium/disable-gaia.patch",
    "chars": 2631,
    "preview": "# Disables Gaia code\n# Somehow it is still activated even without being signed-in: https://github.com/ungoogled-software"
  },
  {
    "path": "patches/core/ungoogled-chromium/disable-gcm.patch",
    "chars": 5820,
    "preview": "# Disable Google Cloud Messaging (GCM) client\n\n--- a/components/gcm_driver/gcm_client_impl.cc\n+++ b/components/gcm_drive"
  },
  {
    "path": "patches/core/ungoogled-chromium/disable-google-host-detection.patch",
    "chars": 28585,
    "preview": "# Disables various detections of Google hosts and functionality specific to them\n\n--- a/chrome/browser/ui/webui/suggest_"
  },
  {
    "path": "patches/core/ungoogled-chromium/disable-mei-preload.patch",
    "chars": 1407,
    "preview": "# Disables use of a binary for preloading the Media Engagement index\n# Said binary is: chrome/browser/resources/media/me"
  },
  {
    "path": "patches/core/ungoogled-chromium/disable-network-time-tracker.patch",
    "chars": 734,
    "preview": "# Disable Network Time Tracker\n# This connects to Google to check if the system time is correct when a website certifica"
  },
  {
    "path": "patches/core/ungoogled-chromium/disable-privacy-sandbox.patch",
    "chars": 12312,
    "preview": "--- a/components/browsing_data/content/browsing_data_model.cc\n+++ b/components/browsing_data/content/browsing_data_model"
  },
  {
    "path": "patches/core/ungoogled-chromium/disable-profile-avatar-downloading.patch",
    "chars": 724,
    "preview": "# Stop downloading of profile avatar (trk:271:...)\n\n--- a/chrome/browser/profiles/profile_avatar_downloader.cc\n+++ b/chr"
  },
  {
    "path": "patches/core/ungoogled-chromium/disable-untraceable-urls.patch",
    "chars": 1084,
    "preview": "# Disable additional URLs that are not caught by the \"trk\" scheme\n\n--- a/rlz/lib/financial_ping.cc\n+++ b/rlz/lib/financi"
  },
  {
    "path": "patches/core/ungoogled-chromium/disable-webrtc-log-uploader.patch",
    "chars": 5281,
    "preview": "# Disables WebRTC log uploading to Google\n\n--- a/chrome/browser/media/webrtc/webrtc_log_uploader.cc\n+++ b/chrome/browser"
  },
  {
    "path": "patches/core/ungoogled-chromium/disable-webstore-urls.patch",
    "chars": 7588,
    "preview": "# Disables Chrome Webstore-related URLs and other internal functionality. Mainly for disabling auto updates via the Chro"
  },
  {
    "path": "patches/core/ungoogled-chromium/doh-changes.patch",
    "chars": 2968,
    "preview": "--- a/chrome/browser/net/default_dns_over_https_config_source.cc\n+++ b/chrome/browser/net/default_dns_over_https_config_"
  },
  {
    "path": "patches/core/ungoogled-chromium/extensions-manifestv2.patch",
    "chars": 6227,
    "preview": "--- a/chrome/browser/extensions/api/developer_private/extension_info_generator.cc\n+++ b/chrome/browser/extensions/api/de"
  },
  {
    "path": "patches/core/ungoogled-chromium/fix-building-with-prunned-binaries.patch",
    "chars": 85396,
    "preview": "# Contains tests and features that are unneeded and would otherwise\n# require binaries to be whitelisted in the pruning "
  },
  {
    "path": "patches/core/ungoogled-chromium/fix-building-without-safebrowsing.patch",
    "chars": 131438,
    "preview": "# Additional changes to Inox's fix-building-without-safebrowsing.patch\n\n--- a/chrome/browser/BUILD.gn\n+++ b/chrome/brows"
  },
  {
    "path": "patches/core/ungoogled-chromium/fix-learn-doubleclick-hsts.patch",
    "chars": 785,
    "preview": "# Split up the learn.doubleclick.net string literal to prevent domain substitution breaking compilation due to the use o"
  },
  {
    "path": "patches/core/ungoogled-chromium/move-js-optimizer-unfamiliar-sites.patch",
    "chars": 2397,
    "preview": "# This was added to the safebrowsing prefs in\n# https://chromium-review.googlesource.com/c/chromium/src/+/6898756\n# Move"
  },
  {
    "path": "patches/core/ungoogled-chromium/remove-f1-shortcut.patch",
    "chars": 511,
    "preview": "--- a/chrome/browser/ui/accelerator_table.cc\n+++ b/chrome/browser/ui/accelerator_table.cc\n@@ -133,7 +133,6 @@ const Acce"
  },
  {
    "path": "patches/core/ungoogled-chromium/remove-unused-preferences-fields.patch",
    "chars": 343362,
    "preview": "# Remove unused Safe Browsing and Sign-in fields from Preferences file\n# TODO: This patch should probably be split up an"
  },
  {
    "path": "patches/core/ungoogled-chromium/replace-google-search-engine-with-nosearch.patch",
    "chars": 2546,
    "preview": "--- a/third_party/search_engines_data/resources/definitions/prepopulated_engines.json\n+++ b/third_party/search_engines_d"
  },
  {
    "path": "patches/core/ungoogled-chromium/toggle-translation-via-switch.patch",
    "chars": 5309,
    "preview": "# Disables translation and removes the \"Translate to\" context menu when --translate-script-url flag is not set\n--- a/chr"
  },
  {
    "path": "patches/extra/bromite/fingerprinting-flags-client-rects-and-measuretext.patch",
    "chars": 15191,
    "preview": "# Adds two flags:\n# 1. --fingerprinting-client-rects-noise to enable fingerprinting deception for Range::getClientRects "
  },
  {
    "path": "patches/extra/bromite/flag-fingerprinting-canvas-image-data-noise.patch",
    "chars": 13601,
    "preview": "# NOTE: Changes made:\n# * Added flag --fingerprinting-canvas-image-data-noise to enable/disable\n#   Canvas image data fi"
  },
  {
    "path": "patches/extra/bromite/flag-max-connections-per-host.patch",
    "chars": 6348,
    "preview": "From: csagan5 <32685696+csagan5@users.noreply.github.com>\nDate: Sun, 8 Jul 2018 22:42:04 +0200\nSubject: Add flag to conf"
  },
  {
    "path": "patches/extra/debian/disable/google-api-warning.patch",
    "chars": 658,
    "preview": "description: disable the google api key warning when those aren't found\nauthor: Michael Gilbert <mgilbert@debian.org>\n\n-"
  },
  {
    "path": "patches/extra/inox-patchset/0006-modify-default-prefs.patch",
    "chars": 6899,
    "preview": "\n--- a/chrome/browser/background/extensions/background_mode_manager.cc\n+++ b/chrome/browser/background/extensions/backgr"
  },
  {
    "path": "patches/extra/inox-patchset/0008-restore-classic-ntp.patch",
    "chars": 1396,
    "preview": "--- a/chrome/browser/search/search.cc\n+++ b/chrome/browser/search/search.cc\n@@ -177,33 +177,8 @@ struct NewTabURLDetails"
  },
  {
    "path": "patches/extra/inox-patchset/0013-disable-missing-key-warning.patch",
    "chars": 495,
    "preview": "--- a/chrome/browser/ui/startup/google_api_keys_infobar_delegate.cc\n+++ b/chrome/browser/ui/startup/google_api_keys_info"
  },
  {
    "path": "patches/extra/inox-patchset/0016-chromium-sandbox-pie.patch",
    "chars": 270,
    "preview": "--- a/sandbox/linux/BUILD.gn\n+++ b/sandbox/linux/BUILD.gn\n@@ -298,6 +298,14 @@ if (is_linux || is_chromeos) {\n       \"su"
  },
  {
    "path": "patches/extra/inox-patchset/0019-disable-battery-status-service.patch",
    "chars": 3488,
    "preview": "--- a/services/device/battery/battery_status_service.cc\n+++ b/services/device/battery/battery_status_service.cc\n@@ -22,1"
  },
  {
    "path": "patches/extra/iridium-browser/Remove-EV-certificates.patch",
    "chars": 1173,
    "preview": "From d32e222a2706cb59f9855b9cf4330f88d1af5435 Mon Sep 17 00:00:00 2001\nFrom: Jan Engelhardt <jengelh@inai.de>\nDate: Thu,"
  },
  {
    "path": "patches/extra/iridium-browser/browser-disable-profile-auto-import-on-first-run.patch",
    "chars": 1852,
    "preview": "From 7134d5fd762237ad2d80093b68ccbd1582476640 Mon Sep 17 00:00:00 2001\nFrom: Joachim Bauch <jojo@struktur.de>\nDate: Thu,"
  },
  {
    "path": "patches/extra/iridium-browser/mime_util-force-text-x-suse-ymp-to-be-downloaded.patch",
    "chars": 783,
    "preview": "From d3dcad96b3c2091026c3a81054bb3ce56538a702 Mon Sep 17 00:00:00 2001\nFrom: Jan Engelhardt <jengelh@inai.de>\nDate: Thu,"
  },
  {
    "path": "patches/extra/iridium-browser/prefs-always-prompt-for-download-directory-by-defaul.patch",
    "chars": 1778,
    "preview": "From 93010fd16c1c9f01a06eab18055bcab54b028cc8 Mon Sep 17 00:00:00 2001\nFrom: Jan Engelhardt <jengelh@inai.de>\nDate: Fri,"
  },
  {
    "path": "patches/extra/iridium-browser/updater-disable-auto-update.patch",
    "chars": 1233,
    "preview": "From f97af1715c10c5926169ff317ca7c91f1d073af9 Mon Sep 17 00:00:00 2001\nFrom: Jan Engelhardt <jengelh@inai.de>\nDate: Fri,"
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-components-ungoogled.patch",
    "chars": 2164,
    "preview": "# Add ungoogled-chromium-specific code to components/ungoogled/\n\n--- /dev/null\n+++ b/components/ungoogled/BUILD.gn\n@@ -0"
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-credits.patch",
    "chars": 3497,
    "preview": "--- a/components/webui/about/resources/about_credits_reciprocal.tmpl\n+++ b/components/webui/about/resources/about_credit"
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-extra-channel-info.patch",
    "chars": 779,
    "preview": "description: change extra channel info to \"Ungoogled Chromium\"\nauthor: Wengling Chen <feiyu2817@gmail.com>\n\n---\n compone"
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-flag-for-bookmark-bar-ntp.patch",
    "chars": 1763,
    "preview": "--- a/chrome/browser/ui/bookmarks/bookmark_bar_controller.cc\n+++ b/chrome/browser/ui/bookmarks/bookmark_bar_controller.c"
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-flag-for-close-confirmation.patch",
    "chars": 8364,
    "preview": "--- a/chrome/browser/ui/browser.cc\n+++ b/chrome/browser/ui/browser.cc\n@@ -140,6 +140,7 @@\n #include \"chrome/browser/ui/v"
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-flag-for-custom-ntp.patch",
    "chars": 2427,
    "preview": "--- a/chrome/browser/chrome_content_browser_client.cc\n+++ b/chrome/browser/chrome_content_browser_client.cc\n@@ -815,11 +"
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-flag-for-disabling-link-drag.patch",
    "chars": 3417,
    "preview": "--- a/chrome/browser/ungoogled_flag_entries.h\n+++ b/chrome/browser/ungoogled_flag_entries.h\n@@ -100,4 +100,8 @@\n      \"D"
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-flag-for-grab-handle.patch",
    "chars": 1074,
    "preview": "--- a/chrome/browser/ui/views/frame/horizontal_tab_strip_region_view.cc\n+++ b/chrome/browser/ui/views/frame/horizontal_t"
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-flag-for-incognito-themes.patch",
    "chars": 2130,
    "preview": "--- a/chrome/browser/ui/views/frame/browser_widget.cc\n+++ b/chrome/browser/ui/views/frame/browser_widget.cc\n@@ -609,6 +6"
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-flag-for-omnibox-autocomplete-filtering.patch",
    "chars": 4953,
    "preview": "--- a/chrome/browser/ungoogled_flag_choices.h\n+++ b/chrome/browser/ungoogled_flag_choices.h\n@@ -40,4 +40,19 @@ const Fea"
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-flag-for-qr-generator.patch",
    "chars": 1877,
    "preview": "--- a/chrome/browser/ui/qrcode_generator/qrcode_generator_bubble_controller.cc\n+++ b/chrome/browser/ui/qrcode_generator/"
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-flag-for-search-engine-collection.patch",
    "chars": 5255,
    "preview": "# Add flag to disable automatic search engine collection\n\n--- a/chrome/browser/ui/tab_helpers.cc\n+++ b/chrome/browser/ui"
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-flag-for-tab-hover-cards.patch",
    "chars": 3517,
    "preview": "--- a/chrome/browser/ui/views/tabs/tab.cc\n+++ b/chrome/browser/ui/views/tabs/tab.cc\n@@ -12,6 +12,7 @@\n #include <memory>"
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-flag-for-tabsearch-button.patch",
    "chars": 3107,
    "preview": "--- a/chrome/browser/ui/browser_command_controller.cc\n+++ b/chrome/browser/ui/browser_command_controller.cc\n@@ -1723,7 +"
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-flag-to-change-http-accept-header.patch",
    "chars": 2499,
    "preview": "--- a/chrome/browser/ungoogled_flag_entries.h\n+++ b/chrome/browser/ungoogled_flag_entries.h\n@@ -92,4 +92,8 @@\n      \"Dis"
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-flag-to-clear-data-on-exit.patch",
    "chars": 3961,
    "preview": "--- a/chrome/browser/browser_features.cc\n+++ b/chrome/browser/browser_features.cc\n@@ -265,4 +265,5 @@ BASE_FEATURE(kRepo"
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-flag-to-close-window-with-last-tab.patch",
    "chars": 2689,
    "preview": "--- a/chrome/browser/ui/tabs/tab_strip_model.cc\n+++ b/chrome/browser/ui/tabs/tab_strip_model.cc\n@@ -18,6 +18,7 @@\n \n #in"
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-flag-to-configure-extension-downloading.patch",
    "chars": 5056,
    "preview": "# Add extension-mime-request-handling chrome://flag to tweak the behavior of\n# extension MIME types\n\n--- a/chrome/browse"
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-flag-to-convert-popups-to-tabs.patch",
    "chars": 2618,
    "preview": "# Make popups go to tabs instead\n\n--- a/chrome/browser/ungoogled_flag_entries.h\n+++ b/chrome/browser/ungoogled_flag_entr"
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-flag-to-disable-beforeunload.patch",
    "chars": 1540,
    "preview": "# Add --disable-beforeunload to always disable beforeunload JavaScript dialogs\n\n--- a/chrome/browser/ungoogled_flag_entr"
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-flag-to-disable-local-history-expiration.patch",
    "chars": 1528,
    "preview": "# Keep local history longer than 90 days\n\n--- a/chrome/browser/ungoogled_flag_entries.h\n+++ b/chrome/browser/ungoogled_f"
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-flag-to-disable-sharing-hub.patch",
    "chars": 1302,
    "preview": "--- a/chrome/browser/sharing_hub/sharing_hub_features.cc\n+++ b/chrome/browser/sharing_hub/sharing_hub_features.cc\n@@ -4,"
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-flag-to-disable-tls-grease.patch",
    "chars": 1415,
    "preview": "--- a/chrome/browser/ungoogled_flag_entries.h\n+++ b/chrome/browser/ungoogled_flag_entries.h\n@@ -88,4 +88,8 @@\n      \"Hid"
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-flag-to-force-punycode-hostnames.patch",
    "chars": 1567,
    "preview": "# Add flag to force punycode in hostnames instead of Unicode when displaying Internationalized Domain Names (IDNs) to mi"
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-flag-to-hide-crashed-bubble.patch",
    "chars": 1306,
    "preview": "# Add flag --hide-crashed-bubble to hide the bubble box:\n# \"Restore Pages? Chromium didn't shut down correctly.\"\n\n--- a/"
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-flag-to-hide-extensions-menu.patch",
    "chars": 2587,
    "preview": "--- a/chrome/browser/ui/views/extensions/extensions_toolbar_desktop.cc\n+++ b/chrome/browser/ui/views/extensions/extensio"
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-flag-to-hide-fullscreen-exit-ui.patch",
    "chars": 1896,
    "preview": "--- a/chrome/browser/ui/views/frame/browser_view.cc\n+++ b/chrome/browser/ui/views/frame/browser_view.cc\n@@ -745,6 +745,9"
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-flag-to-hide-tab-close-buttons.patch",
    "chars": 1466,
    "preview": "--- a/chrome/browser/ui/views/tabs/tab.cc\n+++ b/chrome/browser/ui/views/tabs/tab.cc\n@@ -1192,7 +1192,11 @@ void Tab::Upd"
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-flag-to-increase-incognito-storage-quota.patch",
    "chars": 2944,
    "preview": "--- a/chrome/browser/ungoogled_flag_entries.h\n+++ b/chrome/browser/ungoogled_flag_entries.h\n@@ -144,4 +144,8 @@\n      \"S"
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-flag-to-reduce-system-info.patch",
    "chars": 2986,
    "preview": "--- a/chrome/browser/ungoogled_flag_entries.h\n+++ b/chrome/browser/ungoogled_flag_entries.h\n@@ -128,4 +128,8 @@\n      \"M"
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-flag-to-remove-client-hints.patch",
    "chars": 4635,
    "preview": "--- a/chrome/browser/ungoogled_flag_entries.h\n+++ b/chrome/browser/ungoogled_flag_entries.h\n@@ -132,4 +132,8 @@\n      \"R"
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-flag-to-scroll-tabs.patch",
    "chars": 3326,
    "preview": "--- a/chrome/browser/ui/views/frame/browser_root_view.cc\n+++ b/chrome/browser/ui/views/frame/browser_root_view.cc\n@@ -12"
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-flag-to-show-avatar-button.patch",
    "chars": 2136,
    "preview": "--- a/chrome/browser/ui/views/toolbar/toolbar_view.cc\n+++ b/chrome/browser/ui/views/toolbar/toolbar_view.cc\n@@ -447,6 +4"
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-flag-to-spoof-webgl-renderer-info.patch",
    "chars": 5897,
    "preview": "# Return generic values for WebGLDebugRendererInfo to remove a potential data\n# leak while preventing potential website "
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-flags-for-existing-switches.patch",
    "chars": 3917,
    "preview": "--- a/chrome/browser/about_flags.cc\n+++ b/chrome/browser/about_flags.cc\n@@ -4675,10 +4675,12 @@ const FeatureEntry::Feat"
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-flags-for-referrer-customization.patch",
    "chars": 13355,
    "preview": "--- a/chrome/browser/ungoogled_flag_entries.h\n+++ b/chrome/browser/ungoogled_flag_entries.h\n@@ -116,4 +116,16 @@\n      \""
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-ipv6-probing-option.patch",
    "chars": 3499,
    "preview": "# Disables IPv6 probing and adds an option to change the IPv6 probing result\n\n--- a/chrome/browser/ungoogled_flag_entrie"
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-suggestions-url-field.patch",
    "chars": 20480,
    "preview": "# Add suggestions URL text field to the search engine editing dialog\n# (chrome://settings/searchEngines).\n\n--- a/chrome/"
  },
  {
    "path": "patches/extra/ungoogled-chromium/add-ungoogled-flag-headers.patch",
    "chars": 3297,
    "preview": "--- a/chrome/browser/about_flags.cc\n+++ b/chrome/browser/about_flags.cc\n@@ -4671,7 +4671,13 @@ const FeatureEntry::Featu"
  },
  {
    "path": "patches/extra/ungoogled-chromium/default-webrtc-ip-handling-policy.patch",
    "chars": 642,
    "preview": "--- a/chrome/browser/ui/browser_ui_prefs.cc\n+++ b/chrome/browser/ui/browser_ui_prefs.cc\n@@ -142,7 +142,7 @@ void Registe"
  },
  {
    "path": "patches/extra/ungoogled-chromium/disable-ai-search-shortcuts.patch",
    "chars": 2962,
    "preview": "--- a/chrome/browser/ui/search_engines/template_url_table_model.cc\n+++ b/chrome/browser/ui/search_engines/template_url_t"
  },
  {
    "path": "patches/extra/ungoogled-chromium/disable-chromelabs.patch",
    "chars": 1343,
    "preview": "--- a/chrome/browser/ui/toolbar/chrome_labs/chrome_labs_utils.cc\n+++ b/chrome/browser/ui/toolbar/chrome_labs/chrome_labs"
  },
  {
    "path": "patches/extra/ungoogled-chromium/disable-dial-repeating-discovery.patch",
    "chars": 885,
    "preview": "# Disables the dial registry's repeating discovery timer\n# This caused unnecessary SSDP network spam\n\n--- a/chrome/brows"
  },
  {
    "path": "patches/extra/ungoogled-chromium/disable-download-quarantine.patch",
    "chars": 10835,
    "preview": "# Disables file download quarantining\n\n--- a/components/download/internal/common/base_file.cc\n+++ b/components/download/"
  },
  {
    "path": "patches/extra/ungoogled-chromium/disable-fedcm-by-default.patch",
    "chars": 643,
    "preview": "# Disable Federated Credential Management (FedCM) by default\n\n--- a/content/public/common/content_features.cc\n+++ b/cont"
  },
  {
    "path": "patches/extra/ungoogled-chromium/disable-formatting-in-omnibox.patch",
    "chars": 899,
    "preview": "# Disables omission of URL elements in Omnibox and status bubble\n\n--- a/chrome/browser/ui/status_bubble.h\n+++ b/chrome/b"
  },
  {
    "path": "patches/extra/ungoogled-chromium/disable-intranet-redirect-detector.patch",
    "chars": 704,
    "preview": "# Disables the intranet redirect detector. It generates extra DNS requests and the functionality using this is disabled\n"
  },
  {
    "path": "patches/extra/ungoogled-chromium/enable-certificate-transparency-and-add-flag.patch",
    "chars": 1143,
    "preview": "--- a/chrome/browser/browser_features.cc\n+++ b/chrome/browser/browser_features.cc\n@@ -49,7 +49,7 @@ BASE_FEATURE(kBookma"
  },
  {
    "path": "patches/extra/ungoogled-chromium/enable-extra-locales.patch",
    "chars": 8165,
    "preview": "# Enable extra locales not normally shipped with Chromium on desktop\n\n--- a/build/config/locales.gni\n+++ b/build/config/"
  },
  {
    "path": "patches/extra/ungoogled-chromium/enable-menu-on-reload-button.patch",
    "chars": 462,
    "preview": "# Add ability to always open dev context menu on reload button.\n\n--- a/chrome/browser/ui/views/toolbar/reload_button.cc\n"
  },
  {
    "path": "patches/extra/ungoogled-chromium/enable-page-saving-on-more-pages.patch",
    "chars": 2332,
    "preview": "# Add more URL schemes allowed for saving\n\n--- a/chrome/browser/ui/browser_commands.cc\n+++ b/chrome/browser/ui/browser_c"
  },
  {
    "path": "patches/extra/ungoogled-chromium/enable-paste-and-go-new-tab-button.patch",
    "chars": 5007,
    "preview": "--- a/chrome/browser/ui/views/frame/horizontal_tab_strip_region_view.cc\n+++ b/chrome/browser/ui/views/frame/horizontal_t"
  },
  {
    "path": "patches/extra/ungoogled-chromium/first-run-page.patch",
    "chars": 9879,
    "preview": "--- a/chrome/browser/chrome_browser_main.cc\n+++ b/chrome/browser/chrome_browser_main.cc\n@@ -1234,6 +1234,7 @@ int Chrome"
  },
  {
    "path": "patches/extra/ungoogled-chromium/fix-building-without-mdns-and-service-discovery.patch",
    "chars": 1490,
    "preview": "# Fix building with enable_service_discovery=false and enable_mds=false\n\n--- a/chrome/browser/media/router/discovery/mdn"
  },
  {
    "path": "patches/extra/ungoogled-chromium/fix-distilled-icons.patch",
    "chars": 364,
    "preview": "--- a/components/dom_distiller/core/css/distilledpage_common.css\n+++ b/components/dom_distiller/core/css/distilledpage_c"
  },
  {
    "path": "patches/extra/ungoogled-chromium/keep-expired-flags.patch",
    "chars": 1542,
    "preview": "--- a/components/webui/flags/flags_state.cc\n+++ b/components/webui/flags/flags_state.cc\n@@ -26,6 +26,7 @@\n #include \"bas"
  },
  {
    "path": "patches/extra/ungoogled-chromium/prepopulated-search-engines.patch",
    "chars": 600,
    "preview": "# Disables Site search Starter Pack\n#\n--- a/components/omnibox/common/omnibox_features.cc\n+++ b/components/omnibox/commo"
  },
  {
    "path": "patches/extra/ungoogled-chromium/remove-disable-setuid-sandbox-as-bad-flag.patch",
    "chars": 579,
    "preview": "# Remove the \"--disable-setuid-sandbox\" command line flag as a bad flag\n\n--- a/chrome/browser/ui/startup/bad_flags_promp"
  },
  {
    "path": "patches/extra/ungoogled-chromium/remove-pac-size-limit.patch",
    "chars": 483,
    "preview": "--- a/net/proxy_resolution/pac_file_fetcher_impl.cc\n+++ b/net/proxy_resolution/pac_file_fetcher_impl.cc\n@@ -359,13 +359,"
  },
  {
    "path": "patches/extra/ungoogled-chromium/remove-uneeded-ui.patch",
    "chars": 31308,
    "preview": "# Removes in order:\n# 'Learn more' links on the performance page\n# more 'Learn more' links on the performance page\n# lin"
  },
  {
    "path": "patches/series",
    "chars": 6617,
    "preview": "upstream-fixes/missing-dependencies.patch\n\ncore/inox-patchset/0001-fix-building-without-safebrowsing.patch\ncore/inox-pat"
  },
  {
    "path": "patches/upstream-fixes/missing-dependencies.patch",
    "chars": 3608,
    "preview": "--- a/chrome/browser/chrome_content_browser_client.cc\n+++ b/chrome/browser/chrome_content_browser_client.cc\n@@ -329,6 +3"
  },
  {
    "path": "pruning.list",
    "chars": 1089641,
    "preview": "ash/display/test_data/06af5c10.icc\nash/display/test_data/0dae3211.icc\nash/display/test_data/4c834a42.icc\nash/webui/camer"
  },
  {
    "path": "revision.txt",
    "chars": 2,
    "preview": "1\n"
  },
  {
    "path": "shell.nix",
    "chars": 175,
    "preview": "{ pkgs ? import <nixpkgs> {} }: pkgs.mkShell {\n  nativeBuildInputs = with pkgs; [\n    quilt\n\n    (python3.withPackages ("
  },
  {
    "path": "utils/.coveragerc",
    "chars": 498,
    "preview": "[run]\nbranch = True\nparallel = True\nomit = tests/*\n\n[report]\n# Regexes for lines to exclude from consideration\nexclude_l"
  },
  {
    "path": "utils/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "utils/_common.py",
    "chars": 4800,
    "preview": "# -*- coding: UTF-8 -*-\n\n# Copyright (c) 2020 The ungoogled-chromium Authors. All rights reserved.\n# Use of this source "
  },
  {
    "path": "utils/_extraction.py",
    "chars": 13740,
    "preview": "# -*- coding: UTF-8 -*-\n\n# Copyright (c) 2019 The ungoogled-chromium Authors. All rights reserved.\n# Use of this source "
  },
  {
    "path": "utils/clone.py",
    "chars": 11422,
    "preview": "#!/usr/bin/env python3\n# -*- coding: UTF-8 -*-\n\n# Copyright (c) 2023 The ungoogled-chromium Authors. All rights reserved"
  },
  {
    "path": "utils/depot_tools.patch",
    "chars": 5607,
    "preview": "# Changes to gclient that:\n#   use system python on windows\n#   move dotfiles into the staging directory\n#   skip cipd b"
  },
  {
    "path": "utils/domain_substitution.py",
    "chars": 15401,
    "preview": "#!/usr/bin/env python3\n# -*- coding: UTF-8 -*-\n\n# Copyright (c) 2019 The ungoogled-chromium Authors. All rights reserved"
  },
  {
    "path": "utils/downloads.py",
    "chars": 20473,
    "preview": "#!/usr/bin/env python3\n# -*- coding: UTF-8 -*-\n\n# Copyright (c) 2019 The ungoogled-chromium Authors. All rights reserved"
  },
  {
    "path": "utils/filescfg.py",
    "chars": 8896,
    "preview": "#!/usr/bin/env python3\n# -*- coding: UTF-8 -*-\n\n# Copyright (c) 2019 The ungoogled-chromium Authors. All rights reserved"
  },
  {
    "path": "utils/gsutil.patch",
    "chars": 1659,
    "preview": "# Changes to gsutil that:\n#   allow usage of python 3.14\n--- a/gslib/commands/cp.py\n+++ b/gslib/commands/cp.py\n@@ -1043,"
  },
  {
    "path": "utils/make_domsub_script.py",
    "chars": 4025,
    "preview": "#!/usr/bin/env python3\n# -*- coding: UTF-8 -*-\n\n# Copyright (c) 2023 The ungoogled-chromium Authors. All rights reserved"
  },
  {
    "path": "utils/patches.py",
    "chars": 9830,
    "preview": "#!/usr/bin/env python3\n# -*- coding: UTF-8 -*-\n\n# Copyright (c) 2020 The ungoogled-chromium Authors. All rights reserved"
  },
  {
    "path": "utils/prune_binaries.py",
    "chars": 10441,
    "preview": "#!/usr/bin/env python3\n# -*- coding: UTF-8 -*-\n\n# Copyright (c) 2019 The ungoogled-chromium Authors. All rights reserved"
  },
  {
    "path": "utils/pytest.ini",
    "chars": 247,
    "preview": "[pytest]\ntestpaths = tests\n#filterwarnings =\n#\terror\n#\tignore::DeprecationWarning\n#addopts = --cov-report term-missing -"
  },
  {
    "path": "utils/tests/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "utils/tests/test_domain_substitution.py",
    "chars": 1230,
    "preview": "# -*- coding: UTF-8 -*-\n\n# Copyright (c) 2019 The ungoogled-chromium Authors. All rights reserved.\n# Use of this source "
  },
  {
    "path": "utils/tests/test_patches.py",
    "chars": 1155,
    "preview": "# -*- coding: UTF-8 -*-\n\n# Copyright (c) 2020 The ungoogled-chromium Authors. All rights reserved.\n# Use of this source "
  },
  {
    "path": "utils/third_party/README.md",
    "chars": 189,
    "preview": "This directory contains third-party libraries used by build utilities.\n\nContents:\n\n* [schema](https://github.com/keleshe"
  },
  {
    "path": "utils/third_party/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "utils/third_party/schema.py",
    "chars": 14147,
    "preview": "\"\"\"schema is a library for validating Python data structures, such as those\nobtained from config-files, forms, external "
  }
]

About this extraction

This page contains the full source code of the ungoogled-software/ungoogled-chromium GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 197 files (3.4 MB), approximately 911.0k tokens, and a symbol index with 253 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!