Skip to main content

Overview

Helium uses a patch-based development model inherited from ungoogled-chromium. Instead of maintaining a forked Chromium repository, all modifications are stored as unified diff patches in GNU Quilt format.

Why Patches?

Upstream Compatibility

Easy to rebase on new Chromium versions by reapplying patches

Clear Attribution

Each patch is clearly sourced (ungoogled-chromium, Brave, Helium, etc.)

Modular Changes

Individual features can be enabled/disabled by including/excluding patches

Review-Friendly

Changes are isolated and easy to review individually

Patch Directory Structure

Patches are organized by source and category:
patches/
├── series                          # Defines patch application order
├── upstream-fixes/                 # Backported fixes from Chromium
│   └── vertical/                   # Vertical tabs feature patches
├── inox-patchset/                  # Privacy patches from Inox
├── iridium-browser/                # Privacy patches from Iridium
├── ungoogled-chromium/             # Core degoogling patches
├── bromite/                        # Fingerprinting protection
├── brave/                          # Import and tab features
├── debian/                         # Debian-specific patches
└── helium/
    ├── core/                       # Core Helium functionality
    │   ├── search/                 # Search engine configuration
    │   └── noise/                  # Fingerprinting noise
    ├── settings/                   # Settings page modifications
    ├── hop/                        # Password manager replacement
    └── ui/                         # User interface changes
        └── layout/                 # Layout system changes

The Series File

The patches/series file defines the order in which patches are applied:
# Upstream fixes first
upstream-fixes/missing-dependencies.patch
upstream-fixes/vertical/r1568708-fix-crash-during-collapsed-tabgroup-drag.patch
...

# Then third-party privacy patches
inox-patchset/fix-building-without-safebrowsing.patch
inox-patchset/disable-autofill-download-manager.patch
...

# Finally Helium patches
helium/core/add-zen-importer.patch
helium/core/services-prefs.patch
...
Patch order matters! Later patches may depend on earlier ones. Always maintain the order defined in series.

Applying Patches

Use the patches.py utility to apply patches:

Basic Application

# Apply all patches from patches/series
python3 utils/patches.py apply chromium patches

Advanced Options

# Apply without fuzz (fail if patches don't apply exactly)
python3 utils/patches.py apply chromium patches --no-fuzz

Dry Run Testing

Test if patches will apply cleanly without modifying files:
from pathlib import Path
from utils.patches import dry_run_check

patch_path = Path('patches/helium/core/services-prefs.patch')
tree_path = Path('chromium')

code, stdout, stderr = dry_run_check(patch_path, tree_path)
if code == 0:
    print("Patch will apply cleanly")
else:
    print(f"Patch failed: {stderr}")

Creating New Patches

1

Make Changes

Edit files in the Chromium source tree:
cd chromium
# Make your changes to source files
vim src/chrome/browser/ui/browser.cc
2

Generate Diff

Create a unified diff of your changes:
# From inside the chromium directory
git diff > ../patches/helium/core/my-new-feature.patch
3

Add to Series

Add your patch to patches/series in the appropriate location:
# Edit patches/series
vim ../patches/series

# Add your patch line:
# helium/core/my-new-feature.patch
4

Test Application

Verify your patch applies cleanly:
cd ..
# Reset changes
cd chromium
git checkout .
cd ..

# Apply all patches including your new one
python3 utils/patches.py apply chromium patches
5

Build and Test

Build Helium and test your changes:
cd chromium
ninja -C out/Default chrome
./out/Default/chrome

Patch File Format

Patches use the unified diff format:
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -150,6 +150,10 @@ Browser::Browser(const CreateParams& params)
       tab_strip_model_delegate_(std::make_unique<BrowserTabStripModelDelegate>(this)),
       tab_menu_model_delegate_(std::make_unique<BrowserTabMenuModelDelegate>(this)) {
   
+  // Helium: Set custom window title
+  if (params.type == TYPE_NORMAL) {
+    override_window_title_ = "Helium";
+  }
   
   // Set up the app_controller_ before calling Init() so that it will be available
   // for any code that runs during initialization.

Patch Header Format

--- a/path/to/file.cc    # Original file path (a/ prefix)
+++ b/path/to/file.cc    # Modified file path (b/ prefix)
@@ -150,6 +150,10 @@     # Hunk header: line numbers and context
  • @@ -150,6 means original file, starting at line 150, showing 6 lines
  • +150,10 means modified file, starting at line 150, showing 10 lines
  • Lines starting with - are removed
  • Lines starting with + are added
  • Lines starting with space are context (unchanged)

Modifying Existing Patches

1

Apply Patches Up To Target

Apply all patches before the one you want to modify:
# Manually apply patches in order until the one before yours
# Or create a temporary series file
head -n 100 patches/series > patches/series.tmp
python3 utils/patches.py apply chromium patches/series.tmp
2

Make Changes

Edit the files affected by the patch you want to modify
3

Regenerate Patch

Create an updated patch:
cd chromium
git diff > ../patches/helium/core/my-patch.patch
4

Verify

Test that all patches still apply in order:
cd chromium
git checkout .  # Reset all changes
cd ..
python3 utils/patches.py apply chromium patches
When modifying patches, ensure subsequent patches don’t conflict with your changes. You may need to update multiple patches if they affect the same files.

Patch Categories Explained

Upstream Fixes (48 patches)

Backported features from newer Chromium versions:
upstream-fixes/vertical/               # Vertical tabs implementation
├── r1568708-fix-crash-during-collapsed-tabgroup-drag.patch
├── r1570045-improve-dragging-between-groups-and-unpinned.patch
└── ... (45 more patches)
These patches are prefixed with rXXXXXXX indicating the Chromium revision they came from.

ungoogled-chromium (46 patches)

Core privacy and degoogling features:
disable-crash-reporter.patch
disable-google-host-detection.patch
disable-gcm.patch
disable-domain-reliability.patch
block-trk-and-subdomains.patch
Disable Google services and tracking connections.
disable-gaia.patch                    # No Google account sync
disable-profile-avatar-downloading.patch
disable-privacy-sandbox.patch
disable-webrtc-log-uploader.patch
Disable features that compromise privacy.
add-components-ungoogled.patch
add-ipv6-probing-option.patch
add-flag-to-configure-extension-downloading.patch
add-flag-for-search-engine-collection.patch
add-flag-to-force-punycode-hostnames.patch
Add command-line flags for privacy customization.

Helium Core (85 patches)

Helium-specific functionality:
helium/core/services-prefs.patch           # Helium Services integration
helium/core/onboarding-page.patch          # Welcome page
helium/core/change-chromium-branding.patch # Rebrand to Helium
helium/core/ublock-install-as-component.patch
helium/core/ublock-helium-services.patch

Helium UI (86 patches)

User interface modifications:
helium/ui/
├── layout/                       # Core layout system
│   ├── core.patch
│   ├── settings.patch
│   ├── compact.patch
│   └── vertical.patch
├── location-bar.patch            # Address bar design
├── omnibox.patch                 # Omnibox functionality
├── tabs.patch                    # Tab styling
├── toolbar.patch                 # Toolbar design
├── app-menu-*.patch              # Application menu
└── helium-color-scheme.patch     # Color theme

Validating Patches

Use the development utilities to validate patches:
# Validate all patches apply cleanly
./devutils/validate_patches.py -l chromium -v

# Check patches follow style guidelines
python3 -m pylint patches/*.py

Merging Patch Directories

Combine patches from multiple sources:
# Merge two patch directories
python3 utils/patches.py merge \
  destination_patches \
  source1_patches \
  source2_patches

# Prepend patches to existing directory
python3 utils/patches.py merge \
  --prepend \
  existing_patches \
  new_patches

Best Practices

Keep Patches Small

Each patch should implement one logical change. Easier to review, debug, and maintain.

Document Changes

Add comments in patch files explaining why changes were made:
+  // Helium: Disable password manager
+  // We use our own password management solution
+  return false;

Test Patch Order

After adding/modifying patches, always test that all patches apply cleanly in series order.

Attribution

If a patch is based on work from another project, note it in the patch filename or header.

Troubleshooting

Patch Doesn’t Apply

# Check which files are conflicting
python3 utils/patches.py apply chromium patches --no-fuzz

# Look for:
# "Hunk #1 FAILED at line 150"
# This means the code context changed
Solutions:
  1. Manually update the patch context to match current code
  2. Regenerate the patch from scratch
  3. Check if upstream Chromium changed the affected code

Patches Apply But Build Fails

# Check if all required patches are present
grep "^helium/core/" patches/series

# Verify patch order
cat patches/series | grep -A5 -B5 "your-patch.patch"
Common causes:
  • Patch depends on another patch that wasn’t applied
  • Patch conflicts with a later patch
  • Build configuration doesn’t match patched code

Fuzz Warnings

patching file chrome/browser/ui/browser.cc
Hunk #1 succeeded at 152 (offset 2 lines).
Fuzz means the patch applied but line numbers didn’t match exactly. Usually safe, but verify the changes are correct.

CI Patch Validation

Helium’s CI automatically validates patches:
# From .cirrus.yml
validate_patches_script:
  - ./devutils/validate_patches.py -l chromium_src -v
This ensures:
  • All patches in series file exist
  • Patches apply cleanly to the target Chromium version
  • No syntax errors in patch files
  • Correct file paths in patches

Next Steps

Development Overview

Learn about Helium’s overall architecture

Building Helium

Build Helium with your modified patches

Build docs developers (and LLMs) love