cef Documentation

Chromium Embedded Framework (CEF). A simple framework for embedding Chromium-based browsers in other applications.

4,838 stars C++ C++ 29 files · ~127,836 tokens #browser#cef#chromium#framework
RAW Doc

Technical Documentation: chromiumembedded/cef

ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference

Overview (`README.md`)

The Chromium Embedded Framework (CEF) is a simple framework for embedding Chromium-based browsers in other applications.

Introduction

CEF is a BSD-licensed open source project founded by Marshall Greenblatt in 2008 and based on the Google Chromium project. Unlike the Chromium project itself, which focuses mainly on Google Chrome application development, CEF focuses on facilitating embedded browser use cases in third-party applications. CEF insulates the user from the underlying Chromium and Blink code complexity by offering production-quality stable APIs, release branches tracking specific Chromium releases, and binary distributions. Most features in CEF have default implementations that provide rich functionality while requiring little or no integration work from the user. There are currently over 100 million installed instances of CEF around the world embedded in products from a wide range of companies and industries. A partial list of companies and products using CEF is available on the CEF Wikipedia page. Some use cases for CEF include:

  • Embedding an HTML5-compliant Web browser control in an existing native application.
  • Creating a light-weight native “shell” application that hosts a user interface developed primarily using Web technologies.
  • Rendering Web content “off-screen” in applications that have their own custom drawing frameworks.
  • Acting as a host for automated testing of existing Web properties and applications.

CEF supports a wide range of programming languages and operating systems and can be easily integrated into both new and existing applications. It was designed from the ground up with both performance and ease of use in mind. The base framework includes C and C++ programming interfaces exposed via native libraries that insulate the host application from Chromium and Blink implementation details. It provides close integration between the browser and the host application including support for custom plugins, protocols, JavaScript objects and JavaScript extensions. The host application can optionally control resource loading, navigation, context menus, printing and more, while taking advantage of the same performance and HTML5 technologies available in the Google Chrome Web browser.

Numerous individuals and organizations contribute time and resources to support CEF development, but more involvement from the community is always welcome. This includes support for both the core CEF project and external projects that integrate CEF with additional programming languages and frameworks (see the "External Projects" section below). If you are interested in donating time to help with CEF development please see the "Helping Out" section below. If you are interested in donating money to support general CEF development and infrastructure efforts please visit the CEF Sponsors page.

Getting Started

Users new to CEF development should start by reading the Tutorial for an overview of CEF usage and then proceed to the General Usage page for a more in-depth discussion of architectural and usage issues. Complete API documentation is available here. CEF support and related discussion is available on the CEF Forum.

Binary Distributions

Binary distributions, which include all files necessary to build a CEF-based application, are available on the Downloads page. Binary distributions are stand-alone and do not require the download of CEF or Chromium source code. Symbol files for debugging binary distributions of libcef can also be downloaded from the above links.

Source Distributions

The CEF project is an extension of the Chromium project. CEF maintains development and release branches that track Chromium branches. CEF source code can be downloaded, built and packaged manually or with automated tools. Visit the Branches and Building page for more information.

Security Updates

CEF is based on Chromium, so Chromium security vulnerabilities (CVEs) are likely to affect CEF using the same Chromium version. Applications that distribute CEF should keep their CEF version up to date.

Security fixes are announced in Stable Channel updates on the Chrome Releases blog. To receive updates automatically, subscribe to the blog's feed using a feed reader or a service that delivers feed updates by email. CEF does not provide a separate security announcement mailing list. See the Chromium security documentation for additional information, including eligibility for advance notification.

There may be a delay between Chromium release announcements and the availability of corresponding CEF builds. See Branches and Building for supported branches and Downloads for available builds.

External Projects

The base CEF framework includes support for the C and C++ programming languages. Thanks to the hard work of external maintainers CEF can integrate with a number of other programming languages and frameworks. These external projects are not maintained by CEF so please contact the respective project maintainer if you have any questions or issues.

If you're the maintainer of a project not listed above and would like your project listed here please either post to the CEF Forum or contact Marshall directly.

Helping Out

CEF is still very much a work in progress. Some ways that you can help out:

- Vote for issues in the CEF issue tracker that are important to you.

- Report bugs or request features via the issue tracker. Use the CEF Forum for usage questions.

- Write unit tests for new or existing functionality.

- Submit pull requests. View open issues or search for TODO(cef) in the source code for ideas.

See CONTRIBUTING.md for detailed guidelines on submitting changes, code style, commit messages, and AI tool usage.


Overview (`docs/README.md`)

The Chromium Embedded Framework (CEF) is a simple framework for embedding Chromium-based browsers in other applications. Visit the Project Overview page for more information.


Wiki Pages

The complete list of CEF Wiki pages is available here.


Overview (`tests/README.md`)

CEF Test Applications

This directory contains sample applications and test suites demonstrating the Chromium Embedded Framework (CEF).

Sample Applications

[cefsimple](cefsimple/)

Minimal C++ browser application - the recommended starting point for learning CEF.

  • Purpose: Learning CEF basics
  • Complexity: ~10 source files
  • Best for: Understanding CEF architecture and fundamental APIs
  • Tutorial: CEF Tutorial provides detailed walkthrough

[cefsimple_capi](cefsimple_capi/)

Pure C implementation of cefsimple using the CEF C API.

  • Purpose: C API reference implementation
  • Language: Pure C (C11), no C++
  • Best for: Embedding CEF in C projects or when C++ is unavailable
  • Documentation: Using the CAPI

[cefclient](cefclient/)

Comprehensive sample application demonstrating advanced CEF features.

  • Purpose: Feature demonstration and manual testing
  • Complexity: ~100+ source files
  • Features: Full CEF API coverage, built-in test framework, multiple UI modes
  • Best for: Reference implementation, exploring advanced features

Test Suite

[ceftests](ceftests/)

Automated unit test suite for CEF API validation.

  • Purpose: Automated regression testing
  • Framework: Google Test (gtest)
  • Coverage: Comprehensive CEF API testing across all platforms
  • Best for: CI/CD integration, validating CEF releases

Shared Code

[shared](shared/)

Common utilities and helpers used by cefclient and ceftests.

  • Delegate pattern framework for application customization
  • Message loop implementations (standard, external pump, multi-threaded)
  • Cross-platform resource loading and file utilities
  • Not used by cefsimple or cefsimple_capi (self-contained examples)

Learning Path

New to CEF? Follow this recommended path:

  1. Start with cefsimple and the CEF Tutorial to understand CEF architecture
  2. Explore cefclient to see advanced features and integration patterns
  3. Review ceftests to understand API usage and testing patterns
  4. Study shared code for reusable patterns and best practices

For C API development, substitute cefsimple_capi for cefsimple and consult Using the CAPI.

References


Overview (`tests/cefclient/README.md`)

CEF Client Sample Application

This directory contains the comprehensive CEF sample application demonstrating advanced browser integration features and CEF capabilities.

Overview

cefclient is the primary CEF sample application showcasing:

  • Complete feature set - Demonstrates most CEF APIs and integration patterns
  • Test framework - Built-in tests for various CEF functionality
  • Advanced features - OSR (off-screen rendering), process messaging, scheme handlers, and more
  • Multi-window support - Window management, popups, and DevTools
  • Cross-platform - Full implementations for Windows, Linux, and macOS

Comparison with cefsimple

Feature cefclient cefsimple
Purpose Comprehensive feature demo & testing Minimal learning example
Complexity ~100+ source files ~10 source files
Features Full CEF API coverage Basic browser only
Best for Reference implementation, testing Learning CEF basics

For learning CEF: Start with cefsimple to understand the basics, then explore cefclient for advanced features.

Learning Resources

Understanding cefclient architecture:

  1. CEF Tutorial - Covers cefsimple architecture, which provides the foundation
  2. CEF General Usage - Complete CEF API documentation
  3. This application - Explore the built-in tests and advanced implementations

Architecture

Core Components

  • MainContext (browser/main_context*) - Application state and CEF initialization
  • RootWindowManager (browser/root_window_manager*) - Window creation and management
  • ClientHandler (browser/client_handler*) - CEF client implementation with event handlers
  • RootWindow (browser/root_window*) - Individual browser window management
  • TestRunner (browser/test_runner*) - Test coordination and execution

Directory Structure

  • browser/ - Browser process implementation (main application logic)
  • renderer/ - Renderer process code (runs in isolated renderer processes)
  • common/ - Shared code used by multiple processes
  • resources/ - HTML/JS/CSS test pages and resources
  • mac/, win/ - Platform-specific resources and helpers

Platform Entry Points

  • cefclient_gtk.cc - Linux (GTK) entry point
  • cefclient_mac.mm - macOS (Cocoa) entry point
  • cefclient_win.cc - Windows (Win32) entry point

Key Features Demonstrated

Basic Browser Features

  • Window creation and management
  • Navigation and lifecycle
  • JavaScript execution
  • Resource loading and scheme handlers

Advanced Features

  • Off-Screen Rendering (OSR) - Windowless rendering with custom painting
  • Process messaging - Communication between browser and renderer processes
  • DevTools integration - Chrome Developer Tools
  • Downloads - Download handling and management
  • Dialogs - File dialogs, JavaScript dialogs, context menus
  • Performance - DOM access, frame rate control
  • Extensions - Chrome extension loading (Chrome style)

Testing

The application includes numerous tests accessible via the Tests menu:

  • Binding tests (JavaScript ↔ C++ communication)
  • Window management tests
  • Request/response tests
  • JavaScript execution tests
  • Scheme handler tests
  • And many more...

UI Frameworks

cefclient supports multiple UI approaches:

  • Native - Platform-specific UI (GTK, Cocoa, Win32)
  • Views - Cross-platform CEF Views framework
  • OSR - Custom rendering without native windows

Browser Styles

cefclient uses the Chrome bootstrap and supports two browser styles:

  • Chrome style (default) - Modern Chrome browser with advanced features (autofill, extensions, gamepad, etc.) and standard Chrome UI (toolbars, dialogs, etc.)
  • Alloy style - Traditional CEF browser based on the Chromium Content API, provides more flexibility for custom UI

Use command-line flags to select the browser style and other options.

Exploring Features

Try these different modes to explore various cefclient features:

bash
# Chrome style (default):
cefclient

# Alloy style:
cefclient --use-alloy-style
cefclient --use-alloy-style --use-native
cefclient --off-screen-rendering-enabled

# Multi-threaded message loop (Windows/Linux only):
cefclient --multi-threaded-message-loop
cefclient --use-alloy-style --multi-threaded-message-loop

# Chrome style + native parent (Windows/Linux only):
cefclient --use-native

What each mode demonstrates:

  • Default - Chrome style with Views framework
  • --use-alloy-style - Traditional CEF browser
  • --use-native - Platform-specific native UI instead of Views
  • --off-screen-rendering-enabled - OSR mode (windowless rendering)
  • --multi-threaded-message-loop - Alternative threading model

Note: On macOS, use open cefclient.app --args <flags> to pass command-line arguments.

Once running, explore the Tests menu to see demonstrations of various CEF features.

C API Version

Unlike cefsimple, cefclient does not have a C API equivalent due to its complexity. For a C API example, see cefsimple_capi.

References


Overview (`tests/cefsimple/README.md`)

CEF Simple C++ Example

This directory contains a minimal CEF browser application written in C++ using the CEF C++ API.

Overview

cefsimple is the canonical minimal CEF application demonstrating:

  • C++ implementation - Uses CEF C++ API with automatic reference counting
  • Basic browser functionality - Window creation, navigation, and lifecycle management
  • Cross-platform - Supports Windows, Linux, and macOS
  • Minimal code - Simple architecture suitable for learning CEF basics

C API Version

A pure C implementation of this example is available at ../cefsimple_capi/. The C version demonstrates the same functionality using the CEF C API with manual reference counting.

When to use each:

  • Use cefsimple (C++) - For production applications and most use cases
  • Use cefsimple_capi (C) - When C++ is not available or for embedding in C projects

Learning Resources

New to CEF? Start with the CEF Tutorial, which provides a detailed walkthrough of this exact example:

  • Architecture overview - CEF processes, threads, and callbacks
  • Code walkthrough - Detailed explanation of each file and function
  • Program flow - How cefsimple initializes and runs
  • Key concepts - CefApp, CefClient, handlers, and browser lifecycle

The Tutorial is the recommended starting point for understanding how CEF applications work.

For complete API documentation, see General Usage.

Architecture

Core Files

  • simple_app.h/cc - Application handler (CefApp)
  • simple_handler.h/cc - Client handler with display, life span, and load handlers

Platform-Specific Files

Windows

  • cefsimple_win.cc - Windows entry point (wWinMain)
  • simple_handler_win.cc - Windows-specific handler implementation

Linux

  • cefsimple_linux.cc - Linux entry point (main)
  • simple_handler_linux.cc - Linux/X11-specific handler implementation

macOS

  • cefsimple_mac.mm - macOS entry point with NSApplication integration
  • simple_handler_mac.mm - macOS-specific handler implementation
  • process_helper_mac.cc - Helper process entry point

Key Features

  • Automatic reference counting - CefRefPtr handles object lifetime
  • Helper window - Creates a browser window with navigation controls
  • Error handling - Displays error pages for failed navigations
  • Cross-platform UI - Uses Views framework for consistent UI

Maintenance Pattern

This is the primary CEF sample application. Changes to CEF features, API updates, or bug fixes should be applied here first, then ported to cefsimple_capi (the C version).

Typical workflow:

  1. Update cefsimple (C++ version) with new features or API changes
  2. Port equivalent changes to cefsimple_capi using C API patterns
  3. Result: Both examples demonstrate the same functionality

References


Overview (`tests/cefsimple_capi/README.md`)

CEF Simple C API Example

This directory contains a pure C implementation of the cefsimple sample application using the CEF C API (CAPI).

Overview

This is a conversion of cef/tests/cefsimple from C++ to pure C, demonstrating:

  • Pure C implementation - No C++ code, uses C11 standard
  • Manual reference counting - Using atomic operations for thread safety
  • CEF C API usage - All CEF calls use the C API (include/capi/*.h)
  • Cross-platform - Supports Windows, Linux, and macOS

Maintenance Pattern

This project serves as a reference C API implementation that mirrors the C++ cefsimple example. When changes are made to cef/tests/cefsimple (the C++ version), equivalent changes should be applied to this project using the C API patterns.

Intended workflow:

  1. Changes are made to C++ cefsimple (new features, API updates, bug fixes)
  2. Claude Code analyzes the C++ changes
  3. Claude Code applies equivalent changes to cefsimple_capi using CEF C API
  4. Result: Both examples stay in sync, demonstrating the same functionality in C++ and C

Example Claude prompt:

text
Apply the equivalent changes from the latest commits in cef/tests/cefsimple
to cef/tests/cefsimple_capi, converting the C++ code to C API patterns.
Use the reference counting and handler patterns from UsingTheCAPI.md.

This ensures the C API example remains current and demonstrates the same CEF features as the C++ version.

Learning Path

For best results, we recommend this learning path:

  1. CEF Tutorial - Start here to understand CEF architecture and concepts using the C++ cefsimple example
  2. Using the CAPI - Learn C-specific patterns and reference counting rules
  3. This example (cefsimple_capi) - See the C API implementation in practice

Note: The Tutorial uses C++ code examples, but the architectural concepts (processes, threads, callbacks, program flow) apply equally to the C API version.

Architecture

Utility Modules

  • ref_counted.h - Macros for implementing reference counting (IMPLEMENT_REFCOUNTING_SIMPLE, IMPLEMENT_REFCOUNTING_MANUAL, INIT_CEF_BASE_REFCOUNTED)
  • simple_utils.h - Common utilities (CHECK macro for invariant validation)
  • simple_browser_list.h/c - Dynamic list of browser instances with automatic reference counting

Application Core

  • simple_app.h/c - Application handler (cef_app_t) and browser process handler

Client Handler Module

  • simple_handler.h - Client handler interface and structure definitions
  • simple_handler.c - Main client handler implementation, public API, and browser close operations
  • simple_display_handler.c - Display handler (title change notifications)
  • simple_life_span_handler.c - Life span handler (browser lifecycle management)
  • simple_load_handler.c - Load handler (error page display)

Views Framework

  • simple_views.h/c - Browser view and window delegates for Views-based UI

Platform-Specific Files

Windows

  • cefsimple_win.c - Windows entry point (wWinMain)
  • simple_handler_win.c - Windows-specific handler (window title updates)

Linux

  • cefsimple_linux.c - Linux entry point (main)
  • simple_handler_linux.c - Linux/X11-specific handler (window title updates)

macOS

  • cefsimple_mac.m - macOS entry point with NSApplication integration
  • simple_handler_mac.m - macOS-specific handler (window title updates)
  • process_helper_mac.c - Helper process entry point

Key Differences from C++ Version

The C API requires significantly different patterns than C++:

  • Manual reference counting - Implement atomic add_ref/release functions instead of IMPLEMENT_REFCOUNTING macro
  • Explicit parameter release - Must call release() on all object parameters in callbacks (memory leak if forgotten)
  • Function pointers - Use structure fields with CEF_CALLBACK functions instead of virtual methods
  • Manual string conversion - Use cef_string_from_ascii() / cef_string_clear() instead of CefString class
  • Structure initialization - Use calloc() and set function pointers instead of C++ constructors

See Using the CAPI for complete C API patterns and examples.

Critical C API Rules

When working with the C API, you must follow these rules to avoid crashes and memory leaks:

  1. CEF structure MUST be first member - Allows safe casting between base and wrapper types
  2. Set size to CEF type size - Use sizeof(cef_*_t), not sizeof(your_wrapper_t)
  3. Release all callback parameters - Except self; failure causes memory leaks
  4. Add reference when returning to CEF - CEF will release when done
  5. Use atomic operations - Reference counting functions called from any thread

See Using the CAPI for detailed explanations, complete code examples, and reference counting patterns.

References


Overview (`tests/ceftests/README.md`)

CEF Unit Tests

This directory contains the CEF unit test application (ceftests) - a comprehensive test suite for validating CEF functionality across all supported platforms.

Overview

ceftests is a Google Test-based unit test application that validates CEF APIs and integration patterns:

  • Comprehensive coverage - Tests most CEF APIs and features
  • Automated testing - Runs headlessly for CI/CD integration
  • Cross-platform - Validates behavior on Windows, Linux, and macOS
  • Multiple modes - Supports Chrome style and Alloy style browser testing
  • Quality assurance - Used to verify CEF releases and Chromium updates

Comparison with cefclient

Feature ceftests cefclient
Purpose Automated unit testing Manual feature demonstration
Test Type Google Test (gtest) Interactive manual tests
Execution Headless/automated GUI-based interactive
Best for CI/CD, regression testing Feature exploration, debugging

For testing: Use ceftests for automated validation and cefclient for manual feature testing and exploration.

Test Categories

ceftests includes unit tests for:

Core Features

  • API Version - CEF version and API hash validation
  • Command Line - Command-line argument parsing
  • Life Span - Browser lifecycle and window management
  • Navigation - URL loading, redirects, and navigation events
  • Frame - Multi-frame handling and frame lifecycle

Web Content

  • DOM - DOM manipulation and queries
  • JavaScript - V8 integration and JavaScript execution
  • Display - Rendering and display events
  • Dialogs - JavaScript dialogs and file dialogs

Network and Resources

  • URL Request - Network requests and responses
  • Scheme Handler - Custom protocol handlers
  • Cookie - Cookie management and storage
  • CORS - Cross-origin resource sharing
  • Resource Manager - Resource loading and caching
  • Certificate Errors - SSL/TLS certificate handling

Advanced Features

  • Process Messages - Inter-process communication
  • Message Router - JavaScript-C++ messaging framework
  • DevTools - Chrome DevTools protocol
  • Downloads - Download handling and management
  • Printing - Print and print-to-PDF functionality
  • Media Access - Camera and microphone permissions
  • Audio Output - Audio device enumeration

UI and Rendering

  • Views Framework - CEF Views UI tests (button, panel, textfield, window, scroll view)
  • OSR (Off-Screen Rendering) - Windowless rendering and accessibility
  • Draggable Regions - Custom window dragging areas
  • Permission Prompts - Permission request UI

Utilities

  • Parser - URL and data parsing
  • String - CEF string handling
  • Stream - Stream operations and handlers
  • Time - Time conversion utilities
  • Thread - Threading and task posting
  • Tracing - Performance tracing integration
  • Waitable Event - Synchronization primitives
  • XML/ZIP - XML parsing and ZIP archive handling

Running Tests

Basic Usage

Run all tests with default settings:

bash
# Windows/Linux
ceftests

# macOS
./ceftests.app/Contents/MacOS/ceftests

Common Test Options

Use Google Test flags to control test execution:

bash
# Run specific test suite
ceftests --gtest_filter="NavigationTest.*"

# Run specific test
ceftests --gtest_filter="NavigationTest.LoadURL"

# Exclude specific tests
ceftests --gtest_filter="-AudioOutputTest.*"

# List all available tests
ceftests --gtest_list_tests

# Run with Chrome style Views framework
ceftests --use-views

# Run with Alloy style
ceftests --use-alloy-style

# Combine options
ceftests --use-views --gtest_filter="NavigationTest.*"

Platform-Specific Notes

macOS: Use the path to the executable inside the .app bundle:

bash
./ceftests.app/Contents/MacOS/ceftests --use-views

Linux headless: Use xvfb-run for headless testing:

bash
xvfb-run ceftests --no-sandbox

Filters for Known Issues

Some tests are known to be flaky or have platform-specific issues. For the recommended test filters that exclude these known issues, see Chromium Update § Run CEF tests, section C.

Test Infrastructure

Core Test Framework

  • test_handler.h/cc - Base class for browser-based tests
  • test_suite.h/cc - Test suite initialization and configuration
  • test_util.h/cc - Common test utilities and helpers
  • routing_test_handler.h/cc - Base for tests using message routing
  • thread_helper.h/cc - Threading utilities for async tests
  • track_callback.h - Callback tracking for verifying execution order

Test Server Support

  • test_server.h/cc - HTTP test server implementation
  • test_server_manager.h/cc - Server instance management
  • test_server_runner.h/cc - Server execution framework
  • test_server_observer.h/cc - Server event monitoring

Platform-Specific Files

  • Windows: win/ directory with resources and manifest
  • macOS: Helper process integration, OS rendering support (os_rendering_unittest_mac.mm)
  • Linux: Resource utilities (resource_util_linux.cc)

Test Development

Writing New Tests

Tests use Google Test framework with CEF-specific patterns:

cpp
#include "tests/ceftests/test_handler.h"

TEST(MyTestSuite, MyTest) {
  // Create test handler
  CefRefPtr<MyTestHandler> handler = new MyTestHandler();

  // Execute test
  handler->ExecuteTest();

  // Verify results
  EXPECT_TRUE(handler->succeeded());
}

For browser-based tests, extend TestHandler:

cpp
class MyTestHandler : public TestHandler {
 public:
  void RunTest() override {
    // Create and load browser
    CreateBrowser("https://example.com");
  }

  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
                 CefRefPtr<CefFrame> frame,
                 int httpStatusCode) override {
    // Test logic here
    DestroyTest();
  }
};

Test Execution Modes

ceftests supports multiple execution modes:

  • Chrome style - Tests using Chrome UI layer (default)
  • Alloy style - Tests using Content API layer (--use-alloy-style)
  • Views framework - Tests using CEF Views (--use-views)
  • Headless - Non-interactive testing for CI/CD (Linux: use xvfb-run)

Known Issues

Some tests may be flaky or have known limitations:

For the latest test filters and workarounds, see the ChromiumUpdate.md Wiki page.

Resources

Test resources are located in resources/ directory:

  • SSL certificates for certificate validation tests
  • Test data files

References


Overview (`tests/shared/README.md`)

CEF Shared Test Code

This directory contains common code and utilities shared across CEF test applications (cefclient and ceftests).

Overview

The shared directory provides reusable components that handle common CEF integration patterns, reducing code duplication across test applications and demonstrating best practices for CEF application development.

Used by:

  • cefclient - Comprehensive feature demonstration and testing application
  • ceftests - Automated unit test suite

Not used by:

Directory Structure

browser/

Browser process utilities including:

  • Application framework with delegate pattern (client_app_browser)
  • Message loop implementations (standard, external pump, multi-threaded)
  • Cross-platform resource loading (resource_util)
  • File system operations and geometry utilities

common/

Code shared across all process types:

  • Base application handler (client_app)
  • Process type detection and command-line parsing
  • Custom scheme registration
  • String and binary data utilities

renderer/

Renderer process handler with delegate pattern for V8 context management and JavaScript extension registration.

resources/

Shared test resources (HTML pages, icons, sample files).

Key Concepts

Delegate Pattern: Applications (cefclient, ceftests) extend shared base classes by implementing delegates rather than modifying shared code.

Message Loop Abstraction: Supports multiple threading models - standard CEF loop, external pump (integrates with platform event loops), and multi-threaded mode.

Platform Abstraction: Common interfaces with platform-specific implementations for resource loading, file operations, and window management.

Usage

Applications use shared handlers for process-specific behavior:

cpp
CefRefPtr<ClientApp> app;
switch (process_type) {
  case ClientApp::BrowserProcess:
    app = new ClientAppBrowser();   // Browser process handler
    break;
  case ClientApp::RendererProcess:
    app = new ClientAppRenderer();  // Renderer process handler
    break;
}

Applications customize behavior by implementing delegates without modifying shared code.

References


Overview (`tools/cefbuilds/README.md`)

CEF Builds JSON Tools

Tools for generating and maintaining the JSON index files used by the
CEF Builds CDN and the CEF
installer.

Files

Core libraries

  • cef_json_builder.py — Builds and maintains the master index.json file
    that contains the full catalog of CEF binary distributions across all
    platforms, versions, and channels. Handles file metadata (name, size, sha1,
    last_modified), Chromium version mapping, and sandbox compatibility hashes.

  • cef_installer_builder.py — Generates installer-optimized CDN query files
    from a populated cef_json_builder instance. Produces lightweight JSON files
    designed for efficient HTTP HEAD timestamp checks by the CEF installer.

Data files

  • revoked.json — Manually maintained list of revoked CEF versions. Revoked
    versions are excluded from all installer-generated JSON output. The installer
    also fetches this file from the CDN and refuses to install any listed version.

Examples and tests

  • cef_json_builder_example.py — Example script demonstrating
    cef_json_builder usage. Creates and updates a local index.json with fake
    file data.

  • cef_json_builder_test.py — Unit tests for cef_json_builder.

  • cef_installer_builder_test.py — Unit tests for cef_installer_builder.

CDN file formats

Master index

  • index.json — Full data index of all CEF binary distributions. Generated
    by cef_json_builder. Keyed by platform, with version records sorted
    newest-first by file last_modified timestamp.

Installer query files

Generated by cef_installer_builder from the master index:

  • stable.txt — Current stable milestone number (e.g., 137).

  • {milestone}.json (e.g., 137.json) — Newest build per platform for a
    given milestone. Used by the installer to check if an update is available.

  • {milestone}_{platform}.json (e.g., 137_windows64.json) — All builds
    for a milestone+platform combination, sorted newest-first.

  • {abi_hash}_{platform}.json (e.g., abc123def4567890_windows64.json) —
    All builds compatible with a given ABI hash and platform across all milestones,
    sorted newest-first. Windows platforms only (tied to chrome_elf.dll /
    Chromium sandbox ABI).

Concepts

Platforms

Supported platforms: linux32, linux64, linuxarm, linuxarm64,
macosarm64, macosx64, windows32, windows64, windowsarm64.

Distribution types

Each version may have files of these types: standard, minimal, client,
tools, release_symbols, debug_symbols, signed.

The signed type contains code-signed archives with catalog files, used by the
installer for verified installation.

Channels

Versions belong to either the stable or beta channel.

Version formats

  • New-style: 137.3.5+g62d140e+chromium-137.0.7204.6
    • Short version: 137.3.5 (everything before the first +)
    • Milestone: 137 (major version number)
  • Old-style: 3.2704.1414.g185cd6c (predates the installer format)

ABI hash (`sandbox_compat`)

A 16-character hex string identifying the Windows sandbox ABI compatibility
level. Derived from cef_api_versions.json on GitHub. Versions sharing the same
ABI hash can use the same chrome_elf.dll and are interchangeable from the
sandbox's perspective.

Revocation

The revoked.json file lists versions that must not be installed. When passed
to cef_installer_builder, revoked versions are automatically excluded from
all generated output. The installer also fetches this file from the CDN at
runtime and refuses to install any listed version.

Entries can revoke a single version or a range of versions:

json
{
  "revoked_versions": [
    {
      "version": "137.1.0",
      "reason": "CVE-2024-XXXXX: critical sandbox escape",
      "revoked_at": "2024-01-15T00:00:00Z"
    },
    {
      "version_min": "138.0.0",
      "version_max": "138.2.99",
      "reason": "CVE-2024-YYYYY: affects all 138.0–138.2 builds",
      "revoked_at": "2024-02-01T00:00:00Z"
    }
  ]
}

Single version (point revocation):

  • version — Short version string (e.g., 137.1.0).

Version range (inclusive):

  • version_min — Inclusive lower bound (e.g., 138.0.0).
  • version_max — Inclusive upper bound (e.g., 138.2.99).

Common fields:

  • reason — Human-readable explanation (CVE ID, bug reference, etc.).
  • revoked_at — ISO 8601 timestamp of when the revocation was added.

A single-version entry using version is equivalent to setting both
version_min and version_max to the same value. If both version and
version_min/version_max are present, the range fields take precedence.

Running tests

bash
cd cef/tools/cefbuilds
python3 -m unittest cef_json_builder_test -v
python3 -m unittest cef_installer_builder_test -v

Overview (`tools/clang/README.md`)

CEF Clang Tools

This directory contains clang-based tools for CEF code modernization and refactoring.

Tools

cef_cpp_rewriter

A clang AST-based rewriter tool for automating C++20 modernization of CEF code.

Supported transformations:

  • .contains() for associative containers
  • Structured bindings for range-for loops
  • Iterator loops to range-for
  • DISALLOW_COPY_AND_ASSIGN replacement

See TRANSFORMS.md for complete documentation of all transformations, command-line flags, and examples.

Prerequisites

  • Chromium checkout with CEF
  • Python 3.10+ (Chromium's build scripts require it)
  • ~30 minutes for initial clang bootstrap

Note: CEF is a separate git repository within chromium/src. Chromium's clang
tool scripts (run_tool.py, test_tool.py, apply_edits.py) use git commands
that don't work across repository boundaries. This directory provides CEF-specific
replacements: run_tool.py, test_tool.py, and apply_edits.py.

Python Version

macOS ships with Python 3.9 which is too old. Install Python 3.12 via Homebrew:

bash
brew install [email protected]
ln -s /opt/homebrew/opt/[email protected]/bin/python3.12 /opt/homebrew/opt/[email protected]/bin/python3
export PATH="/opt/homebrew/opt/[email protected]/bin:$PATH"
python3 --version  # Should show 3.12.x

Add the export line to ~/.zshrc to make it permanent.

Quick Start

Initial Setup (one-time, ~30 minutes)

bash
# From chromium/src directory
./cef/tools/clang/scripts/build_clang_rewriter.sh

This will:

  1. Create a symlink from tools/clang/cef_cpp_rewriter to the CEF tool
  2. Bootstrap clang with the tool (~30 minutes)

Warning: The clang bootstrap updates third_party/llvm-build/, which will
invalidate all existing Chromium/CEF build directories and trigger a full
rebuild the next time you build. Plan accordingly.

Running the Tool

text
/* Detailed source-code truncated for AI context efficiency. */

Tool Arguments

The --tool-args option passes arguments directly to cef_cpp_rewriter.
See TRANSFORMS.md for the complete list of command-line flags.

Examples:

bash
# Run only iterator-loops transformation
--tool-args="--only=iterator-loops"

# Run only contains and structured-bindings
--tool-args="--only=contains,structured-bindings"

# Disable specific transformation (all others still run)
--tool-args="--contains=false"

Incremental Rebuilds

After modifying CefCppRewriter.cpp, rebuild quickly with:

bash
ninja -C third_party/llvm-build/Release+Asserts cef_cpp_rewriter

Do not re-run build_clang_rewriter.sh - it rebuilds everything from scratch.

Testing

bash
# Run tool tests
python3 cef/tools/clang/scripts/test_tool.py cef_cpp_rewriter

# With verbose output
python3 cef/tools/clang/scripts/test_tool.py cef_cpp_rewriter --verbose

Compatibility

CEF targets C++20. See TRANSFORMS.md for minimum toolchain versions.

Standalone Distribution

The tool can be packaged for use on any C++ project (not just CEF):

bash
# Package to a target directory
./cef/tools/clang/scripts/package_standalone.sh /path/to/target

# This creates:
# /path/to/target/
# ├── bin/cpp_rewriter           # The tool binary
# ├── lib/clang/<version>/       # Bundled clang headers
# ├── scripts/
# │   ├── run_rewriter.py        # Standalone wrapper
# │   ├── apply_edits.py
# │   └── process_edits.py
# └── README.md

The standalone distribution is self-contained and includes bundled clang headers
so it works without requiring a matching system clang installation.

Usage on any CMake project:

bash
cd my-project

# Generate compile_commands.json (requires Ninja or Makefiles generator)
cmake -G Ninja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -B build

# Run the rewriter
/path/to/target/scripts/run_rewriter.py -p build src/ \
  | /path/to/target/scripts/apply_edits.py --base-dir build

See standalone/README.md for complete standalone usage documentation.

Directory Structure

text
/* Detailed source-code truncated for AI context efficiency. */

Maintenance

This tool is not regularly tested. It may need updates when:

Trigger Likely Fix
Chromium updates tools/clang/ infrastructure Update CMakeLists.txt, check build.py flags
LLVM/Clang API changes Update AST matcher syntax, check deprecations in Clang doxygen
CEF adopts C++23 Add flat_map/flat_set container types to matcher
Build fails after Chromium update Compare with current tools/clang/ast_rewriter/ example

Diagnosing Build Failures

If the tool fails to build after a Chromium update:

  1. Check if ast_rewriter still exists and builds:

    bash
    python3 tools/clang/scripts/build.py --bootstrap --extra-tools ast_rewriter
  2. Compare CMakeLists.txt with the current ast_rewriter version:

    bash
    diff cef/tools/clang/cef_cpp_rewriter/CMakeLists.txt tools/clang/ast_rewriter/CMakeLists.txt
  3. Check for API changes in Clang's AST matchers (review Clang release notes)

Cleanup

After running the tool, you may want to remove the symlink:

bash
rm tools/clang/cef_cpp_rewriter  # Removes symlink only, not CEF source

Resources


Overview (`tools/claude/README.md`)

Tools and Detailed Instructions for CEF Development

This directory contains tools and detailed instructions for using coding
agents to assist with CEF (Chromium Embedded Framework) development. Use these
guides with models that benefit from explicit workflows, examples, and
verification steps, including Claude Opus 4.x and GPT Terra.

The guides were originally written for Claude Code and retain their CLAUDE_*
filenames. Other coding agents can use them directly; adapt any Claude-specific
setup or file attachment syntax to your agent. For GPT Sol or other frontier
models that work well with concise instructions, see the
GPT guides for concise patch and build instructions and
example prompts. For other tasks, newer frontier models can often discover the
relevant details themselves, using the detailed instruction files here as
reference material when needed.


[TOC]


Quick Navigation

Are you updating CEF to a new Chromium version?

→ See CHROMIUM_UPDATE.md for the complete workflow:

  • Fixing patches that fail to apply
  • Fixing build errors from API changes
  • Using analyzer scripts and verification tools

Are you developing a CEF application using a binary distribution?

→ See CLIENT_DEVELOPMENT.md for implementing features:

  • Adding custom URL schemes
  • JavaScript-to-C++ communication
  • Network request interception
  • Popup window customization

Are you writing or debugging CEF unit tests?

→ See CLAUDE_TEST_INSTRUCTIONS.md for working with ceftests:

  • Running existing unit tests
  • Creating new tests for CEF features
  • Debugging test failures and flakiness
  • Using TestHandler, RoutingTestHandler, and TestServer

Are you developing libcef internals (CEF implementation code)?

→ See CLAUDE_LIBCEF_INSTRUCTIONS.md for libcef development patterns:

  • Adding new CEF APIs with proper versioning
  • Implementing WebContentsDelegate callbacks
  • Working with Chrome and Alloy browser styles
  • Finding and fixing NOTIMPLEMENTED() stubs

Are you syncing CEF base headers with Chromium?

→ See CLAUDE_BASE_SYNC_INSTRUCTIONS.md for synchronizing cef/include/base/ headers:

  • CEF-specific modifications (namespaces, includes, platform macros)
  • Files to sync vs files to preserve
  • Handling C++23 reverts
  • Step-by-step sync process

Are you cherry-picking commits from upstream CEF?

→ See CLAUDE_CHERRY_PICK_INSTRUCTIONS.md for cherry-picking commits that include patch files:

  • Extracting and applying source file changes from patches
  • Resaving patches with correct index hashes
  • Handling multiple patch files per commit
  • Recovery when things go wrong

Are you applying a Chromium commit to CEF as a patch?

→ See CLAUDE_SRC_PATCH_INSTRUCTIONS.md for creating or updating CEF patches from Chromium commits:

  • Creating new patch files and adding to patch.cfg
  • Adding changes to existing patch files
  • Handling version differences between Chromium versions
  • Verifying patches can be reverted and reapplied

Are you improving unit test coverage?

→ See CLAUDE_COVERAGE_INSTRUCTIONS.md for running and analyzing code coverage:

  • Human prerequisites for setting up coverage builds
  • Building and running tests with coverage instrumentation
  • Analyzing coverage reports to identify gaps
  • Workflow for improving function and line coverage

Files in This Directory

Documentation

Python Tools

  • analyze_build_output.py - Analyze and format ninja build output with error index
  • analyze_coverage_output.py - Analyze LLVM coverage reports and identify coverage gaps
  • analyze_patch_output.py - Analyze and format patch_updater.py output with file movement detection
  • patch_utils.py - Shared utilities for patch analysis tools (file movement detection)
  • verify_patch.py - Verify that regenerated patches include all changes from reject files
  • format_wiki.py - Apply Bitbucket wiki formatting guidelines to markdown files (supports wildcards; also integrated into ../fix_style.py)

Test Files

Common Setup

For Chromium update workflows, run the setup script:

bash
cd chromium/src/cef/tools
python3 setup_claude.py

This copies CLAUDE.md.in to the project root as chromium/src/CLAUDE.md, enabling Claude Code to understand the Chromium/CEF codebase structure.

Formatting Files

The fix_style.py tool in //cef/tools/ formats C/C++, Python, and Markdown files:

bash
# From chromium/src/cef/tools directory:

# Format single markdown file
python3 fix_style.py claude/CLAUDE_TEST_INSTRUCTIONS.md

# Format all CLAUDE instruction files
python3 fix_style.py claude/CLAUDE_*.md

# Format all markdown files in claude directory
python3 fix_style.py claude/*.md

# Format all staged files (including markdown)
python3 fix_style.py staged

The markdown formatter applies Bitbucket wiki formatting guidelines (blank lines before lists, 4-space indentation, etc.).

Tips for Working with Claude Code

These tips apply to both Chromium updates and client development:

1. Provide Context

Always include relevant version numbers or distribution details:

For Chromium updates:

text
Old version: 139.0.7258.0
New version: 140.0.7339.0

For client development:

text
CEF binary distribution: cef_binary_142.0.10+g29548e2+chromium-142.0.7444.135_macosarm64

2. Attach Relevant Files

Use @filename to attach files Claude should read:

  • @cef/tools/claude/patch_analysis.txt - Analyzed patch output
  • @cef/tools/claude/build_analysis.txt - Analyzed build output
  • @cef/tools/claude/CLAUDE_CHERRY_PICK_INSTRUCTIONS.md - Cherry-picking upstream commits
  • @cef/tools/claude/CLAUDE_CLIENT_INSTRUCTIONS.md - Client development guide
  • @cef/tools/claude/CLAUDE_SRC_PATCH_INSTRUCTIONS.md - Applying Chromium commits as patches
  • @cef/tools/claude/CLAUDE_TEST_INSTRUCTIONS.md - CEF unit testing guide

Tip: In large directory structures like chromium/src, the @ syntax can be slow. Use // syntax for workspace-relative paths instead (e.g., //cef/tools/claude/patch_analysis.txt).

3. Let Claude Work Systematically

Don't interrupt mid-stream unless necessary. Claude will:

  • Create TODO lists
  • Work through items one by one
  • Report progress regularly
  • Ask for help when stuck

4. Use Incremental Prompts

For large tasks, you can guide Claude incrementally:

For Chromium updates:

text
1. "Start fixing patches using @CLAUDE_PATCH_INSTRUCTIONS.md"
2. [Claude works for a while]
3. "Good progress. Continue with the remaining patches."
4. [Claude finishes]
5. "Now let's fix build errors using @CLAUDE_BUILD_INSTRUCTIONS.md"

For unit testing:

text
1. "Create tests for the new feature using @CLAUDE_TEST_INSTRUCTIONS.md"
2. [Claude creates tests]
3. "Run the tests and fix any failures"
4. [Claude debugs and fixes]
5. "Good, now verify they work in both Chrome and Alloy styles"

5. Ask for Summaries

After a session:

text
Please summarize all the changes you made, organized by category.

Claude can provide a detailed summary for documentation or commit messages.

6. Use the Analyzers Proactively

For Chromium updates, run the analyzers yourself first to understand the scope:

For patches:

bash
# From chromium/src/cef/tools/claude directory:
python3 analyze_patch_output.py patch_output.txt \
  --old-version 139.0.7258.0 \
  --new-version 140.0.7339.0 \
  --no-color > patch_analysis.txt

For build errors:

bash
# From chromium/src/cef/tools/claude directory:
python3 analyze_build_output.py build_output.txt \
  --old-version 139.0.7258.0 \
  --new-version 140.0.7339.0 \
  --no-color > build_analysis.txt

Then share the analysis files with Claude.

Getting Help

For Issues with Tools

  • Check the guide files and instruction files listed above for usage examples
  • Check analyzer script help: python3 analyze_patch_output.py --help

For Issues with CEF

For Issues with Claude Code

  • Claude Code documentation: https://docs.claude.com/claude-code
  • Improve prompts with more context and specific examples
  • Break large tasks into smaller, more manageable pieces

Contributing

If you improve these tools or instructions:

  1. Test with a real Chromium update, client development task, or unit testing workflow
  2. Document what you changed and why
  3. Update the appropriate guide (CHROMIUM_UPDATE.md, CLIENT_DEVELOPMENT.md, or CLAUDE_TEST_INSTRUCTIONS.md)
  4. Share improvements with the CEF community

License

These tools are part of the CEF project and follow the same license as CEF (BSD license).


Overview (`tools/gpt/README.md`)

GPT Instructions for CEF Updates

Use these guides when asking a coding agent to update CEF patches or fix build
errors during a Chromium update. The GPT guides give Sol a short overview of
what to do and how to verify the result. They link to the existing Claude
guides for detailed commands, examples, and troubleshooting.

Choosing a model

Model Starting effort Guides to use
GPT-6.1 Sol Low The GPT guides in this directory
GPT-5.6 Terra Choose for your task The Claude guides in cef/tools/claude/

For Sol, start with low effort and use the Sol examples below. The agent can
consult the linked Claude guides as it works. For Terra, use the Terra examples,
which point directly to the detailed Claude guides. Those guides work with
other models despite their filenames.

Select the model and effort in your coding agent before sending the prompt.
The instruction files describe the work; model selection is a separate setting.

These recommendations are a starting point for trying the workflows on real
updates. Adjust the GPT model and reasoning effort to balance speed and cost
while meeting the same completion checks. Check whether the agent covers every
failed file, preserves CEF behavior, and saves source fixes into the patches.
If Sol gets stuck on a particular issue or cannot explain why a behavioral
change is correct, try that issue again at medium effort. When comparing speed
and cost, include any follow-up work needed to correct the result.

Getting started

Before launching the agent, use the existing Chromium update guide to prepare
your checkout and the analysis for the phase you're working on:

  1. Review the complete update workflow
    for the order of checkout updates, patch repairs, builds, and testing.
  2. Check the prerequisites and
    setup: you'll need a Chromium checkout
    with CEF integration, Python 3, and the appropriate build configuration.
    The setup_claude.py step installs context for Claude Code; it is not a
    prerequisite for using the GPT prompts.
  3. For patch repairs, follow Phase 1: Fixing Patches
    to capture patch_updater.py output and generate patch_analysis.txt with
    your old and new Chromium versions. Then use the patch prompt below.
  4. Once the patches apply cleanly, follow Phase 2: Fixing Build Errors
    to capture a build of your chosen target and generate build_analysis.txt.
    Then use the build prompt below.

Start your coding agent from the chromium/src checkout root. Copy one of the
complete prompts below, then replace the Chromium versions and analysis path
with yours. For a build task, also set your output directory and build target.
All paths in the prompts are relative to chromium/src.

Use the analysis files produced by the existing tools in cef/tools/claude/:

  • patch_analysis.txt lists failed patches and files.
  • build_analysis.txt lists build errors and points to the relevant sections
    of build_output.txt.

The build examples include permission to fix Chromium files outside cef/
when custom CEF patches cause their build failures. Keep that instruction if
it applies to your update: it tells the agent to save those fixes into the
associated CEF patches and overrides the detailed build guide's usual scope.

Example prompts for Sol at low effort

Update patches

text
Work from the chromium/src checkout root; all paths below are relative to it.

Please update patches from 154.0.8037.0 to 156.0.8078.0
Read and follow the instructions in:
cef/tools/gpt/GPT_PATCH_INSTRUCTIONS.md
When a patch has multiple failed files you MUST check all of the files.

Here's the patch output analysis: cef/tools/claude/patch_analysis.txt

Fix build errors

text
Work from the chromium/src checkout root; all paths below are relative to it.

Please fix build errors for the 154.0.8037.0 to 156.0.8078.0 update
Read and follow the instructions in:
cef/tools/gpt/GPT_BUILD_INSTRUCTIONS.md

Build output directory: out/Debug_GN_arm64
Build target: cef

IMPORTANT: Some .cc files outside of cef may also have build errors after
the Chromium update, due to the custom CEF patches applied. Fix those build
errors and re-save the associated CEF patch files using
cef/tools/gpt/GPT_PATCH_INSTRUCTIONS.md.

Here's the build error analysis: cef/tools/claude/build_analysis.txt

IMPORTANT: Do not try to read all of cef/tools/claude/build_output.txt. Use
the positional markers from cef/tools/claude/build_analysis.txt to read
relevant sections ONLY.

Example prompts for Terra

Update patches

text
Work from the chromium/src checkout root; all paths below are relative to it.

Please update patches from 154.0.8037.0 to 156.0.8078.0
Read and follow the instructions in:
cef/tools/claude/CLAUDE_PATCH_INSTRUCTIONS.md
When a patch has multiple failed files you MUST check all of the files.

Here's the patch output analysis: cef/tools/claude/patch_analysis.txt

Fix build errors

text
Work from the chromium/src checkout root; all paths below are relative to it.

Please fix build errors for the 154.0.8037.0 to 156.0.8078.0 update
Read and follow the instructions in:
cef/tools/claude/CLAUDE_BUILD_INSTRUCTIONS.md

Build output directory: out/Debug_GN_arm64
Build target: cef

IMPORTANT: Some .cc files outside of cef may also have build errors after
the Chromium update, due to the custom CEF patches applied. You should also
fix those build errors and re-save the associated CEF patch files using
cef/tools/claude/CLAUDE_PATCH_INSTRUCTIONS.md. This extends the build
guide's default restriction to files under cef.

Here's the build error analysis: cef/tools/claude/build_analysis.txt

IMPORTANT: Do not try to read all of cef/tools/claude/build_output.txt. Use
the positional markers from cef/tools/claude/build_analysis.txt to read
relevant sections ONLY.

Overview (`libcef_dll/bootstrap/installer/README.md`)

CEF Installer Library

The CEF Installer Library provides automatic CEF version management for
applications using the bootstrap architecture. It handles downloading,
installing, updating, and uninstalling CEF from a shared installation
directory.

Contents

Client Integration

Prerequisites

The installer downloads and verifies signed Release builds of CEF. For
loading to succeed, your application must:

  1. Use a Release build of bootstrap.exe — the installer downloads (or your
    application bundles) signed Release CEF binaries, and sandbox initialization
    is not compatible between Debug and Release binaries.
  2. Code sign chrome_elf.dll (which ships next to the exe) with the same
    certificate
    as the exe. The bootstrap verifies that both binaries share the
    same primary thumbprint at startup.

1. Embed Installer Configuration

Add a CEF_INSTALLER_CONFIG resource to your client DLL's .rc file (or to the
bootstrap .exe's .rc file — as a fallback when the client DLL has no config,
or for standalone mode; see Standalone Mode
Security
):

rc
CEF_INSTALLER_CONFIG RCDATA "installer_config.json"

Create installer_config.json with your application's requirements:

json
{
  "appid": "A3B9C4D5-E6F7-4A8B-9C0D-E1F2A3B4C5D6",
  "vmin": "151.1",
  "vmax": "",
  "abi_hash": "1234567890ABCDEF",
  "launch_health": "explicit"
}

For production builds, generate this resource from a template so vmin and
abi_hash stay synchronized with the bootstrap. See
Build Integration.

2. Configuration Fields

Field Required Description
appid Yes Unique UUID for your application. Never changes once assigned.
vmin Yes Minimum CEF version (e.g., "151.1" or "151.2.1").
vmax No Maximum CEF version. Empty string means no upper bound.
abi_hash No Sandbox compatibility hash (16 hex chars). Must match CEF_SANDBOX_COMPAT_HASH from cef_version.h. Required for sandbox support.
channel No Manifest channel used for downloads: "" (stable, default) or "beta". Installed versions are channel-agnostic; see Version Selection.
launch_health No Local launch-health mode: "off" (default), "explicit" (recommended), or "exit_code" (zero-code-change opt-in).
enable_explicit_modes No Enable explicit installer commands (/cef-update, /cef-uninstall, and /cef-retention-*) and standalone auto-install. Default false. Only read from the bootstrap .exe's embedded resource. See Standalone Mode Security.
unchecked_cef_path No Path to a directory containing libcef.dll, used as-is without version, ABI, or signature checks. Relative paths are resolved against the client DLL directory. Only read from the client DLL's embedded resource; ignored in the bootstrap resource. See Unchecked CEF Path.
bundled_cef_path No Path to a pre-extracted CEF distribution directory (must contain cef_version.json, catalog.cat, and Release/libcef.dll). Participates in version selection (newer wins, installed wins ties, revocation demotion). Relative paths are resolved against the client DLL directory. Only read from the client DLL's embedded resource and RunInstaller JSON; ignored in the bootstrap resource. See Version Selection.
install_path No Exclusive installer-state namespace. Relative values are resolved against the client DLL directory. Only read from the client DLL's embedded resource; bootstrap resources cannot set it. RunInstaller JSON has an independent operation-specific field.
cdn_urls No One through three ordered HTTPS base URLs for the selected application config. Accepted from client and bootstrap resources; the selected source replaces rather than merges with fallback sources. When omitted, source resolution falls through. See Download Source Precedence and Robustness.

Unrecognized configuration keys emit one warning per key and are otherwise
ignored. They never make an otherwise valid configuration fail, preserving
forward compatibility when one config is consumed by different bootstrap
versions.

Version Clamping: The vmin value is automatically clamped to the
bootstrap's compile-time API version. This prevents the installer from
downloading a CEF version older than what the bootstrap was built against,
which could cause compatibility issues. For example, if you specify
vmin: "150.0" but the bootstrap was built with CEF API version 15101, the
effective minimum becomes "151.1". This is particularly relevant for
CMake/Bazel builds where vmin may be manually configured.

If clamping makes the range empty, configuration fails immediately with exit
code 100. For example, vmin: "150.0", vmax: "151.0" with an API 15101
bootstrap reports the configured minimum, effective minimum (151.1),
bootstrap API version, and configured maximum. It is not reported as the
transient no-matching-version error 103.

Note: CEF automatically loads resources (.pak files) and locales from
the Release/ subdirectory where libcef.dll resides. No CefSettings
configuration is required when using the installer.

Maximum Version Guidance

Production configurations should generally cap vmax at the newest Chromium
milestone the application has tested. Major Chromium upgrades can require
application-specific compatibility work. For example, "151.99" accepts
versions through the literal, inclusive 151.99 bound while excluding 151.100
and Chromium 152 or later. Raise the ceiling deliberately after validating the
next milestone.

Command-Line Options

Archive extraction uses Chromium's available-memory estimate when selecting
parallel extraction. --enable-low-end-device-mode can reduce that estimate
and cause extraction to fall back to single-threaded streaming.

When the bootstrap executable is run with these flags, it enters installer mode:

Flag Description
/cef-update Check for and install CEF updates.
/cef-uninstall Unregister application and prune unused CEF versions.
/cef-retention-dry-run Report stale registrations and affected versions without modifying installer state.
/cef-retention-apply Recompute and apply registration retention under the writer lock.
/cef-max-age-days=<90..3650> Retention-only age threshold. Default: 180 days.
/cef-forcecheck Force CDN check even if cache is fresh (use with /cef-update).
/cef-background Run with below-normal process priority, no UI, and low-impact single-threaded extraction. For uninstall with a writable target, opt into asynchronous temp-child relaunch; the parent returns 109 after the child starts.
/cef-headless Disable progress UI without changing extraction behavior.
/cef-parent=<hwnd> Pumped parent window for progress notifications and supported asynchronous-uninstall lifecycle results.

Examples:

text
MyApp.exe /cef-update                    # Check for updates
MyApp.exe /cef-update /cef-forcecheck    # Force update check
MyApp.exe /cef-update /cef-background    # Background update, no UI
MyApp.exe /cef-uninstall                 # Uninstall
MyApp.exe /cef-uninstall /cef-headless   # Silent uninstall
MyApp.exe /cef-uninstall /cef-background # Async; 109 means child started

In official builds, /cef-update, /cef-uninstall, and both
/cef-retention-* commands require enable_explicit_modes: true in the
bootstrap executable's embedded configuration. This requirement also applies
when a client DLL is present. See
Standalone Mode Security for configuration-source
and build-type behavior.

Background Update API

Client DLLs can trigger background updates using the RunInstaller export:

cpp
#include <windows.h>
#include "include/wrapper/cef_util_win.h"

// Function pointer type
typedef const char* (*RunInstallerFunc)(const char* command,
                                        const char* config_json);

void ConfirmHealthThenCheckForUpdates() {
  // Get export from bootstrap.exe
  HMODULE bootstrap = GetModuleHandle(nullptr);
  auto run_installer = reinterpret_cast<RunInstallerFunc>(
      GetProcAddress(bootstrap, "RunInstaller"));
  if (!run_installer) {
    return;  // Not running under bootstrap
  }

  // Read config from client DLL resource
  std::string config_json;
  if (!cef_util::ReadResourceData(GetCodeModuleHandle(),
                                  L"CEF_INSTALLER_CONFIG",
                                  &config_json)) {
    return;
  }

  // Copy the result before another call invalidates it on this thread.
  const char* confirmation = run_installer("launch_success", nullptr);
  std::string confirmation_json = confirmation ? confirmation : "";
  // Application helper: require valid JSON with "success" set to true.
  if (!ParseInstallerSuccess(confirmation_json)) {
    return;
  }

  // Skip the update unless launch health was confirmed successfully.
  const char* result = run_installer("update", config_json.c_str());
  // ...
}

Applications that use launch-health tracking should confirm the current launch
before checking for an update. Call launch_success after CEF reaches a
healthy steady state, such as after the first page renders successfully. The
returned pointer remains valid only until the next RunInstaller call on the
same thread, so copy and parse the result immediately. Continue with update
only when the result contains "success": true. If confirmation is
unavailable, malformed, or unsuccessful, skip the update for this run. This
keeps the current, known-good version eligible as a fallback under the existing
pruning rules.

Cefclient follows this sequence once, 60 seconds after startup. If confirmation
fails, cefclient does not check for an update during that process.

RunInstaller does not sequence these calls automatically. Applications that
want a confirmed fallback must call launch_success before update.
Confirmation does not pin a version indefinitely: the application must remain
registered for the same platform, the version must satisfy that platform's
global vmin, and a revoked version can still be removed.

launch_success needs no configuration and does not acquire the installer lock
or open the database. The bootstrap creates the version-specific launch-state
record before RunWinMain. This is separate from the version-independent
liveness record used to determine when an application registration is stale.
Updating liveness alone does not confirm a launch or protect a particular CEF
version.

RunInstaller Commands

Command Description
"install" Install CEF if not present.
"update" Check for and install updates.
"uninstall" Unregister app and prune unused versions.
"query" Query installed versions (no modifications).
"retention_dry_run" Return a store-wide retention report without modifying installer state.
"retention_apply" Recompute and apply store-wide retention.
"launch_success" Confirm launch health — the current CEF version is working. Lightweight (no lock, no database).

Extended Configuration

When calling RunInstaller, you can include extended configuration fields in
the JSON:

json
{
  "appid": "A3B9C4D5-E6F7-4A8B-9C0D-E1F2A3B4C5D6",
  "vmin": "151.1",
  "vmax": "",
  "abi_hash": "1234567890ABCDEF",

  "cdn_urls": ["https://cef-builds.spotifycdn.com/"],
  "install_path": "",
  "bundled_cef_path": "",
  "certificate_thumbprint": "",
  "force_check": false,
  "show_progress_ui": true,
  "parent_window": "0",
  "local_download_path": ""
}
Field Default Description
cdn_urls (omitted) One through three ordered HTTPS base URLs for this operation. When omitted, resolution falls through to the selected application config and then the hardcoded default. See CDN file format.
install_path "" Exclusive custom CEF installation path. A safe existing read-only directory may be used by query/automatic startup; mutations require write access and never fall back. Empty uses the standard search.
bundled_cef_path "" Path to pre-extracted bundled CEF directory. Used in-place (not copied to shared install dir). Also accepted in the client DLL's embedded CEF_INSTALLER_CONFIG resource (see Configuration Fields). See Version Selection.
certificate_thumbprint CEF certificate Non-empty expected catalog-signer thumbprint. A trusted caller may select another Windows-valid signing certificate for legitimately re-signed distributions; use a distinct install_path and matching client-side load policy.
force_check false Force CDN version check even if cache is fresh.
show_progress_ui true Show progress dialog (false for silent operation).
parent_window "0" HWND as a base-10 unsigned string. A JSON number is accepted only when finite, non-negative, integral, exactly represented, and within uintptr_t.
local_download_path "" Operation-local directory or UNC root with the CDN mirror layout. Normal hash, catalog, signature, metadata, and size validation still applies; this source does not persist revocation deltas.

cdn_urls entries are at most 2048 bytes each. They must have an HTTPS scheme
and non-empty host, cannot contain userinfo, a query, fragment, newline, or
backslash, and are normalized to a trailing-slash path prefix. Supplied order
and duplicates are preserved. An empty array, more than three entries, a
wrong type, or an invalid URL is config error 100.

RunInstaller JSON applies only to that API call. Automatic startup resolves
CEF from an embedded configuration before RunWinMain; a later API call does
not retroactively change that selection. To rely entirely on RunInstaller, do
not embed CEF_INSTALLER_CONFIG in either the client DLL or bootstrap .exe.
See Configuration Fields for where individual fields
are accepted and Config Security for application-config
source priority and fallback behavior.

Result JSON Format

json
{
  "success": true,
  "outcome": "committed",
  "libcef_path": "C:\\ProgramData\\CEF\\Versions\\151.1.0\\windows64\\Release\\libcef.dll",
  "installed_version": "151.1.0"
}

Or on error:

json
{
  "success": false,
  "outcome": "failed",
  "error_code": 103,
  "error_name": "NO_MATCHING_VERSION",
  "error_message": "No compatible CEF version found"
}

outcome is always committed, cleanup_deferred, or failed.
cleanup_deferred is successful and includes a warnings string array naming
physical or auxiliary cleanup left for a later pass. Depending on the command,
this may include trash or quarantine reclamation, retention evidence cleanup,
deferred version removal, or retry-state cleanup. Check retry_required to
determine whether the same operation should be run again. Database and
version-index publication are hard commit requirements and therefore return
failed, never deferred cleanup. Failed results always contain numeric
error_code, symbolic error_name, and a diagnostic string. Successful
results omit all error fields. The returned pointer is thread-local and
remains valid until the next call on the same thread or until that thread
exits. Copy the JSON into caller-owned storage before another same-thread call
or before the calling thread exits if it must be retained.

There is intentionally no RunInstaller("last_error"): a process-global
query would race concurrent calls and duplicate the synchronous per-call and
startup results.

How It Works

Automatic Installation

The bootstrap's startup behavior depends on whether a client DLL is present.

When a client DLL is present:

  1. Bootstrap loads client DLL with DONT_RESOLVE_DLL_REFERENCES (no code
    execution) and verifies its code signature
  2. Reads config from client DLL resource (falls back to bootstrap resource only
    when the client resource is missing)
  3. If no config found: installer is skipped, app launches normally
  4. If /cef-uninstall: runs uninstall and exits
  5. Resolves CEF — searches for a compatible version in the shared directory,
    downloads from CDN if needed (same logic as standalone auto-install). If
    /cef-update was specified, a CDN check is forced.
  6. Populates the size-gated cef_version_info_t startup result
  7. Loads the client DLL and calls RunWinMain
  8. If CEF resolution failed: libcef_path is null and
    installer_error_code/installer_error_message describe the failure. The
    client tries adjacent fallback; cancellation (108) should exit gracefully
    with "restart to retry," while no fallback is a fatal error.

The appended startup fields require
CEF_VERSION_INFO_SIZE_WITH_INSTALLER_ERROR. Clients must check size
before reading them. Resolved success has a non-null path and code 0; installer
not configured has a null path, code 0, and null message; recoverable failure
has a null path, nonzero code, and UTF-8 message. All pointers are
bootstrap-owned process-lifetime storage and must not be freed. Crashpad keys
bs-install-error, bs-install-msg, bs-install-vmin, bs-install-vmax,
bs-install-policy-denied, and bs-install-cancelled supplement this
synchronous contract. For example, the installer-specific annotations for error
103 in representative cefclient Crashpad metadata look like this:

json
{
  "bs-install-error": "103",
  "bs-install-msg": "No CDN version matches requirements (platform = windows64, version >= 151.1, ABI hash = 1671cc913eeb4ecf); no validated CDN manifest candidates were available",
  "bs-install-vmax": "unbounded",
  "bs-install-vmin": "151.1"
}

If installer resolution fails and the adjacent libcef.dll fallback is also
unavailable, cefclient additionally records a LOG_FATAL message. That field
is generated by the client and is not part of the bootstrap annotation contract.

Standalone mode (no client DLL):

  1. Bootstrap reads config from its own embedded resource.
  2. If no config found: installer is skipped, bootstrap fails (no client DLL to
    fall back to)
  3. If /cef-uninstall: runs uninstall and exits
  4. Resolves CEF — searches for a compatible version in the shared directory,
    downloads from CDN if needed. If /cef-update, forces a CDN check.
  5. On success: exits (there is no client DLL to hand off to)

In official builds, enable_explicit_modes gates explicit commands and
standalone auto-install, but not normal startup resolution with a client DLL;
see Standalone Mode Security.

Uninstall execution and outcomes

Requirement: In official builds, the /cef-uninstall command requires
enable_explicit_modes: true in the bootstrap executable's embedded
configuration, whether or not a client DLL is present. See
Standalone Mode Security. The
RunInstaller("uninstall") API remains available without this flag.

Normal behavior: When the executable is outside the selected writable
install directory, /cef-uninstall waits for the uninstall to finish. Exit 0
means the application registration and required version-index update were
committed. Some files may still be awaiting safe deletion; in that case the
operation still returns 0 and logs cleanup_deferred warnings. Database or
required index failures return the corresponding nonzero error instead.

RunInstaller("uninstall") always runs in the current process and reports the
same committed, deferred-cleanup, or failed distinction in JSON. See
Result JSON Format for the general outcome contract.

When uninstall continues in another process: The CLI starts a temporary
child and returns 109 only when:

  • /cef-background requests an asynchronous uninstall; or
  • the running executable is inside the selected install directory and must exit
    before self-removal can continue.

Exit 109 means the child started and uninstall is still pending; it does not
mean uninstall completed. A caller that supplies a valid, continuously pumped
/cef-parent window receives a relaunch_started handoff and a correlated
operation_result after the child's result and progress UI are finalized. The
terminal result does not mean the child process has exited. Callers without a
supported window receive no asynchronous completion contract. Do not
automatically rerun /cef-uninstall after 109 or missing delivery; a later
invocation is a new operation, not a status query. Use non-background external
uninstall or RunInstaller("uninstall") when synchronous completion is
required.

The temporary file set contains the executable, required runtime DLLs,
optional crash_reporter.cfg, and validated cef_uninstall_state.json. It
also contains the already-authenticated client DLL under its actual basename
only when that DLL's resource supplied the selected application config. A
bootstrap-resource selection copies no client DLL.

Failures and retries: Before deciding whether a child is necessary, the
bootstrap safely compares the executable and target directory identities. If it
cannot determine their relationship, or if a required child cannot be prepared
or started, it returns 105 without uninstalling in the current process.

If no writable target is available, uninstall returns configuration error 100
without starting a child. The diagnostic explains whether no valid install
directory was found, the directory is not writable, or the configured path is
not a directory. This includes an exclusive custom or administrator store that
is readable but cannot be modified.

Registrations never expire automatically. After registration removal and the
required index update commit, later writer operations retry deferred .trash/
and orphan cleanup.

Enterprise Policy

Managed deployments can disable the standard shared user store, select an
ordered HTTPS source list or trusted local/UNC mirror, or disable external
downloads completely. Policy is machine-wide, takes precedence over
operation-specific download sources, preserves offline provisioned/bundled
selection, and reports valid enforcement separately from malformed policy.
See ADMIN_POLICY.md for the normative registry schema,
architecture view, provisioning procedures, revocation curation, and
administrator troubleshooting.

Download Source Precedence and Robustness

For each operation, the installer selects one effective download source and
keeps that selection fixed. A source is a policy decision, local mirror, or
ordered URL list. An origin is one URL within an ordered list.

Source selection

Priority Source Behavior
1 Enterprise policy Policy can select the hardcoded default CDN, an ordered HTTPS list, a local/UNC mirror, or disabled downloads; lower priorities are ignored.
2 Operation-local mirror /cef-download-path or the equivalent operation setting uses local files without HTTP.
3 Operation URL list cdn_urls supplied to RunInstaller provides one through three ordered HTTPS origins.
4 Selected application URL list Uses cdn_urls from the application config selected by normal config precedence.
5 Hardcoded CEF CDN Used when no higher-priority source is configured.

Client-resource and bootstrap-resource configs are alternative application
sources. Normal config precedence selects one; their URL lists are never
combined.

Origin failover and version fallback

For a URL source, the revocation list and manifest independently try each
origin in order. After selecting a version, the installer keeps that version's
<archive>.sha256 sidecar and archive on the same configured origin. If the
sidecar is missing, it verifies the archive with the SHA1 from the validated
manifest. Every origin is tried for that version before version fallback is
considered.

The installer selects at most one next-best compatible, non-revoked version
from the same validated manifest when:

  • every URL origin reports that the selected archive is missing;
  • the effective local or policy mirror lacks the archive; or
  • candidate-specific hash, extraction, signature, catalog, or metadata
    verification fails after allowed recovery.

It does not change versions after cancellation, an outage across all origins,
policy denial, or a local write, permission, staging, or publication failure.
Those failures are terminal for the operation.

Resuming interrupted HTTP downloads

Only archive downloads are resumable. A partial is reusable only while both
its configured origin and its final origin after redirects still match. A safe
non-empty partial sends Range: bytes=N-. The installer appends only after a
valid 206 response whose Content-Range begins at N. If the server returns
200, the installer safely restarts from zero using that response without an
extra request.

A configured- or final-origin change deletes the old partial before new
response bytes are written. Each version candidate permits at most one
additional full request without Range, shared across all origins. That
request may recover from a final-origin change that needs another response,
invalid Range protocol including 416, a completed-response length
inconsistency, or a hash mismatch.

An interruption, timeout, or truncated transport may retain a matching safe
partial. Moving to another configured origin deletes it first. Cancellation
ends the current operation but may preserve a safe partial for a later attempt.
Local and UNC mirrors always copy from zero and never use HTTP Range.

Cache and cleanup

Each complete or partial archive is limited to 500 MB. A complete archive is
published to the cache only after length and SHA256/SHA1 validation. It is
removed after the checked version index and required application registration
have been published successfully.

Extraction, signature, metadata, and publication failures before commit retain
the archive for retry. Failure to delete it after commit reports successful
cleanup_deferred. Installer-owned complete archives and structurally valid
origin-bound partials are pruned after seven days; unrelated files are not
matched. There is no aggregate byte or LRU quota.

Shared Installation Directory

CEF uses a shared directory for installed versions. The installer resolves
directories using a single priority-ordered search that collects readable
directories and stops at the first writable one.

Candidate locations (searched in priority order):

  1. HKLM\SOFTWARE\CEF\InstallLocation registry value (requires admin to set)
  2. %ProgramFiles%\CEF\
  3. %LocalAppData%\CEF\

An install_path embedded in the client DLL is an explicit per-application
override; the operation-specific RunInstaller field has the same directory
semantics. Either takes precedence over the standard search, and only that
directory is used. An existing safe readable but non-writable directory
remains the sole read-only store. A writable directory is the sole read/write
store. Invalid, unsafe, inaccessible, or missing read-only paths fail without
default-search fallback. Query never creates or write-probes the custom
directory.

HKLM\SOFTWARE\CEF\InstallLocation is a machine-wide default candidate, not a
mandatory enterprise-policy location. It participates in the same ordered
search and source-derived role rules as Program Files and LocalAppData.
Mandatory policy values live under HKLM\SOFTWARE\Policies\CEF; see
ADMIN_POLICY.md.

install_path must be distinct from every resolved standard candidate. An
exact, normalized, or safe same-directory alias of the HKLM, Program Files, or
LocalAppData candidate is configuration error 100 and never falls through.
Omit install_path when consuming a standard store. On 64-bit Windows the
HKLM provisioning value uses the shared 64-bit registry view for every process
architecture; see ADMIN_POLICY.md.

The algorithm: For each candidate in priority order, the installer checks
if the directory is readable (add to scan list) and writable (use for
installs). The search stops at the first writable directory. This means
readable directories never include locations with lower security priority
than the writable directory.

Scenario Readable dirs (in search order) Writable dir Notes
Admin, no registry ProgramFiles ProgramFiles LocalAppData never checked
Standard user, ProgramFiles exists ProgramFiles, LocalAppData LocalAppData Sees admin-installed versions
Admin, HKLM registry set Registry path Registry path Exclusive — naturally falls out
Standard user, HKLM registry set Registry, ProgramFiles, LocalAppData LocalAppData Reads from all admin locations
Official elevated automatic startup, enable_explicit_modes: false, no registry ProgramFiles none ProgramFiles is read-only; LocalAppData is never checked
Official elevated automatic startup, enable_explicit_modes: false, HKLM registry set Registry path, ProgramFiles none Both locations are read-only; LocalAppData is never checked
Official elevated automatic startup, enable_explicit_modes: true, no registry ProgramFiles ProgramFiles Stops at ProgramFiles
Official elevated automatic startup, enable_explicit_modes: true, HKLM registry set Registry path Registry path Stops at the first writable location
install_path writable install_path install_path Exclusive custom role, regardless of path spelling
install_path read-only install_path none Exclusive selection; unregistered and untracked

Security: Directory roles come from candidate source, never ACLs or path
prefixes. Elevated operations do not search %LocalAppData%, which prevents a
privileged process from trusting content in a user-writable directory. In the
official automatic-startup rows, enable_explicit_modes is a trusted bootstrap
setting, not enterprise policy or a directory permission. This restriction is
specific to automatic startup: an elevated client may still use
RunInstaller("install") or RunInstaller("update") to write to the first
writable administrator location when the setting is false. Non-official builds
also allow administrator mutation for development and testing. See
Background Update API.

If no writable directory is available but a compatible version already exists
in a readable directory, the installer returns that version instead of failing.
This covers the common case where an admin installed CEF to %ProgramFiles%
and a non-elevated app simply needs to use it.

Directory permissions: The installer does not set or modify ACLs on the
install directory. It relies on inherited permissions from the parent directory,
which are already correct for the standard locations (%ProgramFiles% restricts
write access to administrators; %LocalAppData% restricts access to the owning
user). When using HKLM\...\InstallLocation or install_path to configure a
custom location, the administrator or app-specific installer with admin
permissions is responsible for setting appropriate permissions on that
directory before use. An install_path store is application-owned by
contract regardless of path spelling, ACLs, writability, or elevation, and is
eligible for explicit registration retention. Durable administrator
provisioning uses the source-derived HKLM role (which may point anywhere) or
Program Files; those roles are never eligible for user-liveness retention.

Directory structure:

text
CEF/
├── installer.json              # Database of registered apps
├── versions.json               # Version index for lock-free reads
├── revocation_cache.json       # Cached CDN revocation delta
├── cef_installer.log           # Installer log file
├── .cache/                     # Manifests (1-hour TTL); validated/partial archives (7-day TTL)
├── .launch/                    # Per-app launch health sentinels (survive pruning)
├── .staging/                   # Temp extraction (same-volume for atomic rename)
├── .trash/                     # Pending deletions from in-use versions
└── Versions/
    ├── 151.1.0/
    │   └── windows64/          # Platform: windows64, windows32, or windowsarm64
    │       ├── cef_version.json    # Version metadata (from build, signed in catalog)
    │       ├── catalog.cat         # Code signing catalog
    │       ├── CREDITS.html
    │       ├── LICENSE.txt
    │       ├── README.txt
    │       └── Release/            # Binaries and resources
    │           ├── libcef.dll
    │           ├── chrome_elf.dll
    │           ├── chrome_100_percent.pak
    │           ├── resources.pak
    │           ├── icudtl.dat
    │           ├── locales/
    │           └── ...
    └── 151.2.0/
        └── windows64/
            └── ...

The platform subdirectory (windows64, windows32, or windowsarm64) allows
different architectures to coexist within the same version directory.

Version Selection

The installer considers two sources of CEF versions: installed versions
from the selected installer-managed directory,
and an optional bundled version shipped with the application via
bundled_cef_path.

unchecked_cef_path is resolved before version selection and bypasses this
process entirely; see Unchecked CEF Path.

Compatibility Filtering

Both installed and bundled versions must pass these checks:

  • Version >= vmin and <= vmax (if specified)
  • Matching abi_hash (if specified, required for sandbox)
  • Matching platform (e.g., windows64)

Installed versions are signature-verified at download time (catalog +
certificate thumbprint). For bundled versions, startup verifies only that
catalog.cat exists as a regular non-reparse file. That is a
distribution-layout check: the catalog signature and member hashes are not
verified at runtime, because hashing a typical distribution (~236 files, ~370
MB) would add seconds to every app startup. Bundled integrity rests on the app
developer's signed installer/package.

channel selects what the installer downloads, not what it runs. It chooses
the stable or beta manifest URL for a network or mirror check, but channel is
not recorded in installed metadata and does not filter installed candidates.
Version range, ABI hash, platform, revocation, and launch-health state define
runtime eligibility; consequently a stable-channel app may use a compatible
version previously installed through a beta-channel request. To avoid adopting
a beta version from a newer milestone in a shared store, follow the
maximum-version guidance and cap vmax at the
approved stable milestone. This limits eligible versions but does not identify
their source: strict stable/beta isolation requires separate install_path
stores.

Selection Decision Tree

This is the single source of truth for resolution order. The first matching
branch wins; later sections describe the individual mechanisms in more detail.

text
unchecked_cef_path set and libcef.dll present there?
├─ yes → use it as-is (no version / abi / signature / revocation checks). Done.
└─ no  → continue

Build candidate sets, each filtered by vmin/vmax, abi_hash, and platform:
  • installed: shared-dir versions, EXCLUDING revoked AND crash-disqualified
  • bundled:   the bundled_cef_path version, if configured

A. Primary selection — does a qualifying candidate exist?
   ├─ installed and (non-revoked) bundled both qualify
   │     → newer wins; on a tie the installed version wins (it passed the
   │       installer's download-time catalog verification). Done.
   ├─ exactly one qualifies → use it. Done.
   └─ none qualify          → go to B

B. CDN download (skips installed + crash-disqualified versions)
   ├─ best compatible, non-revoked build installs → Done.
   ├─ candidate-specific absence/content failure
   │     → try one next-best entry from the same manifest; success → Done.
   └─ no match / revoked / terminal failure       → go to C

C. Last-resort fallback (a demoted version beats a broken app)
   ├─ a revoked bundled version exists                → use it. Done.
   ├─ else newest non-revoked crash-disqualified
   │       installed version exists                   → use it. Done.
   └─ else                                            → fail (no version)

The mechanisms used by the decision tree are detailed below:
Bundled Version Behavior,
Revocation, Unchecked CEF Path, and
Launch Health Tracking (crash-disqualification).

Bundled Version Behavior

When bundled_cef_path is specified (via the client DLL's embedded
CEF_INSTALLER_CONFIG resource or RunInstaller JSON):

  • The bundled directory must contain Release/libcef.dll, cef_version.json,
    and the package's signed catalog.cat. Runtime checks only that the catalog
    is present as a regular non-reparse file; signature/member verification is the
    packaging pipeline's responsibility.
  • The bundled version is used in-place — it is never copied to the shared
    install directory. This is intentional: CDN-installed versions have an
    additional installer-enforced catalog-verification step.
  • Bundled distributions use the revocation behavior summarized below. The
    decision tree defines their exact ordering
    relative to installed and downloaded versions.

Revocation

The CDN publishes a revocation list (revoked.json) of versions with known
security issues. Revoked versions are handled differently depending on source:

Source Revocation behavior
Installed Always excluded — never selected on any branch of the decision tree, including the last resort. Pruned on next install/update.
CDN Blocked from download even if CDN still lists them (caching delay).
Bundled Demoted, not excluded — drops out of primary selection but survives to the last resort (branch C).

Query Command

The query command uses the same selection logic (installed vs. bundled, newer
wins, installed wins ties) and does not contact the CDN. It reads all readable
directories' valid versions.json indexes and does not require a writable
directory. A missing or corrupt index is not replaced by a directory scan on
the query path; query remains index-only and recovery belongs to automatic
startup or the next writer-locked mutation. Revocation is checked using the
compiled-in baseline and any disk-cached CDN delta (no network required).
Revoked bundled versions are demoted, matching install/update behavior. It is a
lightweight, offline-capable operation.

Install, Update, and Read-only Behavior

Operation Local behavior Version-manifest check Possible writes
Automatic startup Uses the best compatible candidate Only after a local miss Install, register, and prune when writable
install Uses the best compatible candidate Only after a local miss Install, register, and prune
update Keeps the best candidate as a fallback Always; cached or remote as permitted Install, register, and prune
query and read-only resolution Reports the best local candidate Never None

For update, a manifest entry that is the same, older, unavailable, or revoked
leaves the local result unchanged and returns a successful no-op. force_check
bypasses cache freshness but does not force a download.

What all operations validate: Every operation uses the same side-effect-free
offline selector. It receives installed candidates in directory-priority order,
optional bundled metadata, compatibility requirements, effective revocations,
and optional launch-health disqualifications. For duplicate (version, platform) entries, the first valid candidate from the highest-priority
directory wins.

Before selection, each indexed installed candidate is checked against its
per-version metadata, required catalog and libcef.dll files, and reparse-safe
canonical path. These checks add filesystem work for each indexed candidate but
do not hash the distribution or reverify its signature. Launch-health
disqualification applies only to installed candidates; the app-shipped bundled
candidate remains eligible as a fallback. Results retain whether the selected
version was installed or bundled and whether it represents a rollback.

Read-only guarantees: Query and read-only resolution do not repair indexes,
reconcile state, register applications, prune versions, reclaim trash, or write
caches. Bundled CEF remains eligible when no writable directory exists.

Startup concurrency: Automatic startup performs offline selection before
acquiring the writer mutex. When it selects an installed version, it holds a
no-delete handle on Release/libcef.dll through RunWinMain, preventing a
concurrent prune from moving that version. If removal wins the race, startup
rereads the indexes and selects again.

Emergency startup recovery: If normal automatic startup finds no usable
indexed or bundled candidate, it may scan roots whose index is confirmed missing
or corrupt. A valid index—including a valid empty index—remains authoritative
and is never supplemented by scanning. An integrity failure does not delete the
damaged index; an inconclusive I/O failure fails closed without scanning.

The recovery scan examines at most four roots and 128 version directories, with
a soft 500 ms elapsed-time budget because synchronous Windows filesystem calls
cannot be interrupted. Every discovered candidate passes the normal metadata,
distribution, path, platform, ABI, range, revocation, launch-health, and lease
checks. Recovery does not acquire the writer mutex, access the network, modify
state, or repair the index. query never uses this recovery path.

Permanent repair occurs the next time a writable install, update, or
uninstall operation acquires the writer mutex—possibly during the same
automatic startup if the emergency scan finds no usable candidate. The writer
rescans and validates the on-disk version directories, then automatically
rebuilds, atomically writes, and rereads versions.json. Invalid version
directories are omitted. If the replacement index cannot be written,
atomically published, reread, or validated, the writer operation stops with a
recovery error.

Revocation refresh: Effective revocations combine the compiled baseline with
every valid cache in the readable-directory set. A successful additive refresh
is cached only in the selected writable directory. A recent writable-store cache
avoids HTTP only when both its integrity footer and JSON payload validate.

When that cache is stale or invalid and startup has a usable local candidate,
startup makes one synchronous refresh attempt across the effective ordered
cdn_urls list. It tries each origin until one returns a valid revoked.json
or the shared 5-second wall-clock deadline expires; a slow earlier origin can
therefore consume the budget before a later origin is reached. Each request has
3-second connect/resolve and 2-second receive/response limits. Internally, the
launch-sensitive request uses asynchronous WinHTTP, tries one address per
origin, and cancels at the shared deadline so DNS, connection, headers, and body
do not multiply the budget. If no origin succeeds, startup uses the compiled
baseline plus valid cached revocations and records an integrity-protected,
source-specific 15-minute backoff.

Example Scenarios

CDN builds nightly, app ships on a 2-week cycle:

Installed Bundled Bundled revoked? CDN available? Result
v151.1 v151.2 No — Bundled v151.2 (newer)
v151.2 v151.1 No — Installed v151.2 (newer)
v151.2 v151.2 No — Installed v151.2 (tie → installed wins)
v151.1 v151.2 Yes — Installed v151.1 (revoked bundled demoted)
— v151.2 No — Bundled v151.2
— v151.2 Yes v151.3 ✓ CDN v151.3
— v151.2 Yes fails Bundled v151.2 (last resort)
— — — v151.3 ✓ CDN v151.3
— — — fails Error

Unchecked CEF Path

The unchecked_cef_path config field provides a fast path for applications that
bundle libcef.dll alongside their own binaries and want to skip the installer
entirely.

When set, the installer checks for libcef.dll directly in the specified
directory before any other resolution (shared directory scan, CDN download,
bundled version comparison). No version range, ABI hash, platform, signature,
or revocation checks are performed — the application is responsible for the
integrity of the DLL at this path.

json
{
  "appid": "A3B9C4D5-E6F7-4A8B-9C0D-E1F2A3B4C5D6",
  "vmin": "151.1",
  "abi_hash": "1234567890ABCDEF",
  "unchecked_cef_path": "."
}

Caution with ".": This setting places an unchecked libcef.dll beside the
client DLL and makes it the intentional first choice, ahead of installer-managed
or bundled versions. That adjacent DLL can be dangerous for a client that does
not strictly control load ordering. The client must explicitly load the exact
libcef_path returned by the bootstrap, as the cefclient reference
implementation does, before calling any CEF function. Because libcef.dll
is delay-loaded, a premature CEF call can implicitly load the adjacent DLL by
name. Without an adjacent DLL, the same bug would normally fail visibly instead
of silently loading an unintended copy. Use "." only when this override is
intentional, its directory is controlled, and the client enforces that ordering.

Key behaviors:

  • Relative paths are resolved against the directory containing the client
    DLL that provided the config. "." means "libcef.dll is next to the client
    DLL."
  • Client DLL only: The field is only read from the client DLL's embedded
    CEF_INSTALLER_CONFIG resource. It is ignored in the bootstrap .exe's
    resource. A key supplied in RunInstaller JSON is ignored and does not
    enable the bootstrap startup fast path.
  • Fallback: If libcef.dll is not found at the specified path, the
    installer falls through to the normal resolution flow (shared directory, CDN
    download).
  • Uninstall: The field is ignored for /cef-uninstall — uninstall always
    proceeds through the normal database and pruning flow.

Contrast with bundled_cef_path: bundled_cef_path (accepted from the
client DLL's embedded CEF_INSTALLER_CONFIG resource and RunInstaller JSON)
points to a full CEF distribution with cef_version.json, catalog.cat, and a
Release/ subdirectory. It participates in version selection (newer wins,
installed wins ties) and revocation demotion. unchecked_cef_path is simpler —
it points directly at the directory containing libcef.dll with no metadata
requirements and no version comparison.

Contrast with no CEF_INSTALLER_CONFIG resource: When neither the client
DLL nor bootstrap executable provides an installer config, the bootstrap skips
CEF resolution and validation entirely and reports “installer not configured”
to the client (null path, error 0). The client then decides how to locate CEF;
for example, cefclient falls back to an adjacent libcef.dll.
unchecked_cef_path is different: it explicitly asks the bootstrap to return a
particular libcef.dll, and if that file is absent, the still-configured
installer continues with normal installed, bundled, or CDN resolution.

Progress Notifications

When parent_window is set in the extended config, progress updates are sent
via WM_COPYDATA:

cpp
// COPYDATASTRUCT.dwData = 0x43454649 ('CEFI')
// COPYDATASTRUCT.lpData = UTF-8 JSON string:
{
  "step_name": "downloading",  // Also "checking", "extracting", "verifying", "installing", "committing", "cleaning"
  "step": 4,
  "total_steps": 9,
  "bytes_done": 1048576,
  "bytes_total": 10485760,
  "message": "Downloading..."
}

step is zero-based, and total_steps is the final step index (cleaning,
currently 9), not a step count. A notification with step == total_steps means
that the operation reached the cleaning phase; it does not indicate success or
guarantee that no later error occurred. Read-only operations and other
early-return paths report only the work they actually perform. The CEFI
stream has no terminal notification. Determine synchronous outcomes from the
RunInstaller result or process exit; asynchronous uninstall uses the separate
CEFR protocol below. This allows client applications to display custom
progress UI instead of the built-in dialog.

Asynchronous uninstall lifecycle

For a relaunched /cef-uninstall with a valid /cef-parent, WM_COPYDATA
uses dwData = 0x43454652 (CEFR) for two UTF-8 JSON events:

json
{"protocol_version":1,"event":"relaunch_started","operation_id":"0123456789abcdef0123456789abcdef","child_pid":1234}
{"protocol_version":1,"event":"operation_result","operation_id":"0123456789abcdef0123456789abcdef","command":"uninstall","success":true,"outcome":"committed","exit_code":0}

The public operation ID is 32 lowercase hexadecimal characters. It correlates
events but does not authenticate either process. operation_result extends the
normal result JSON: success outcomes are committed or cleanup_deferred;
failures include matching error_code, error_name, and error_message.
Success uses exit code 0. Failure codes are restricted to the named stable
installer errors 100-108, 110-119, and 199. Exit 109 is reserved for the
original process and is never a child terminal; arbitrary integers paired with
UNKNOWN_ERROR are malformed. Warnings are optional. Retention-only result
fields are not emitted for uninstall.

Each payload is at most 32 KiB including exactly one trailing NUL. Failure text
is at most 4096 UTF-8 bytes; at most 32 warnings of 1024 bytes each are sent.
diagnostics_truncated:true reports any reduction. Dispatch by dwData before
parsing JSON, copy the bytes, return promptly, and parse outside the window
procedure. Lifecycle return values—including 2—are ignored and cannot cancel
work. Each send is one attempt bounded by 500 ms with no retry.

Events from separate operations may interleave, and a terminal can arrive
before its handoff. Buffer terminal-first events by operation ID, attribute
them only after the matching handoff, and keep only the first valid correlated
terminal. Bound and expire correlation state because the channel is not
authenticated, prefer evicting orphan terminals, and consume or remove entries
when the caller no longer needs them. An orphan terminal remains indeterminate.
child_pid is diagnostic metadata, not a durable process handle or wait
contract.

The terminal is sent after progress UI closure, and no later progress is
emitted, but it reports logical result/UI finalization rather than child exit.
cleaning progress is not completion. Delivery timeout/error, parent window
destruction or reuse, UIPI rejection, process initialization failure, invalid
trusted state, crash, forced termination, and power loss can leave completion
indeterminate. A higher-integrity receiver must explicitly allow WM_COPYDATA
with ChangeWindowMessageFilterEx; the installer does not change its filter.

Cancellation via Parent Window

The parent window can request cancellation by returning
kWmCopyDataResultCancel (2) from its WM_COPYDATA handler:

cpp
// 'CEFI' in little-endian — identifies CEF installer progress messages.
constexpr ULONG_PTR kCefInstallerProgressId = 0x43454649;
// Return this from WM_COPYDATA to request cancellation.
constexpr LRESULT kCefInstallerCancel = 2;

case WM_COPYDATA: {
  COPYDATASTRUCT* cds = reinterpret_cast<COPYDATASTRUCT*>(lParam);
  if (cds->dwData == kCefInstallerProgressId) {
    // Process progress JSON...

    // Return kCefInstallerCancel to cancel the installation.
    if (user_wants_cancel) {
      return kCefInstallerCancel;
    }
  }
  return TRUE;  // Message handled, continue installation
}

Only the specific sentinel value (2) triggers cancellation. Standard
WM_COPYDATA return values (TRUE = handled, FALSE = not handled), timeouts,
and send errors are all treated as "continue". This means a well-behaved parent
following MSDN conventions will never accidentally cancel the install.

Parent Window Handle (Cross-Process Security)

The parent_window HWND is treated as untrusted input from an external
process. The following table describes how the installer's progress dialog
interacts with that window:

Aspect Progress dialog behavior
Ownership No owner relationship established (CreateDialogParamW receives NULL)
Window state Never modified (EnableWindow, SetForegroundWindow not called)
Positioning Read-only via GetWindowRect for centering the progress dialog
Validation IsWindow(), IsWindowVisible(), IsIconic(), MonitorFromRect() checks
Dialog lifetime Independent of parent; the dialog survives if the parent is destroyed
Z-order Always topmost for visibility regardless of parent

The HWND is only a positioning hint and progress-notification destination. Its
window procedure may request cancellation, but cannot control another process
or window through the installer. During retention apply, cancellation after
the committing state begins is deferred until the next safe checkpoint before
physical cleanup. See Config & Input Parsing
for the cross-process trust boundary and security invariants.

Exit Codes

When running in standalone installer mode (/cef-update, /cef-uninstall,
/cef-retention-dry-run, or /cef-retention-apply), the process exit code
indicates success, pending work, or failure.

Installer-specific exit codes use the 100-199 range to avoid conflicts with
cef_resultcode_t values (Chrome codes: 0-40+, sandbox codes: 7006+).

The authoritative numeric values and their meanings are defined alongside the
constants in installer_constants.h. For enterprise
policy outcomes and remediation, see ADMIN_POLICY.md.

Concurrent Access

Multiple processes can read or use the same CEF store at once. The installer
keeps those reads responsive while allowing only one process at a time to
change a particular writable store. See
Install, Update, and Read-only Behavior
for when query and automatic startup remain read-only or enter a writer path.

Protecting a version that is in use

When automatic startup selects an installed version, it keeps
Release/libcef.dll open without permission to delete it. Other processes may
use the same version at the same time. If a writer later removes that version,
it first removes the version from versions.json so new readers cannot select
it. The writer then tries to move the version directory to .trash/. A running
application's open file prevents that move, so physical cleanup is deferred
until after RunWinMain returns; the running application is not disrupted.

Publishing changes safely

A named mutex serializes changes to each writable install directory. Writers
publish durable state in this order:

Change Publication order
Add a version Validate and publish the complete version directory, atomically publish and reread the expanded versions.json, then publish the app registration.
Remove a version Publish registration removal when required, atomically publish and reread the reduced versions.json, move the directory to .trash/, then delete it. Pruning starts with the index update.

If a durable step fails, the installer stops before starting the next step; it
does not try to roll back work already published. This ordering ensures that
versions.json never points to a directory that removal has already moved.
Failure to move or delete files after the required database and index changes
is a successful cleanup_deferred outcome, not a failed logical operation. A
running application and a temporary access-denied error are common reasons for
deferred cleanup. See Result JSON Format for how to handle
that outcome.

Index recovery and existing destinations

Every versions.json update is written atomically, reread with the production
parser, and compared with the intended contents. A writer-locked install,
update, or uninstall automatically rebuilds a missing or damaged index as
described in
Install, Update, and Read-only Behavior.

When the existing index is valid, it remains authoritative. A version directory
that is not listed in the index is treated as an orphan and quarantined rather
than silently added back. Recovery never invents an app registration. Invalid
directory names and version-level reparse points are skipped so they do not
block unrelated changes, but they produce deferred-cleanup warnings. A
persistent artifact keeps later write results at cleanup_deferred until it is
removed.

Once an install has downloaded, extracted, and catalog-verified a staging
distribution, any safe regular-file or directory target already present at the
canonical destination is moved opaquely to the reparse-safe .trash/ directory.
The installer does not read or classify the existing target's contents. It then
renames the current operation's verified staging directory into place before
publishing the expanded index, so existing bytes never win this collision.

A quarantine failure is a hard error that preserves both the target and verified
source at their original paths. A replacement-rename failure is also hard: the
old target remains under .trash/, verified staging remains at its source, and
the canonical destination is absent. The old target is never rolled back.
Physical reclamation of a successfully quarantined target is best-effort; if
logical directory and index publication otherwise complete, remaining trash is
reported as cleanup_deferred and retried by a later writer.

Staging is owned by the current controller operation and is cleaned up
best-effort on return. It is never persisted as trusted state. A later retry may
reuse verified archive-cache bytes, but it re-extracts the archive and repeats
catalog verification before attempting publication again.

Lock waits and failure behavior

  • The mutex name is derived from the writable install directory, so independent
    stores do not block one another.
  • Explicit write commands wait up to 30 seconds by default. If the current
    writer does not finish in time, the waiting command returns
    kExitCodeLockTimeout.
  • Automatic startup waits at most five seconds when it needs the writer lock.
    On timeout, it returns kExitCodeLockTimeout (107). A client DLL receives the
    code and diagnostic through cef_version_info_t; standalone mode exits 107.
    If enabled, the progress UI shows localizable “busy” guidance and the error
    code instead of the diagnostic.
  • A local-hit startup refreshes its app registration only when the entry is
    missing or changed. It makes a zero-timeout lock attempt and skips the refresh
    if another writer is busy, so application launch is not delayed.
  • If a writer exits while holding the mutex, Windows gives ownership to one
    waiter. That process records the abandoned lock and reconciles installer
    state before continuing.
  • A version required by an app registered in the same install directory is
    never deleted.

Version Pruning

Unused CEF versions are automatically cleaned up in the writable directory:

  • Each app's required CEF version range is registered by the installer in
    installer.json
  • Apps are registered by (uuid, platform) - the same app on different
    architectures are separate entries
  • On uninstall, the app is unregistered and its now-unused version directories
    are pruned immediately. Valid crash-history records for that app remain for
    up to 90 days so a reinstall does not immediately retry known-bad builds.
  • Versions not required by any app registered in that install directory are
    deleted
  • Revoked installed versions are prunable even when a registration or confirmed
    fallback would otherwise protect them; a usable non-revoked fallback is
    retained when available
  • Pruning covers every platform subdirectory; see
    Pruning Protection for the cross-platform rules
  • Pruning happens during uninstall, after a successful install or update, and
    after eligible clean client exits as described in
    Post-Exit Pruning

Explicit Registration Retention

Registration expiry is disabled by default. Normal commands never remove a
registration because it is old. This includes automatic startup, install,
update, uninstall, query, post-exit pruning, and ordinary prune.

Retention runs only when explicitly requested through one of these commands:

  • CLI: /cef-retention-dry-run or /cef-retention-apply
  • API: RunInstaller("retention_dry_run", ...) or
    RunInstaller("retention_apply", ...)

An application-owned maintenance UI or trusted updater should:

  1. Run retention_dry_run.
  2. Show the affected registrations and versions.
  3. Warn that removed registrations may require a future download and may break
    offline or download-restricted applications.
  4. Run retention_apply only after the user or operator confirms.
  5. Retry when retry_required is true. Treat cleanup_deferred as a successful
    logical change with physical cleanup still outstanding.

The CLI is intended for trusted support tools and managed scripts running as
the affected user. The bootstrap must enable explicit commands through its
embedded enable_explicit_modes setting.

Do not run retention automatically at startup, after exit, from a system-wide
elevated task, or in response to disk pressure. Those uses require a separate
policy because they have different availability risks.

Eligible stores

Retention is based on store ownership, not on whether the current process is
elevated or can write to a path.

Store Retention allowed?
Per-user default store Yes
Application-owned install_path store Yes
HKLM-configured store No
Program Files provisioning store No

Provisioning stores are rejected before write probing, file logging, or lock
acquisition. The same rules apply to the API and CLI.

Age and launch evidence

The default age threshold is 180 days. A request may override it with
max_age_days in API JSON or /cef-max-age-days on the CLI. The accepted range
is 90 through 3650 days, inclusive. Evidence is stale when its age is equal to
or greater than the threshold.

Evidence is evaluated separately for each (appid, platform). The newest
canonical, integrity-valid timestamp wins: either a health sentinel's
pid_start_time or a version-less liveness record's last_launch.

Evidence Result
Valid; age is below the threshold Registration is retained
Valid; age meets or exceeds the threshold Registration is a removal candidate
Missing or zero Age is unknown; registration is retained
Malformed, integrity-failed, mismatched, or noncanonical Age is unknown; registration is retained
Future-dated Age is unknown; registration is retained

Unknown evidence always protects the registration.

How retention runs

  1. Preview the current plan with dry-run. Dry-run takes the writer lock and
    is a read-only operation that produces a report without changing installer
    state. Each evidence file is read atomically, but the reads do not form one
    simultaneous global snapshot.

  2. Recompute and validate the plan during apply. Apply takes the writer lock
    independently and does not use the earlier dry-run report as input. It first
    loads any interrupted cleanup recorded in installer-owned
    retention_pending.json into its working state, then builds a preliminary
    plan. After a progress checkpoint, it reads the launch evidence again and
    builds the final plan.

    The writer lock prevents concurrent installer mutations, but application
    launches can update launch-success or liveness evidence without taking that
    lock, including while the progress callback is running. Apply compares the
    preliminary and final registration-removal and expected version-removal
    sets. If either changed, it makes no changes and returns
    kExitCodeRetentionSnapshotChanged, retry_required:true, and the updated
    report for review.

  3. Order overlapping launches and enter the commit phase. There is no
    single instant at which every evidence file is observed. A launch published
    before the direct final read of its canonical liveness file can protect that
    registration. A launch published afterward is ordered after this retention
    operation, even if other evidence files are still being scanned. Legacy
    health files are ordered by their own verified reads. Apply then offers one
    final cancellation checkpoint. Once accepted, the progress state changes to
    committing, and database and index publication are non-cancellable. Apply
    does not repeatedly replan or roll back a published database because newer
    evidence appeared after its final read.

  4. Publish the logical changes. Before changing the database, apply records
    the exact version-cleanup scope in retention_pending.json. It saves all
    registration removals in one checked database update, then publishes the
    reduced version index before moving any version directories. If the database
    update fails, the index, versions, and evidence remain unchanged, and apply
    attempts to restore the previous pending scope. If that restore fails, the
    result requires a retry and includes a warning. If index publication fails
    after the database update, the result reports success:false,
    outcome:"failed", registrations_committed:true,
    versions_pruned:false, and retry_required:true.

  5. Finish physical cleanup, or preserve it for retry. Retention removes only
    versions made newly unreferenced by the registration removals. Pre-existing
    unreferenced indexed versions and unrelated revoked versions appear in the
    report but are not removed. Ordinary pruning may remove them during
    uninstall, after a qualifying install or update, or after an eligible clean
    client exit; see Version Pruning.

    Evidence is deleted only after logical publication. Apply verifies the exact
    payload, integrity status, and handle-resolved .launch parent against its
    final observation, using the same opened file for the checks and deletion.
    New or replaced evidence is preserved, as is any target reached by
    redirecting an intermediate directory through a junction or symlink.

    Live leases, failed directory moves, deferred cancellation, or evidence
    deletion failures may return successful cleanup_deferred with warnings. A
    cancellation request made during committing is remembered, disables the
    Cancel button, and stops physical cleanup at the next safe checkpoint after
    logical publication. retention_pending.json lets a later apply finish the
    exact remaining scope even if the registrations are gone or the index has
    already been reduced. The retry scans on-disk metadata for pending versions
    missing from the index and clears the pending record after cleanup succeeds.

  6. Return a deterministic report. Reports sort registrations by
    appid/platform and versions by version/platform. Each registration includes
    its full appid, range, ABI, evidence kind, timestamp, age, decision, and
    reason. Each version includes its requirement state before and after
    retention, its compatibility or protection reason, and whether removal is
    expected.

    reason is a stable symbolic value suitable for use as a localization key.
    Registration reasons are provisioning_store_ineligible,
    database_pruning_blocked, invalid_retention_options, invalid_evidence,
    missing_evidence, future_evidence, stale_evidence, and
    fresh_evidence. Version reasons are revoked,
    required_by_remaining_registration, confirmed_launch_protection,
    newly_unreferenced, and already_unreferenced.

    Optional diagnostic text is for troubleshooting only; do not use it for
    localization or program logic. Headless CLI and API calls return JSON.
    Normal CLI use returns deterministic text. For retention, RunInstaller
    accepts only install_path, max_age_days, and log_level. Its result
    pointer remains valid until the next call on the same thread or until that
    thread exits.

Availability warning

Retention trades application availability for recovered disk space. It is not
proof that an application was uninstalled. Applying it may force a dormant
application to download CEF again, and may permanently break an application
that is offline or prohibited from downloading. Use dry-run first.

Launch Health Tracking

Launch-health tracking is an optional local safeguard against a CEF version
that repeatedly fails during application startup. For opted-in apps, it records
per-version launch outcomes in sentinel files and, after repeated failures,
removes the failing version from normal selection so a later launch can choose
a compatible fallback. Confirmed versions are also protected from pruning.
This per-device recovery mechanism complements, but does not replace,
fleet-wide revocation of a known-bad build.

Launch-health rollback is opt-in per app. The launch_health field accepts:

  • "off" (default): no health-state read for selection, no health sentinel, and
    no local crash rollback.
  • "explicit" (recommended): only RunInstaller("launch_success") confirms.
    Every ordinary RunWinMain return is neutral; only a dead process without
    confirmation increments the failure count.
  • "exit_code": exit 0 confirms, neutral exit codes preserve history, other
    exits or process death count as failure, and launch_success may confirm
    early.

Opt in only when the app is effectively single-instance for a user-data
directory. Apps with conventional exit codes can use exit_code; apps with
custom nonzero codes should confirm reliably and use explicit. Partial
explicit adoption is unsafe: unconfirmed ordinary exits never reset earlier
failures, so failures can accumulate across otherwise healthy runs.

The default is off because local crash attribution is heuristic; fleet-wide
bad builds belong on the revocation list. With tracking off, a startup-crashing
installed version can remain selected indefinitely. Recovery must come from a
launch-time revocation, another app installing a newer compatible version, or
an app update that raises vmin or ships a newer bundle. An in-app
RunInstaller("update") cannot help when startup never reaches that call.
Opted-in apps should keep their own confirmed fallback and assume deeper
rollback may require a CDN download or bundled fallback.

For opted-in apps, health evaluation runs during every install or update
check, including a background RunInstaller("update") call, so background
updates also keep versions disqualified by repeated launch failures out of
normal selection.

When automatic startup resolves exclusively from a read-only store and no
writable installer state root exists, launch-health tracking is omitted for
that launch.

An app's launch_health value controls health tracking and version selection
for that app; it does not hide .launch/ files belonging to other registered
apps in the same store. Shared maintenance still honors those files regardless
of the current app's mode: confirmed health sentinels can protect versions from
pruning, and retention evaluates each registration's health or liveness
evidence independently. See Pruning Protection and
Explicit Registration Retention.

Launch-state files

The .launch/ directory contains two file formats because it supports two
independent policies:

  • A versioned health sentinel records whether a particular CEF version
    started successfully. Opted-in launch-health rollback uses it to disqualify
    repeatedly failing versions and protect confirmed versions from pruning.
  • A versionless liveness record records when the app last launched.
    Registration retention uses it to decide
    whether a registration may be stale, even when launch-health rollback is
    off.

Keeping them separate lets liveness be published and refreshed independently
of launch-health mode, while preserving crash and confirmation history for each
CEF version across app launches and version changes.

Versioned health sentinel. This per-app, per-version state file is written
to the .launch/ directory under the install directory immediately before
RunWinMain:

text
<install_dir>/.launch/<appid_hash>_<version>_<platform>

appid_hash is the first 16 hex chars of SHA-1(appid). The version and
platform are embedded in both the filename and the JSON body. The file uses
a CRC32 integrity footer (same format as other installer files):

json
{
  "appid": "A3B9C4D5-E6F7-4A8B-9C0D-E1F2A3B4C5D6",
  "version": "151.1.0",
  "platform": "windows64",
  "pid": 12345,
  "pid_start_time": "133612345678901234",
  "consecutive_failures": 2,
  "running": true,
  "confirmed": false,
  "last_update": "133612345678901999"
}

The pid and pid_start_time fields prevent stale updates. After
RunWinMain, the bootstrap updates the launch state only if the PID in the
file still matches the current process. If a newer instance has already
overwritten the file, the exiting process skips the update.
last_update is a required, nonzero decimal Windows-epoch FILETIME used only
for launch-history garbage collection. The common writer replaces any caller
value with current time immediately before every pre-launch, explicit
launch-success, normal-exit, or neutral-exit publication. Failure to obtain a
nonzero time fails that publication without replacing prior state. A clean
exit after explicit launch success remains a no-op and does not publish twice.

Because files live in a single .launch/ directory (not inside version
directories), crash history survives version pruning. This prevents a doom
loop where a crashed version is pruned, re-downloaded from the CDN, and
crashes again.

Writes use a same-directory temporary file and atomic replacement. If the
pre-launch write fails, the error is logged and the client launches untracked
for that run. CRC detects corruption but is not authentication. Both launch
record formats require a valid integrity footer; footerless files and health
sentinels missing last_update are invalid old-schema state. Read-only paths
ignore and preserve invalid records, while a writer-locked repair pass may
remove them.

Versionless liveness record. All non-bundled main-process launches write
this retention-liveness record immediately before the health sentinel (when
enabled) and before RunWinMain:

text
<install_dir>/.launch/<appid_hash>_<platform>

Its bounded, integrity-protected body contains only appid, platform, and a
decimal Windows-epoch last_launch timestamp. The version-less two-segment
filename cannot be parsed as a health sentinel or protect/disqualify a version.
Liveness publication and health-sentinel publication have independent success
semantics; liveness success never substitutes for a failed health sentinel.
The bootstrap rewrites it when missing, malformed, identity-mismatched,
future-dated, or at least 30 days old; the content timestamp, not mtime, is
authoritative. This records liveness for later retention policy without
deleting registrations based on age.

Mode Classification

This applies only to the opt-in modes (explicit and exit_code).

After RunWinMain returns, the bootstrap classifies the exit code:

Event Mode Effect
Explicit confirmation (RunInstaller("launch_success")) Both opted-in modes Write running=false, confirmed=true, failures=0.
Ordinary return explicit Write running=false, confirmed=false; preserve failures regardless of exit code.
Exit 0 exit_code Confirm and reset failures.
Neutral exit (for example 21, profile in use) exit_code Write unconfirmed and preserve failures.
Other exit exit_code Leave running=true; count it on the next launch.

Clients using either opted-in mode can call
RunInstaller("launch_success", nullptr) during RunWinMain; no config is
needed because the bootstrap records the active sentinel before calling the
client. In explicit mode this call is the only way to confirm health. In
exit_code mode it can confirm health before process exit. Either way, an
app-level crash after confirmation does not penalize the CEF version, while a
crash before confirmation still can. See the mode table above for behavior when
the call is omitted and RunInstaller Commands for the
API contract.

Rollback Behavior

This applies only to the opt-in modes (explicit and exit_code).

On each launch, the installer scans .launch/ for the app's files, covering
both installed and previously-pruned versions, to build the
crash-disqualified set:

  1. If a sentinel shows running=true and the process is dead, the version is
    treated as having crashed one more time. A pid_start_time before the
    estimated current Windows boot boundary is indeterminate and never counts as
    a crash. If boot evidence is unavailable or inconsistent, classification also
    fails open to indeterminate.
  2. If three or more consecutive crashes are projected, the version is
    crash-disqualified.

Disqualified versions then feed into the
Selection Decision Tree: they are removed from
primary selection (so the next-best version is chosen instead) and from the CDN
skip list, but a non-revoked disqualified version remains available as a last
resort if nothing else qualifies. See the decision tree for the exact ordering
relative to revoked and bundled versions.

Cleanup Strategy

This applies only to the opt-in modes (explicit and exit_code). Shared
cleanup performed by pruning is described separately in
Stale File GC.

On successful exit (version confirmed), the bootstrap deletes the .launch/
files for older versions that were previously confirmed (qualifying those
versions for pruning). It only deletes confirmed-state files; .launch/ files
recording crash history (running=true, or failures>0) are kept.
This confirmed-file cleanup is separate from the 90-day age grace applied to
orphaned and below-vmin crash history.

This is about the small .launch/ sentinel files, not the version directories.
A crashed version directory is not protected from pruning — only confirmed
versions are (see Pruning Protection) — so a known-bad
version's bits can be reclaimed. Its crash-history file lives separately in
.launch/ and survives that pruning, which is what keeps the version
disqualified and off the CDN re-download list, preventing a crash-loop.

Cleanup only affects the current app's files. If apps A and B both have
confirmed files for v1, app A confirming v2 deletes app A's v1 file but
leaves app B's v1 file intact.

Stale File GC

During ordinary pruning, valid orphaned records (no app registered for their
(appid, platform)) and records below the minimum vmin of all registered
apps on their platform become eligible for age cleanup. They are retained
while their integrity-verified content timestamp is less than 90 days old and
deleted when its age is 90 days or more. Health records use last_update;
liveness records use last_launch. Filesystem timestamps are ignored.
Consequently, a record that is already old may be deleted the first time it
becomes eligible. A future timestamp or unavailable clock preserves the file.

Writer-locked prune also repairs a reparse point at .launch/, removes
final-component reparse launch files without following their targets, and
deletes schema-invalid or noncanonical files, including abandoned atomic-write
temporaries. Query and launch-health evaluation remain read-only and fail
closed on those paths. Turning launch health off for the mutating app removes
its launch records without the age grace. Explicit registration-retention apply
uses its separate retention policy and does not run ordinary launch-record age
GC.

Pruning Protection

Versions with a confirmed launch state (running=false, confirmed=true,
failures=0) in .launch/ are protected from pruning. The check spans all
apps — if any app has a confirmed file for a version, that version is kept.
This ensures a shared version is not deleted while any app registered in the
same install directory considers it good. Registrations and protection are
intentionally not aggregated across stores.

Protection applies only to a registered app's record at or above that
platform's global vmin. An orphaned or below-vmin record never protects
version binaries, even while its recent history is retained, its timestamp is
future-dated, or the clock is unavailable. Conversely, an active registered
confirmed record at or above vmin protects its version regardless of record
age.

Protection is independent of the pruning app's mode: an off app does not
ignore another registered app's confirmed rollback target. Revocation remains
authoritative, so a revoked version is prunable despite confirmation.

Cross-platform: Pruning, and therefore launch-state handling, is not gated
to the running installer's platform. Version pruning scans all platform
subdirectories (a 64-bit installer can prune orphaned 32-bit versions and vice
versa), so protection, orphan deletion, and stale GC are evaluated for every
platform's .launch/ files — each against its own (appid, platform)
registration and that platform's vmin. Without this, a prune on one platform
could delete a version another platform's launch health is keeping as a
rollback target. The shared installer.json database provides registration
context for all platforms.

When a newer version is confirmed, the older version's launch file is
cleaned up as described in Cleanup Strategy. Once all
apps have moved on, the version loses its protection and becomes eligible for
pruning.

Post-Exit Pruning

After RunWinMain returns with a clean exit (code 0) and the launch state
is updated, the bootstrap runs a prune pass with a minimal lock timeout (1 ms)
so it never blocks exit. This cleans up versions that are no longer needed. If
the lock is unavailable (another writer is running), pruning is skipped
silently.

Before regular pruning, the post-exit pass also checks for version directories
that are missing from a valid version index. It considers only directories with
the expected Versions/<version>/ path and moves eligible directories
to .trash.

This additional cleanup is skipped when the version index is missing, corrupt,
or unreadable. Regular pruning still handles those cases as before: it may scan
Versions/, write a replacement index, and remove versions that are no longer
needed.

Windows may allow a directory containing a loaded DLL to be moved to .trash
but prevent its immediate deletion. A later install, update, or uninstall
can remove it after the DLL is no longer in use. Version discovery and index
recovery ignore .trash, so a quarantined directory cannot be selected as an
installed version.

Pruning runs only on exit code 0 — not on neutral exit codes or failures.
Deleting older confirmed launch files requires both exit code 0 and confirmed
health for the current version. In explicit mode, an ordinary exit code 0
does not satisfy that second requirement. A neutral exit such as "profile in
use" implies another instance is concurrently running with libcef.dll
loaded (or that an immediate relaunch is coming), which violates the "no
in-use file conflicts" premise that makes post-exit pruning safe. The next
clean exit, or a standalone /cef-update, prunes instead.

When RunInstaller("launch_success") has already confirmed this launch, the
post-exit handler skips the sentinel write (the confirmation is already
durable). Cleanup and pruning still run, but — as above — only on exit code 0.

Security

See SECURITY.md for the full security model, trust boundaries,
invariants, and developer checklists.

Code Signing

Downloaded CEF distributions require a Windows-valid signed catalog and a
non-empty expected certificate thumbprint before publication. Applications
using a legitimate alternative signer must also use an isolated install_path
and a matching client-side load policy. See
Signature Verification for the
authoritative trust model and invariants.

See Create a signed catalog
for distribution catalog generation, signing, and verification commands.

Config Security

Config is loaded with the following priority:

When client DLL is present:

  1. Client DLL embedded resource
  2. Bootstrap .exe embedded resource (fallback)

Standalone mode (no client DLL):

  1. Bootstrap .exe embedded resource. This is also the only source for
    enable_explicit_modes.

The selected source's cdn_urls replaces, rather than merges with, every
fallback application-config source.

An embedded resource is trusted and authoritative when present: malformed
JSON, missing/invalid fields, or an empty post-clamp range is a hard config
error and never falls through to another embedded source. Only an absent client
resource may fall back to the bootstrap resource. A missing standalone
resource reports no config; malformed JSON or invalid fields produce config
error 100.

Enterprise Provisioning

Admin/shared stores use explicitly managed, durable provisioning pins. Use a
dedicated installer bootstrap rather than elevating the application. See
Provisioning and revocation curation
for exact-version pins, signer requirements, store isolation, scheduled
updates, and offline mirrors.

Standalone Mode Security

The enable_explicit_modes flag (default false) controls whether a
bootstrap binary can be used as a standalone installer. This flag is only
read from the bootstrap .exe's own CEF_INSTALLER_CONFIG embedded
resource
— it is ignored in client DLL resources and RunInstaller JSON.
When false in official builds:

  1. Explicit commands blocked: /cef-update and /cef-uninstall are
    rejected (both standalone and with a client DLL present).
  2. Standalone auto-install blocked: Auto-install (no client DLL, no explicit
    command) is blocked.
  3. Automatic admin-store mutation blocked: A launcher with a client DLL may
    read provisioned HKLM/ProgramFiles versions or use bundled CEF, but it does
    not create, probe, register, reconcile, prune, cache, or download into an
    admin-default store and an elevated process does not continue to
    %LocalAppData%.

Without enable_explicit_modes, the binary can only function as a launcher
when a client DLL is present. CEF resolution still requires a
CEF_INSTALLER_CONFIG resource in the client DLL or bootstrap .exe —
without one, the bootstrap loads the client DLL with no installer
involvement. The RunInstaller export is always
available regardless of this flag. In non-official builds, all standalone
operations are allowed regardless of this flag (for development/testing).

Bootstrap resource Explicit commands Standalone auto-install
enable_explicit_modes: true Allowed Allowed
No flag or false Blocked Blocked
Missing or invalid Blocked Blocked

App builders who want standalone installer features must explicitly opt in
by embedding "enable_explicit_modes": true in the bootstrap .exe's
CEF_INSTALLER_CONFIG resource. See
Config & Input Parsing for the security
rationale and official-build invariants behind this gate.

Sandbox Compatibility

The abi_hash field ensures sandbox compatibility between client and CEF:

  • Hash is computed from sandbox-related struct definitions
  • Mismatched hashes prevent sandbox escape or crashes
  • Use CEF_SANDBOX_COMPAT_HASH from cef_version.h

Build Integration

The installer consumes two embedded Windows RT_RCDATA resources. Its
first-run UI can additionally be branded and localized with executable string-
table resources.

Standard binary distribution CMake example

The standard Windows binary distribution documents an opt-in
-DUSE_INSTALLER=On CMake example in its README.txt. That Release-only,
bootstrap/sandbox build focuses on cefclient and embeds a generated managed
CEF_INSTALLER_CONFIG in the client DLL while staging only the bootstrap,
client DLL, and chrome_elf.dll runtime payload. It does not modify bootstrap
resources or enable bootstrap-only explicit command-line or standalone modes.
Replace the sample application identity and follow the signing and compatible
CEF distribution requirements in this document before production use.

1. `CEF_INSTALLER_CONFIG` (client DLL or bootstrap resource)

A JSON configuration specifying the application's CEF version requirements.
See Config Security for loading priority and
Standalone Mode Security for the
enable_explicit_modes field. The default bootstrap binary does not
include this resource; apps that want bootstrap-driven CEF resolution must
embed the config in their client DLL or add it to the bootstrap .exe (at
build time via the .rc file, or post-build using a resource editor such
as ResourceHacker).

For build-time integration, add the corresponding RCDATA declaration to the
client DLL or renamed bootstrap executable's .rc file. To add or update the
resource after building, see
Embedding Resources with ResourceHacker.

2. Progress-dialog branding and localization

Applications ship a renamed bootstrap executable and may customize its icon,
version information, and installer strings alongside the embedded config.
See installer_resources.rc for the authoritative
dialog layout, string-table IDs, and default English text. The progress UI
loads these resources from the running executable, using the compiled English
text only when a lookup fails.

Override the values in the renamed executable's .rc file or with the same
resource editor used to embed CEF_INSTALLER_CONFIG. For localization, add
language-specific STRINGTABLE blocks with the same numeric IDs; LoadStringW
selects the resource for the thread UI language. The error dialog's standard
OK button is supplied and localized by Windows. Translate both
IDS_INSTALLER_ERROR_MESSAGE_FORMAT and the body resources so the error-code
label, punctuation, placeholder order, and guidance are all localized. In that
format, $1 is the localized guidance body and $2 is the invariant decimal
error code. Both placeholders are required; an invalid customized format falls
back to the complete English format so it cannot omit the numeric error code.

Installer error_message values in result JSON and startup reporting remain
unlocalized diagnostics for logs and support. The progress dialog instead
selects localized guidance from error_code and includes that numeric code for
correlation.

3. `CEF_REVOCATION_LIST` (bootstrap resource)

A compiled-in baseline revocation list embedded in the bootstrap binary.
Generated at build time by tools/make_revocation_resource.py from
tools/cefbuilds/revoked.json and cef_api_versions.json. This ensures the
installer can enforce revocation checks even without network access.

See the installer_revocation_resource target in cef/BUILD.gn.

Testing

See TESTING.md for unit, integration, E2E, manual, fuzz, and code
coverage workflows.

Troubleshooting

Installer is skipped

  • Verify CEF_INSTALLER_CONFIG resource exists in client DLL
  • Use Resource Hacker or similar to inspect the DLL

Version not found

  • Check vmin/vmax range matches available versions
  • Verify abi_hash matches if sandbox is enabled
  • Note that vmin is clamped to the bootstrap's API version (check logs for
    "clamped" messages)

Resource loading fails

  • Verify the Release/ directory (alongside libcef.dll) contains .pak files
    and locales/ subdirectory
  • CEF automatically loads resources relative to libcef.dll location

Reset launch-health history

When launch-health tracking is enabled, the bootstrap
records whether each CEF version reached a healthy state or repeatedly failed
to launch. It uses this history to avoid a crash-disqualified version and to
preserve a confirmed fallback. Reset the history only when those records no
longer reflect the application's actual health.

There is no timed disqualification expiry. Close the application and delete
its files under \.launch\ to reset local health history.
Task termination, OOM/process kills, logoff, or failure to confirm before
those events can still look like a crash. Newer versions, a rising vmin, a
clean last-resort redemption, or this manual reset provide recovery.

Logs

  • Check \cef_installer.log for detailed logs (e.g.,
    %ProgramFiles%\CEF\cef_installer.log or
    %LocalAppData%\CEF\cef_installer.log)
  • If no writable install directory is found (including when install_path
    points to a read-only directory), logs fall back to %TEMP%\cef_installer.log

Overview (`tools/clang/standalone/README.md`)

C++ Rewriter Tool

A clang-based AST rewriter for C++20 modernization. This tool performs
type-aware code transformations that cannot be done with simple regex,
such as replacing map.find(x) != map.end() with map.contains(x).

Distribution Contents

This is a self-contained distribution that includes everything needed to run:

text
cpp_rewriter/
├── bin/
│   └── cpp_rewriter           # The clang-based rewriter tool
├── lib/
│   └── clang/<version>/       # Bundled clang headers (stdarg.h, etc.)
│       └── include/
├── scripts/
│   ├── run_rewriter.py        # Main wrapper script
│   ├── apply_edits.py         # Applies generated edits to files
│   └── process_edits.py       # Deduplicates and formats edits
└── README.md

The bundled lib/clang/ headers are automatically detected and used by the
wrapper script, so the tool works without requiring a matching system clang.

Prerequisites

  • A C++ project with a compile_commands.json file
  • Python 3.6+

Generating compile_commands.json

The tool requires a compilation database to understand your project's
build configuration. Here's how to generate one for common build systems:

CMake

Important: CMAKE_EXPORT_COMPILE_COMMANDS only works with Makefile
and Ninja generators. It does NOT work with Xcode or Visual Studio generators.

bash
# Using Ninja (recommended)
cmake -G Ninja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -B build

# Using Unix Makefiles
cmake -G "Unix Makefiles" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -B build

# compile_commands.json is now in build/

If you normally use Xcode/Visual Studio, create a separate build directory
just for tooling:

bash
# Keep your Xcode build, add a parallel Ninja build for tools
cmake -G Ninja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -B build-tools
# Use build-tools/compile_commands.json with the rewriter

Meson

bash
meson setup build
# compile_commands.json is automatically generated in build/

Bear (for Make-based projects)

bash
# Install Bear: https://github.com/rizsotto/Bear
bear -- make
# compile_commands.json is generated in current directory

Ninja

bash
ninja -C build -t compdb > build/compile_commands.json

Usage

Basic Usage

bash
# Run on all files in compile_commands.json
./scripts/run_rewriter.py -p /path/to/build | ./scripts/apply_edits.py --base-dir /path/to/build

# Run on specific source directories only
./scripts/run_rewriter.py -p build src/ include/ | ./scripts/apply_edits.py --base-dir build

Filter by Path Pattern

Use --path-filter with a substring to process only matching files:

bash
# Only process files containing "src/core/" in their path
./scripts/run_rewriter.py -p build --path-filter "src/core/" | ./scripts/apply_edits.py --base-dir build

Disable Specific Transformations

bash
# Disable structured bindings transformation
./scripts/run_rewriter.py -p build --tool-args="--structured-bindings=false" | ./scripts/apply_edits.py --base-dir build

# Run only the .contains() transformation
./scripts/run_rewriter.py -p build --tool-args="--only=contains" | ./scripts/apply_edits.py --base-dir build

Verbose Output

bash
# Show progress
./scripts/run_rewriter.py -p build -v src/ 2>&1 | tee /tmp/edits.txt
./scripts/apply_edits.py --base-dir build < /tmp/edits.txt

Available Transformations

  • .contains() for associative containers
  • Structured bindings for range-for loops
  • Iterator loops to range-for
  • DISALLOW_COPY_AND_ASSIGN replacement

All transformations are enabled by default. Use --flag=false to disable specific ones,
or --only=<list> to run only specific transformations.

See TRANSFORMS.md for the complete list of command-line flags,
before/after examples, supported containers, and edge cases.

Troubleshooting

"compile_commands.json not found"

Make sure you've generated the compilation database. See the
"Generating compile_commands.json" section above.

"No files found matching..."

  • Check that the path filter matches your file paths
  • Use -v for verbose output to see which files are being processed
  • Try running without a path filter first

Transformations not applying

  • The tool only transforms code, not macro expansions
  • Some patterns may not match due to unusual formatting
  • Use -v to see error messages from the tool

Compatibility

The transformed code uses C++20 features. See TRANSFORMS.md for
minimum compiler requirements.

Building from Source

This tool is built as part of the CEF (Chromium Embedded Framework) project.
See the CEF repository for build instructions.


Overview (`tools/debug/ax_viewport_collapse/README.md`)

ax_viewport_collapse Debug Tools

Manual verification tools for the ax_viewport_collapse CefBrowserSettings feature.
These connect to a running cefclient via CDP to inspect the accessibility tree.

Prerequisites

text
python3 -m pip install websocket-client

Usage

1. Launch cefclient with remote debugging

text
cefclient.exe --remote-debugging-port=9222 --remote-allow-origins=* --ax-viewport-collapse

The --ax-viewport-collapse flag maps to CefBrowserSettings.ax_viewport_collapse = STATE_ENABLED in cefclient.

To load the test page directly:

text
cefclient.exe --remote-debugging-port=9222 --remote-allow-origins=* --ax-viewport-collapse --url=file:///<path>/test_viewport_collapse.html

2. Run the verification script

text
python3 verify_viewport_collapse.py

Or navigate to a URL first:

text
python3 verify_viewport_collapse.py --url https://example.com

Options

Flag Description
--host CDP host (default: 127.0.0.1)
--port CDP port (default: 9222)
--url Navigate to URL before fetching tree
--raw Print full JSON response instead of summary

Comparing with/without collapse

Run two cefclient instances on different ports:

text
cefclient.exe --cache-path=<path1> --remote-debugging-port=9222 --remote-allow-origins=* --ax-viewport-collapse
cefclient.exe --cache-path=<path2> --remote-debugging-port=9223 --remote-allow-origins=*
text
python3 verify_viewport_collapse.py --port 9222 --raw > with.json
python3 verify_viewport_collapse.py --port 9223 --raw > without.json
diff with.json without.json

Expected output (with collapse enabled)

text
AX Tree Summary
============================================================
Total nodes returned:  77
  Ignored:             3
  Full (with children):70
  Summary (collapsed): 4

Collapsed summary nodes (off-screen landmarks/headings):
------------------------------------------------------------
  region: "Content preferences"
  region: "Audio quality"
  region: "Display"
  region: "Storage"

Off-screen landmark sections appear as leaf nodes with role + name only. In-viewport nodes are fully serialized with children.

Files

  • verify_viewport_collapse.py — CDP script that calls Accessibility.getFullAXTree() and prints a summary or raw JSON
  • test_viewport_collapse.html — Test page with 6 landmark sections, 2 in viewport and 4 pushed off-screen by a spacer

Overview (`libcef_dll/bootstrap/installer/testdata/README.md`)

CEF Installer Test Data

This directory contains test fixtures for the CEF installer integration tests.

Test Certificate

The test_signing.pfx file contains a self-signed code signing certificate
used for testing catalog signature verification.

Important: This certificate is NOT trusted by Windows. Tests use
SetSignatureTestingMode(true) to accept signatures from untrusted roots
when the thumbprint matches.

Files

  • test_signing.pfx - PKCS#12 file with certificate and private key
    • Password: test
  • test_signing.cer - Public certificate only (DER format)
  • test_thumbprint.txt - SHA1 thumbprint of the certificate

Regenerating the Certificate

If the certificate needs to be regenerated (e.g., for a longer validity period):

powershell
.\generate_test_cert.ps1

Note: The certificate has a 100-year validity period to avoid test failures
due to expiration.

XZ Multi-Block Test Fixtures

These .tar.xz archives are used by InstallerXzIndexTest,
InstallerParallelXzTest, and ExtractTarXzIntegrationTest to test XZ index
parsing, parallel block decompression, and single-threaded fallback paths.

Files

  • small_single.tar.xz - Tiny single-block archive (1 block, ~276 bytes)

    • Contains one file: hello.txt ("Hello CEF\n")
    • Created with default xz settings (no --block-size)
  • single_block.tar.xz - Medium single-block archive (1 block, ~920 bytes)

    • Contains 20 generated files (4 KB each) across 3 directories
    • Created with default xz settings (no --block-size)
    • Used for single-block fallback tests
  • multi_block_4.tar.xz - Multi-block archive (30 blocks, ~11 KB)

    • Contains 20 generated files (4 KB each) across 3 directories
    • Created with --block-size=4096 (4 KB blocks)
    • Used for parallel extraction and XZ index parsing tests
  • multi_block_large.tar.xz - Larger multi-block archive (37 blocks, ~15 KB)

    • Contains 50 generated files (4 KB each) across 3 directories
    • Created with --block-size=8192 (8 KB blocks)
    • Used for larger parallel extraction and thread-count limit tests

All archives use xz compression level 6 (default) with LZMA2 filter.

Regenerating the Fixtures

Requires xz.exe (XZ Utils for Windows). Set the XZ_EXE environment
variable to the path of xz.exe.

bash
python generate_fixtures.py [--xz-exe PATH]

Important: The archives contain deterministic content generated by
generate_fixtures.py. Regenerating will produce byte-identical archives
only if the same version of xz is used. Block counts may vary slightly
with different xz versions but should always meet the minimum requirements
(1 block for single-block fixtures, 4+ blocks for multi-block fixtures).


Config.Yml (`docs/_config.yml`)

title: CEF Documentation
theme: jekyll-theme-minimal


Api Versioning (`docs/api_versioning.md`)

This page explains CEF API versioning.

Contents


Overview

The CEF library/framework exports an application binary interface (ABI) that is consumed by client applications. Historically this ABI has been unversioned meaning that any change in CEF major/minor version would require a recompilation of the client application. Given the long release cycles for many client applications this could mean users being stuck at an old/insecure Chromium version for an extended period of time.

To address this and related issues we have introduced the concept of CEF API versioning. API versioning allows applications built for one Stable API version to run with all past and future CEF binaries that also support that version (see "Supported Versions" below).

CEF API versioning has three levels:

  • Legacy API. API that existed prior to 2025 is grandfathered in as Stable API without an explicitly assigned version number.
  • Versioned API. Stable API introduced in 2025 and later that is explicitly assigned a version number.
  • Experimental API. API introduced in 2025 and later that is explicitly marked as "experimental" (see "Macros" below).

Stable API is guaranteed to provide back/forward ABI compatibility and a fixed C/C++ API within a specific (supported) range of binary builds. Developers that wish to update CEF/Chromium separately from the client application should select this option by defining CEF_API_VERSION=XXXYY in their project configuration (see "Number Format" and "Test Coverage" below).

Experimental API provides access to additional/experimental functionality on top of the most recent Stable API version but comes with some limitations:

  • It's tied to a specific CEF major/minor version build.
  • It doesn't support back/forward ABI compatibility.
  • It's excluded (compiled out) when a Stable API version is selected.

By default, client applications are built with Experimental API enabled (no explicit CEF_API_VERSION define). The CEF_API_VERSION definition, in combination with certain macros (see "Macros" below), is used to customize the available C/C++ API as seen by the C/C++ compiler. Client applications and libcef_dll_wrapper must be recompiled if the CEF_API_VERSION definition changes.

API versioning does not does not guarantee behavioral compatibility, particularly when moving between major Chromium versions. App developers are advised to perform their own testing before rolling out any particular combination of binaries.

Usage in Pull Requests

Project contributors use pull requests to propose CEF code changes. Any API changes in pull requests should follow the Implementation guidelines laid out below and use the CEF_EXPERIMENTAL placeholder value in C/C++ header files.

API changes may become Stable (be assigned a version) if explicitly approved by the CEF admin. API approved for Stable can use the CEF_NEXT placeholder value instead of CEF_EXPERIMENTAL in pull requests. This value will be converted to a specific/new CEF API version by the CEF admin prior to upstream merge (see example here).

Implementation

The CEF library/framework exports a C API that isolates the user from the CEF runtime and code base. The libcef_dll_wrapper project, which is distributed in source code form as part of the binary release, wraps this exported C API in a C++ API that is then linked into the client application. The code for this C/C++ API translation layer is automatically generated by the translator tool.

At runtime the client application reports the configured CEF_API_VERSION to the CEF library/framework via the first call to cef_api_hash (performed internally by libcef_dll_wrapper). This is a global value that modifies the library/framework runtime behavior and cannot be changed after initial configuration. All API surface (enums, structs, etc.) must match the configured API version or otherwise the application will crash with a fatal runtime error.

Each API version has an associated set of API hashes. The version_manager.py tool verifies API hashes at build time by reading and parsing all header files that contribute to the exported C API at that particular version. An API hash mismatch may occur during the cef_create_projects build step if the exported C API for an existing version has changed in an unexpected/unsupported way. This is considered a fatal build error and should be resolved before proceeding (see example here).

Number Format

The CEF API version number format is XXXZZ where:

  • XXX is the CEF (and Chromium) major version (e.g. 133)
  • ZZ is an incremental number (e.g. 01, 02, etc) that gets reset to 00 when the CEF major version changes. This becomes the CEF minor version.

This format is incremental to work with logical operators and based on the CEF version at which it was first introduced. New version numbers are generated by the version_manager.py tool (see "Usage in Pull Requests" above).

By default, client applications are built with Experimental API enabled (no explicit CEF_API_VERSION define). Client applications can select a Stable API version by defining CEF_API_VERSION=XXXYY in their project configuration (see "Test Coverage" below).

Macros

Stable and Experimental API is explicitly tagged in C/C++ header files using CEF versioning macros provided by cef_api_hash.h. API without explicit tags is considered Legacy API (see "Overview" above). In C++ header files, versioning information is additionally included in CEF metadata (the /*--cef(...)--*/ comment). Stable API uses a specific version number and Experimental API uses the CEF_EXPERIMENTAL placeholder (see "Number Format" above).

CEF macros (for C/C++ headers) are as follows:

  • #if CEF_API_ADDED(XXXYY) surrounds code added in version XXXYY.
  • #if CEF_API_REMOVED(XXXYY) surrounds code removed in version XXXYY.
  • #if CEF_API_RANGE(XXXYY, XXXYZ) surrounds code added in version XXXYY and removed in version XXXYZ.

CEF metadata (for classes/methods in C++ headers) is as follows:

  • added=XXXYY marks classes/methods added in version XXXYY.
  • removed=XXXYY marks classes/methods removed in version XXXYY.
  • added=XXXYY,removed=XXXYZ marks classes/methods added in version XXXYY and removed in version XXXYZ.

For example, a C++ header might contain:

cpp
#include "include/cef_api_hash.h"

///
/// Client handler.
///
/*--cef(source=client)--*/
class CefClientHandler : public virtual CefBaseRefCounted {
 public:
  // Other method declarations here...

#if CEF_API_ADDED(13305)
  ///
  /// Callback when something happens. Added in version 13305.
  ///
  /*--cef(added=13305)--*/
  virtual void OnSomething() = 0;
#endif
};

The new OnSomething callback will be available with Experimental API versions and Stable API versions >= 13305. It will be unavailable (compiled out) client-side with Stable API versions < 13305. See cef_api_hash.h for additional client-side macros.

The associated CEF library/framework (libcef) implementation might contain:

cpp
#include "cef/libcef/common/api_version_util.h"

void CefClientImpl::OnSomething() {
  if (CEF_API_IS_ADDED(13305)) {
    handler->OnSomething();
  } else {
    // Default handling
  }
}

All API versions are supported on the library/framework side so it's necessary to check the runtime-configured value. The CEF_API_IS_ADDED macro shown above returns true if the configured value is >= 13305. See api_version_util.h for additional libcef-side macros.

See "Test Coverage" below for additional usage examples.

ABI Guidelines

ABI versioning requires strict guidelines for how API surface is defined and mutated across versions.

Enums:

  • All enums that might change get a _NUM_VALUES value at the end.
  • New values are added immediately before _NUM_VALUES (except in cases where the value is explicitly set).
  • Old/deprecated values are renamed to _DEPRECATED (or _REMOVED) but not removed.

Structs:

  • All structs get a size_t size member at the beginning.
  • New members are added at the end.
  • Old/deprecated members are renamed to _deprecated (or _removed) but not removed.
  • Existing members never change type (old may be renamed and new added with the same name).

Member methods:

  • Return value and parameters never change for existing methods.
  • New methods are added at the end of the class.
  • Old/deprecated members in the C API struct are given an opaque placeholder pointer type (uintptr_t) to preserve binary compatibility (memory layout) with older versions.
  • The existing method may be deprecated and a new method added if return value or parameters need to change. C++ naming may use polymorphism (e.g. multiple OnEvent with different params) but C API should use numbering (e.g. on_event as the original, on_event[2...N] or on_event_[VERSION] as the replacement).

Static methods & functions:

  • Return value and parameters never change for existing methods/functions.
  • Ordering doesn't matter as these symbols are exported directly from the library/framework.
  • The existing method/function may be deprecated and a new method/function added if return value or parameters need to change. C++ naming may use polymorphism (e.g. multiple CefDoWork with different params) but C API should use numbering (e.g. cef_do_work as the original, cef_do_work[2...N] or cef_do_work_[VERSION] as the replacement).

See cef_types.h and "Test Coverage" below for examples.

Test Coverage

Test coverage has been added for API version compatibility. CEF sample apps (cefclient, cefsimple, ceftests) can be built with different Stable API versions as follows (see "Number Format" above):

  • Add cef_api_version=XXXYY to GN_DEFINES when building CEF/Chromium from source code.
  • Add -D api_version=XXXYY to the cmake command-line when building with a CEF binary distribution.

API version-related unit tests can be run with ceftests --gtest_filter=ApiVersionTest.*.

Examples of API versioning are available at include/test/cef_api_version_test.h (C++ header), libcef/common/test/api_version_test_impl.cc (libcef implementation) and tests/ceftests/api_version_unittest.cc (client implementation). An explanation of the associated C API headers and C++ wrapper code generated by the translator tool can be found here.

Supported Versions

API versioning support was added in M133 (January 2025). Newer CEF versions may occasionally drop support for older API versions (see example here). This may occur on a schedule (e.g. annually), or as a result of incompatible Chromium changes breaking existing APIs in irresolvable ways. When this occurs it will be clearly communicated and likely supported by shared installers.


Architecture (`docs/architecture.md`)

This page provides an overview of the CEF architecture.

Contents


Overview

The Chromium Embedded Framework (CEF) is an open source project founded by Marshall Greenblatt in 2008 to develop a Web browser control based on the Google Chromium project. CEF currently supports a range of programming languages and operating systems and can be easily integrated into both new and existing applications. It was designed from the ground up with both performance and ease of use in mind. The base framework includes C and C++ programming interfaces exposed via native libraries that insulate the host application from Chromium and Blink implementation details. It provides close integration between the browser control and the host application, which can optionally control resource loading, navigation, context menus, printing, JavaScript bindings and more, while taking advantage of the same performance and HTML5 technologies available in the Google Chrome Web browser.

See the project README.md for additional introductory material.

Dependencies

The CEF project depends on a number of other projects maintained by third parties. The major projects that CEF depends on are:

  • Chromium - Provides general functionality like network stack, threading, message loop, logging and process control needed to create a fully functional Web browser. Implements "platform" code that allows Blink to communicate with V8 and Skia. Many Chromium design documents can be found at http://dev.chromium.org/developers.
  • Blink (formerly WebKit) - The rendering implementation used by Chromium. Provides DOM parsing, layout, event handling, rendering and HTML5 JavaScript APIs. Some HTML5 implementations are split between the Blink and Chromium code bases.
  • V8 - JavaScript engine.
  • Skia - 2D graphics library used for rendering non-accelerated content. More information on how Chromium integrates Skia is available here.
  • Angle - 3D graphics translation layer for Windows that converts GLES calls to DirectX. More information about accelerated compositing is available here.

Versions

There have been three versions of CEF to date.

  • CEF1 - Single process implementation using the Chromium WebKit API (now discontinued).
  • CEF2 - Multi process implementation built on the Chromium browser (now discontinued).
  • CEF3 - Multi process implementation using either the Chromium Content API (called the "Alloy runtime") or the complete Chrome UI (called the "Chrome runtime").

Only the CEF3 version is currently developed and supported. See the "History" and "Modern Architecture" sections below for background and the Branches And Building page for current development information.

History

In 2008, when the CEF project started, Chromium was a monolithic application built on top of Apple's WebKit rendering engine. Google maintained an in-tree variant of WebKit (called the Chromium WebKit API) that added additional functionality specific to Chromium. The first version of CEF (called CEF1) was implemented on top of this Chromium WebKit API and used the default single-process application model supported by WebKit1 at that time (for more details on CEF1 architecture see the "CEF1" section below).

Use of the Chromium WebKit API came with some known limitations. For example, that API layer did not support the multi-process model that was the basis for most of Google Chrome's stability and security advantages. It also lacked many of the UI features that were becoming necessary to support new web platform features (like device selection dialogs, permissions management, etc). We therefore began a new effort, in 2010, to reimplement CEF (called CEF2) on top of the fully-featured but monolithic Chromium browser UI. The lack of internal API boundaries in the Chromium code at that time made it necessary for us to take an "all or nothing" approach. If we could create a CEF browser window based on the complete Chrome UI we would effectively gain access to all of Chrome's capabilities "for free" (for more details on CEF2 architecture see the "CEF2" section below).

Google began acknowledging the validity of the embedded use case for Chromium in 2010 and started work on new internal API boundaries to make embedding easier. The Chromium Content API, which encapsulated the multi-process application model and provided stub implementations for most web platform features, reached a development stage in late 2011 where it was a viable option for Chromium embedders like CEF. It still didn't provide all of the features that we might like, but it was a "supported" API layer. We therefore abandoned the CEF2 effort in early 2012 and focused our efforts instead on the Content API as the more viable embedding option. The existing CEF1 API, which was strongly dependent on WebKit1 and the single-process model, could not be directly ported to the Content API. A new CEF API (called CEF3), with substantial breaking API changes and multi-process support, was released in early 2013 (for more details on CEF3 architecture see the "Modern Architecture" and "CEF3" sections below).

Also in 2013, Google decided to fork the WebKit project. After renaming it to Blink they removed many of the API boundaries that had previously existed between Chromium and WebKit and effectively deleted the Chromium WebKit API. The multi-process architecture, which was optional in WebKit1, was now a requirement with Chromium/Blink. CEF1, which depended on the Chromium WebKit API, no longer had a viable future and CEF3 became the only path forward.

Fast forward another 7 years, to 2020, and the Chromium architecture has again changed substantially. A lot of effort has been spent to define clear architectural layers (sometimes called components) in the Chromium/Blink source code. New platforms and new embedders have entered the Chromium ecosystem. And yet, many features desired by CEF users remain stubbornly out of reach in the Chrome UI layer. With this new, more modern, version of Chromium, we decided it was time to revisit our goals from CEF2 and create a new CEF version based on the Chrome UI layer (now called the "Chrome runtime" in CEF3). Continue to the "Modern Architecture" section for details.

Modern Architecture

The modern Chromium code base is composed of architectural layers, with the primary application layers being "content" (for the Content API) and "chrome" (for the Chrome UI layer). The "content" layer includes WebContents as the primary browser object and provides implementations for most of the generic web platform and application/process management functionality. The "chrome" layer includes Browser as the primary browser object and provides implementations for many advanced features (including datalist/autofill, extensions, gamepad, webhid, webmidi, etc.), along with standard Chrome UI toolbars and dialogs for device selection, user permissions, settings, etc.

For historical reasons (outlined above), CEF browser windows have been based on the "content" layer WebContents object since the introduction of CEF3 in 2013. Adding support for additional "chrome" layer features in that architecture was a serious effort because it involved careful understanding and cherry-picking of the related functionality from "chrome" code into the CEF code base. Over time we have added dependencies on the "chrome" layer targets to support some features in CEF (such and extensions, PDF viewer and print preview) and to avoid code duplication. As a result, CEF3 binaries in modern times already contained most/all of the code that would be required to launch a stand-alone "chrome" Browser window, but simply lacked the mechanism to do so.

In 2020 we launched a new effort to implement the CEF3 API on top of a fully-featured "chrome" Browser window. To be usable in existing CEF3 client applications the "chrome" window needed to support a large subset of CEF3's existing callbacks/intercepts (e.g. loading progress, network requests, etc.), and be inserted into the existing Views framework structure. At the same time, it was necessary to substantially refactor the internal CEF (libcef) code to extract functionality that was common to both the old WebContents-based approach and the new Browser-based approach. As part of this refactoring we renamed the existing "content" layer implementation to the "Alloy runtime" (where Alloy = Chrome + other stuff) and named the new "chrome" layer implementation as the "Chrome runtime". Proceed to the "CEF3" section below for additional feature and implementation details.

Common API Usage

All versions of CEF expose a simple, easy-to-use API designed to insulate users from the underlying Chromium and Blink code complexity. Common usage is as follows:

  1. Initialize CEF by calling CefInitialize().

  2. Perform work on the UI message loop by calling CefRunMessageLoop() or CefDoMessageLoopWork().

  3. Create a browser window by calling CreateBrowser() or CreateBrowserSync() and passing in a CefClient instance.

  4. Shut down CEF by calling CefShutdown() before the application exits.

C Wrapper API

The libcef shared library exports a C API that isolates the user from the CEF runtime and code base. The libcef_dll_wrapper project, which is distributed in source code form as part of the binary release, wraps this exported C API in a C++ API that is then linked into the client application. The code for this C/C++ API translation layer is automatically generated by the translator tool. Direct usage of the C API is described on the Using The CAPI page.

CEF1 (Discontinued)

CEF1 was the initial version of CEF first released in 2008. It was discontinued in 2013 when Google forked the WebKit project and subsequently deleted the Chromium WebKit API. See the "History" section above for additional details.

The single process architecture used by CEF1 integrates Chromium and WebKit directly into the client application. Advantages to the single process architecture included reduced memory usage and closer integration with the client application. For example, having the renderer running in the same process allowed JavaScript bindings to synchronously communicate between client code and renderer code. Disadvantages included reduced performance with certain types of accelerated content, crashes due to plugins like Flash running in the same process, and lack of support for application security and stability features that required a multi-process architecture.

API Usage

Below is an overview of the main CEF1 interfaces and their uses. Since CEF1 is discontinued we no longer provide detailed usage information.

  • CefApp - This interface is passed to CefInitialize() and allows the application to customize global functionality like resource loading and proxy handling.
  • CefClient - This interface is passed to CefCreateBrowser() or CefCreateBrowserSync() and acts as the connection between an individual CEF browser instance and the client application. Additional interfaces for request handling, display handling, etc. are exposed via this interface.
  • CefBrowser - Exposes capabilities provided by the browser. This includes back/forward navigation, source retrieval, request loading, etc. A single CefBrowser may have one or more child CefFrame objects.

Threading Considerations

CEF1 includes UI, IO and FILE threads. The UI thread creates browser windows and is used for all interaction with WebKit and V8. The IO thread is used for handling schema and network requests. The FILE thread is used for the application cache and other miscellaneous activities.

When using CEF1 you should keep the following threading considerations in mind:

  • Do not perform blocking operations on the UI thread. This can lead to serious performance issues.
  • The UI thread will be the same as the main application thread if CefInitialize() is called with a CefSettings.multi_threaded_message_loop value of false.
  • All WebKit and V8 interation must take place on the UI thread. Consequently, some CEF API functions can only be called on the UI thread. Functions that have this limitation will be documented accordingly in the associated CEF header file.
  • The CefPostTask method can be used to post tasks asnychronously between the various threads.

Implementation Details

CEF1 has the following major implementation classes:

  • CefContext - Represents the global CEF context. A single CefContext object is created by CefInitialize() and destroyed by CefShutdown().
  • CefProcess - Used by CefContext to create and manage the CEF threads.
  • BrowserWebKitInit - Manages the global WebKit environment as exposed by the Chromium WebKit API.
  • WebViewHost - Provides the native window "wrapper" implementation for a WebView. This class extends WebWidgetHost which provides functionality shared in common with popup widgets (like select menus) on some platforms.
  • CefBrowserImpl - Implements the CefBrowser interface, creates the WebViewHost and provides the glue code for a single browser instance.
  • BrowserWebViewDelegate - Implements the WebKit interfaces that provide communication between CefBrowserImpl and the underlying WebView.

CEF2 (Discontinued)

CEF2 was an effort to reimplement the CEF1 API on top of the Chrome browser UI as it existed in 2010. CEF2 was discontinued when Chromium announced support for the Content API. Information about CEF2 can be found in the "History" section above and here on the CEF Forum.

CEF3

CEF3 has been the recommended and supported version of CEF since January 2013. It uses the same multi process architecture as the Chromium Web browser via either the Chromium Content API (called the "Alloy runtime") or the complete Chrome UI (called the "Chrome runtime"). This architecture provides a number of advantages over the single process architecture used by CEF1:

  • Support for multi-process run mode (with related stability and security advantages).
  • More code sharing with the Chromium browser.
  • Improved performance and less breakage due to use of the "supported" code path.
  • Faster access to new features.

In most cases CEF3 will have the same performance and stability characteristics as the Chromium Web browser. Both the Alloy runtime and the Chrome runtime support modern web platform features and provide the safety/security expected from an up-to-date Chromium-based browser. The Chrome runtime, which was introduced in 2020, adds implementations for many advanced features (including datalist/autofill, extensions, gamepad, webhid, webmidi, etc.), along with standard Chrome UI toolbars and dialogs for device selection, user permissions, settings, etc. At the time of writing, the Chrome runtime requires use of the Views framework in CEF client applications and does not support off-screen rendering. A current list of open issues specific to the Chrome runtime can be found here.

Starting with the M125 release (mid-2024) the Alloy runtime has been split into separate style and bootstrap components. Both Chrome style and Alloy style browsers/windows can now be created while using the Chrome bootstrap. The Alloy bootstrap has been removed starting with the M128 release.

API Usage

See the General Usage page for detailed API usage instructions.

Process Considerations

See the Processes section of the General Usage page for details.

Threading Considerations

See the Threads section of the General Usage page for details.

Implementation Details

For an implementation overview see the "Modern Architecture" section above.

Shared CEF library/runtime classes use the Cef prefix. Classes that implement the Alloy or Chrome runtime use the Alloy or Chrome prefixes respectively. Classes that extend an existing Chrome-prefixed class add the Cef or Alloy suffix, thereby following the existing naming pattern of Chrome-derived classes.

Alloy Runtime Classes

Alloy runtime bootstrap was removed in M128 (mid-2024) and Alloy runtime style is now supported using the Chrome runtime bootstrap. Alloy runtime style is based on the "content" layer and has the following major implementation classes:

  • AlloyBrowserHostImpl - Implements the CefBrowser and CefBrowserHost interfaces in the browser process. Provides the glue code and implements interfaces for communicating with the RenderViewHost.

Chrome Runtime Classes

Chrome runtime bootstrap and style is based on the "chrome" layer and has the following major implementation classes:

An overview of the Chrome Browser object model is also available here.


Automated Build Setup (`docs/automated_build_setup.md`)

This page describes the setup of an automated build system for CEF.

Contents


Overview

CEF provides tools for automatically downloading, building and packaging Chromium and CEF source code. These tools are the recommended way of building CEF locally and can also be integrated with an automated build system. Examples of automated build systems for CEF include https://cef-builds.spotifycdn.com/index.html hosted by Spotify. This document outlines how to build a system similar to Spotify's. For general CEF build instructions see the Branches And Building page.

Services

An automated build system tracks changes to a CEF branch, automatically builds a new version of CEF when changes are detected, and uploads the resulting artifacts (CEF binary files) to an Internet or Intranet Website.

The following high-level services are necessary for an automated build system:

  1. Build execution service. Used to perform the CEF automated builds using agents running on different OS platforms. For example, a build system like TeamCity or Jenkins.
  2. Artifact storage service. Used to store artifacts referenced and generated by the build agents. For example, a shared file system or a storage system like Artifactory.
  3. Artifact publishing service. Used to publish artifacts for download on the Intranet or Internet. Generally provides some form of distributed caching. For example, a cloud provider like Amazon S3 + Cloudfront or Google Cloud Storage.

Setup of these specific services is outside the scope of this document. However, we will refer to the capabilities of these services in generic terms.

Execution Steps

The reference build system works by executing the following steps in order:

1\. Change Trigger

Runs automatically at some interval (for example, once daily) to check if a new change has been committed to the CEF repository. The GitHub REST API exposes an endpoint that can be queried to identify the most recent commit in a branch. For example, this will return the most recent commit for 2785 branch:

https://api.github.com/repos/chromiumembedded/cef/branches/2785

The resulting "commit.sha" value can then be compared to the last successful CEF build version to identify if a new build is required (e.g. a sha value of "1e15b7626244fa6c6455b7a24552664e4cc14fd3" from branch 2785 will match a build artifact file named "cef_binary_3.2785.*.g1e15b76_*.tar.bz2").

If artifacts are stored on a shared file system then languages like Python can access the file list directly. If artifacts are stored in Artifactory then see the Artifactory REST API documentation for instructions on how to retrieve a directory listing.

2\. Platform Builder

Initiated by the change trigger when a new commit is detected. A separate platform builder is configured for each supported build platform (linux32, linux64, macosx64, windows32, windows64). It executes a build on that platform and then uploads the resulting build artifact files to the storage service (shared file system, Artifactory, etc.).

See the "Platform Build Configurations" section below for recommended platform build configurations.

3\. Artifact Publisher

Initiated by the platform builder upon a successful build. It retrieves the list of build artifact files from the storage service and uses the cefbuilds tools to compare against the index.json file generated by the previous run. When changes are detected it uploads the new changes to the publishing service (Amazon S3, GCS, etc.). The new index.json file is then stored for future reference.

In addition to generating index.json files the cefbuilds tools can also generate the index.html file used by the Spotify builder (provide your own custom branding to cef_html_builder.py via the "branding" parameter). See the cef_json_builder_example.py and cef_html_builder.py files from the cefbuilds directory for sample usage.

Platform Build Configurations

The following configurations are executed by the platform builder step. These are the recommended configurations for externally published artifacts but are not recommended for local builds. See the the Branches And Building page for general build instructions.

These instructions are intended for use with the currently supported release branches. This page will be updated as build requirements change.

Lower-case values between %% (%download_dir%, %cef_branch%, etc.) must be provided by you.

Linux Configuration

What's Required

  • Linux Build Requirements as listed on the Branches And Building page.
  • At least 16GB of RAM and 200GB of free disk space.

We recommend building on Linux using a Docker container. All architectures can be built using the same Linux container image with a configuration as shown here that utilizes Chromium's install-build-deps.py script.

64-bit Build Commands

To build 64-bit CEF with a 64-bit Linux Docker image:

sh
export GN_DEFINES="is_official_build=true use_sysroot=true symbol_level=1 is_cfi=false"
export CEF_ARCHIVE_FORMAT=tar.bz2
python3 automate-git.py --download-dir=%download_dir% --branch=%cef_branch% --minimal-distrib --client-distrib --force-clean --no-chromium-history --build-target=cefsimple --x64-build --with-pgo-profiles

ARM Build Commands

To build ARM CEF with a 64-bit Linux Docker image:

sh
export CEF_INSTALL_SYSROOT=arm
export GN_DEFINES="is_official_build=true use_sysroot=true symbol_level=1 is_cfi=false use_thin_lto=false chrome_pgo_phase=0 use_vaapi=false"
export CEF_ARCHIVE_FORMAT=tar.bz2
python3 automate-git.py --download-dir=%download_dir% --branch=%cef_branch% --minimal-distrib --client-distrib --force-clean --no-chromium-history --build-target=cefsimple --arm-build

ARM64 Build Commands

To build ARM64 CEF with a 64-bit Linux Docker image:

sh
export CEF_INSTALL_SYSROOT=arm64
export GN_DEFINES="is_official_build=true use_sysroot=true symbol_level=1 is_cfi=false use_thin_lto=false chrome_pgo_phase=0"
export CEF_ARCHIVE_FORMAT=tar.bz2
python3 automate-git.py --download-dir=%download_dir% --branch=%cef_branch% --minimal-distrib --client-distrib --force-clean --no-chromium-history --build-target=cefsimple --arm64-build

MacOS Configuration

What's Required

  • MacOS Build Requirements as listed on the Branches And Building page.
  • At least 16GB of RAM and 200GB of free disk space.

If Xcode is not installed to the default location then:

1. Set the correct location using the xcodeselect tool.

2. Set the SDKROOT environment variable:

sh
export SDKROOT=$(xcrun -show-sdk-path -sdk macosx)

64-bit Build Commands

To build Intel 64-bit CEF on a Intel 64-bit MacOS host system:

sh
export GN_DEFINES=is_official_build=true
export CEF_ARCHIVE_FORMAT=tar.bz2
python3 automate-git.py --download-dir=%download_dir% --branch=%cef_branch% --minimal-distrib --client-distrib --force-clean --no-chromium-history --x64-build --with-pgo-profiles

To build Intel 64-bit CEF on a ARM64 MacOS host system:

sh
export GN_DEFINES=is_official_build=true
export CEF_ARCHIVE_FORMAT=tar.bz2
export CEF_ENABLE_AMD64=1
python3 automate-git.py --download-dir=%download_dir% --branch=%cef_branch% --minimal-distrib --client-distrib --force-clean --no-chromium-history --x64-build --with-pgo-profiles

ARM64 Build Commands

ARM64 builds require Xcode 12.2 and CEF branch 4280 or newer.

To build ARM64 CEF on an ARM64 MacOS host system:

sh
export GN_DEFINES=is_official_build=true
export CEF_ARCHIVE_FORMAT=tar.bz2
python3 automate-git.py --download-dir=%download_dir% --branch=%cef_branch% --minimal-distrib --client-distrib --force-clean --no-chromium-history --arm64-build --with-pgo-profiles

To build ARM64 CEF on a Intel 64-bit MacOS host system:

sh
export GN_DEFINES=is_official_build=true
export CEF_ARCHIVE_FORMAT=tar.bz2
export CEF_ENABLE_ARM64=1
python3 automate-git.py --download-dir=%download_dir% --branch=%cef_branch% --minimal-distrib --client-distrib --force-clean --no-chromium-history --arm64-build --with-pgo-profiles

Windows Configuration

What's Required

  • Visual Studio and Windows SDK versions as listed on the Branches And Building page.
  • At least 16GB of RAM and 200GB of free disk space.

Install the required Visual Studio sub-components by passing the following arguments to the Visual Studio installer:

sh
$ PATH_TO_INSTALLER.EXE ^
--add Microsoft.VisualStudio.Workload.NativeDesktop ^
--add Microsoft.VisualStudio.Component.VC.ATLMFC ^
--includeRecommended

If building for ARM64 install the following additional sub-components:

sh
$ PATH_TO_INSTALLER.EXE ^
--add Microsoft.VisualStudio.Component.VC.Tools.ARM64 ^
--add Microsoft.VisualStudio.Component.VC.MFC.ARM64 ^
--includeRecommended

If Visual Studio or Windows SDK is not installed to the default location then set environment variables like the following before executing automate-git.py:

sh
set WIN_CUSTOM_TOOLCHAIN=1
set CEF_VCVARS=none
set GYP_MSVS_OVERRIDE_PATH=%vs_root%
set VS_CRT_ROOT=%vs_crt_root%
set SDK_ROOT=%sdk_root%
set PATH=%sdk_root%\bin\%sdk_version%\x64;%vs_root%\VC\Tools\MSVC\%vc_tools_version%\bin\HostX64\x64;%vs_root%\VC\Redist\MSVC\%vc_redist_version%\x64\%vc_redist_crt%;%vs_root%\SystemCRT;%PATH%
set LIB=%sdk_root%\Lib\%sdk_version\um\%arch%;%sdk_root%\Lib\%sdk_version\ucrt\%arch%;%vs_root%\VC\Tools\MSVC\%vc_tools_version%\lib\%arch%;%vs_root%\VC\Tools\MSVC\%vc_tools_version%\atlmfc\lib\%arch%;%LIB%
set INCLUDE=%sdk_root%\Include\%sdk_version%\um;%sdk_root%\Include\%sdk_version%\ucrt;%sdk_root%\Include\%sdk_version%\shared;%vs_root%\VC\Tools\MSVC\%vc_tools_version%\include;%vs_root%\VC\Tools\MSVC\%vc_tools_version%\atlmfc\include;%INCLUDE%

The correct PATH, LIB and INCLUDE values for your installation can be determined by running the vcvarsall.bat script and passing in the desired architecture and SDK version.

See comments in gclient_hook.py for Windows custom toolchain requirements. VS + SDK can be packaged for distribution to build agents using a script like Chromium's package_from_installed.py.

32-bit Build Commands

To build 32-bit CEF on a 64-bit Windows host system:

sh
set GN_DEFINES=is_official_build=true
set GYP_MSVS_VERSION=2022
set CEF_ARCHIVE_FORMAT=tar.bz2
python3 automate-git.py --download-dir=%download_dir% --branch=%cef_branch% --minimal-distrib --client-distrib --force-clean --no-chromium-history --with-pgo-profiles

64-bit Build Commands

To build 64-bit CEF on a 64-bit Windows host system:

sh
set GN_DEFINES=is_official_build=true
set GYP_MSVS_VERSION=2022
set CEF_ARCHIVE_FORMAT=tar.bz2
python3 automate-git.py --download-dir=%download_dir% --branch=%cef_branch% --minimal-distrib --client-distrib --force-clean --no-chromium-history --x64-build --with-pgo-profiles

ARM64 Build Commands

To build ARM64 CEF on a 64-bit Windows host system:

sh
set CEF_ENABLE_ARM64=1
set GN_DEFINES=is_official_build=true use_thin_lto=false chrome_pgo_phase=0
set GYP_MSVS_VERSION=2022
set CEF_ARCHIVE_FORMAT=tar.bz2
python3 automate-git.py --download-dir=%download_dir% --branch=%cef_branch% --minimal-distrib --client-distrib --force-clean --no-chromium-history --build-target=cefsimple --arm64-build

Branches And Building (`docs/branches_and_building.md`)

This page provides information about CEF branches and instructions for downloading, building and packaging source code.

Contents


Background

The CEF project is an extension of the Chromium project hosted at chromium.org. CEF maintains development and release branches that track Chromium branches. CEF source code can be built manually or with automated tools.

Development

Ongoing development of CEF occurs in the CEF master branch. This location tracks the current Chromium master branch and is not recommended for production use.

Current CEF master branch build requirements are as follows. See the Master Build Quick Start page for a development build quick-start guide.

Windows Build Requirements macOS Build Requirements Linux Build Requirements
Win 10+ deployment, Win 10+ build system w/ VS2022 17.13.4, Win 10.0.28000.2270 SDK, Ninja macOS 13.0+ deployment, 26.6+ build system w/ 27.0 base SDK (Xcode 27.0), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja

The following URLs should be used for downloading development versions of CEF.

CEF1 is no longer actively developed or supported. See the CEF1 Retirement Plan for details.

Release Branches

CEF branches are created to track each Chromium release milesone (MXX) branch. Users developing applications for production environments are encouraged to use release branches for the following reasons:

  • Binary CEF builds are tied to specific Chromium releases.
  • Release versions of CEF/Chromium are better tested and more appropriate for release applications.
  • Within a release branch the CEF API is "frozen" and generally only security/bug fixes are applied.
  • CEF release branches can include patches to Chromium/Blink source if necessary.
  • CEF master development won't interfere with consumer release schedules.

CEF release branches and associated platform build requirements are described below.

Version Number Format

The CEF version number format was changed in 2019 to include more relevant information and to provide compatibility with the Semantic Versioning 2.0 standard.

Newer CEF release version numbers have the format "X.Y.Z+gHHHHHHH+chromium-A.B.C.D" where:

  • "X" is the CEF (and Chromium) major version (e.g. 73).
  • "Y" is the CEF minor version. It's an incremental number that starts at 0 when a release branch is created and changes only when the CEF C/C++ API changes (see Api Versioning for details).
  • "Z" is the CEF bugfix version. It's an incremental number that starts at 0 when a release branch is created and changes on each commit, with reset to 0 when "Y" changes.
  • "gHHHHHHH" is the 7-character abbreviation for the Git commit hash. This facilitates lookup of the relevant commit history in Git.
  • "A.B.C.D" is the Chromium version (e.g. 73.0.3683.75).

Older CEF release version numbers have the format X.YYYY.A.gHHHHHHH where:

  • "X" is the CEF major version (e.g. 3).
  • "YYYY" is the Chromium branch.
  • "A" is an incremental number representing the number of commits in the current branch. This is roughly equivalent to the SVN revision number but on a per-branch basis and assists people in quickly determining the order of builds in the same branch (for bug reports, etc).
  • "gHHHHHHH" is the 7-character abbreviation for the Git commit hash. This facilitates lookup of the relevant commit history in Git.

Detailed Chromium and CEF version information is available in the include/cef_version.h header file which is created during the build process or by loading the "about:version" URL in a CEF-derived application.

Current Release Branches (Supported)

CEF is currently tracking even-numbered Chromium milestone branches (new CEF branch every ~4 weeks, adjusted for holidays). Support for new branches begins when they enter the Chromium beta channel. Support for older branches ends when they exit the Chromium stable channel (after 2 weeks as stable), unless that branch also qualifies for extended and/or LTC/LTS channels.

  • Stable channel: Every other Chromium milestone branch starting with M152. These branches receive broad security fixes for ~2 weeks after entering the stable channel.
  • Extended channel: Every fourth Chromium milestone branch starting with M152. These branches receive broad security fixes for ~8 weeks total (including time spent in stable).
  • LTC/LTS channel: Every 12th Chromium milestone branch starting with M160 (every sixth from M138-M150). These branches receive platform-agnostic security fixes for ~9 months total (including time spent in stable and/or extended).

The Spotify automated builder provides CEF builds for supported branches. Updating CEF branches is currently a manual process so there will likely be a delay between Chromium release announcements and the availability of associated CEF builds. See the Chromium release calendar for estimated Chromium release dates and versions.

Channel Release Branch Version Branch Date Last Refresh Windows Build Requirements macOS Build Requirements Linux Build Requirements
Beta 8078 156 Oct 2026 Jan 4, 2027 Win 10+ deployment, Win 10+ build system w/ VS2022 17.13.4, Win 10.0.28000.2270 SDK, Ninja macOS 13.0+ deployment, 26.6+ build system w/ 27.0 base SDK (Xcode 27.0), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja
Extended 7977 152 Aug 2026 Oct 20, 2026 Win 10+ deployment, Win 10+ build system w/ VS2022 17.13.4, Win 10.0.26100.7705 SDK, Ninja macOS 13.0+ deployment, 15.6+ build system w/ 26.0 base SDK (Xcode 26.0), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja
LTC 7871 150 Jun 2026 Apr 13, 2027 Win 10+ deployment, Win 10+ build system w/ VS2022 17.13.4, Win 10.0.26100.7705 SDK, Ninja macOS 12.0+ deployment, 15.6+ build system w/ 26.0 base SDK (Xcode 26.0), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja

Legacy Release Branches (Unsupported)

Legacy CEF builds are available from the Spotify automated builder back to 2704 branch. Building legacy branches is not supported. If you choose to build a legacy branch you will need to solve any build errors on your own.

  • Newer legacy branches (within the past year) can often be built using the same tooling as current branches. You will need to (a) manually download depot_tools, (b) sync depot_tools to a revision that existed at the time of the branch, and (c) set the DEPOT_TOOLS_UPDATE=0 environment variable to keep it from updating automatically.
  • Older legacy branches can potentially be built by downloading a CEF source archive at the desired branch from here and a Chromium source archive at the associated/required version from here, and then combining them to create the required directory structure.
  • See the Build Notes section at the end of this page for historical build details.
Release Branch Version Branch Date Windows Build Requirements macOS Build Requirements Linux Build Requirements
8037 154 Sep 2026 Win 10+ deployment, Win 10+ build system w/ VS2022 17.13.4, Win 10.0.28000.2270 SDK, Ninja macOS 13.0+ deployment, 15.6+ build system w/ 26.0 base SDK (Xcode 26.0), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja
7922 151 Jul 2026 Win 10+ deployment, Win 10+ build system w/ VS2022 17.13.4, Win 10.0.26100.7705 SDK, Ninja macOS 12.0+ deployment, 15.6+ build system w/ 26.0 base SDK (Xcode 26.0), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja
7827 149 May 2026 Win 10+ deployment, Win 10+ build system w/ VS2022 17.13.4, Win 10.0.26100.7705 SDK, Ninja macOS 12.0+ deployment, 15.6+ build system w/ 26.0 base SDK (Xcode 26.0), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja
7778 148 Apr 2026 Win 10+ deployment, Win 10+ build system w/ VS2022 17.13.4, Win 10.0.26100.4654 SDK, Ninja macOS 12.0+ deployment, 15.6+ build system w/ 26.0 base SDK (Xcode 26.0), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja
7727 147 Mar 2026 Win 10+ deployment, Win 10+ build system w/ VS2022 17.13.4, Win 10.0.26100.4654 SDK, Ninja macOS 12.0+ deployment, 15.6+ build system w/ 26.0 base SDK (Xcode 26.0), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja
7680 146 Feb 2026 Win 10+ deployment, Win 10+ build system w/ VS2022 17.13.4, Win 10.0.26100.4654 SDK, Ninja macOS 12.0+ deployment, 15.6+ build system w/ 26.0 base SDK (Xcode 26.0), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja
7632 145 Jan 2026 Win 10+ deployment, Win 10+ build system w/ VS2022 17.13.4, Win 10.0.26100.4654 SDK, Ninja macOS 12.0+ deployment, 15.6+ build system w/ 26.0 base SDK (Xcode 26.0), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja
7559 144 Dec 2025 Win 10+ deployment, Win 10+ build system w/ VS2022 17.13.4, Win 10.0.26100.4654 SDK, Ninja macOS 12.0+ deployment, 15.6+ build system w/ 26.0 base SDK (Xcode 26.0), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja
7499 143 Nov 2025 Win 10+ deployment, Win 10+ build system w/ VS2022 17.13.4, Win 10.0.26100.4654 SDK, Ninja macOS 12.0+ deployment, 15.6+ build system w/ 26.0 base SDK (Xcode 26.0), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja
7444 142 Oct 2025 Win 10+ deployment, Win 10+ build system w/ VS2022 17.13.4, Win 10.0.26100.4654 SDK, Ninja macOS 12.0+ deployment, 15.6+ build system w/ 26.0 base SDK (Xcode 26.0), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja
7390 141 Sep 2025 Win 10+ deployment, Win 10+ build system w/ VS2022 17.13.4, Win 10.0.26100.4654 SDK, Ninja macOS 12.0+ deployment, 15.3+ build system w/ 15.5 base SDK (Xcode 16.4), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja
7339 140 Aug 2025 Win 10+ deployment, Win 10+ build system w/ VS2022 17.13.4, Win 10.0.26100.3323 SDK, Ninja macOS 12.0+ deployment, 15.3+ build system w/ 15.5 base SDK (Xcode 16.4), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja
7258 139 Jul 2025 Win 10+ deployment, Win 10+ build system w/ VS2022 17.13.4, Win 10.0.26100.3323 SDK, Ninja macOS 12.0+ deployment, 15.2+ build system w/ 15.4 base SDK (Xcode 16.3), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja
7204 138 Jun 2025 Win 10+ deployment, Win 10+ build system w/ VS2022 17.13.4, Win 10.0.26100.3323 SDK, Ninja macOS 11.0+ deployment, 15.2+ build system w/ 15.4 base SDK (Xcode 16.3), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja
7151 137 May 2025 Win 10+ deployment, Win 10+ build system w/ VS2022 17.13.4, Win 10.0.26100.3323 SDK, Ninja macOS 11.0+ deployment, 14.5+ build system w/ 15.0 base SDK (Xcode 16.0), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja
7103 136 Apr 2025 Win 10+ deployment, Win 10+ build system w/ VS2022 17.13.4, Win 10.0.26100.3323 SDK, Ninja macOS 11.0+ deployment, 14.5+ build system w/ 15.0 base SDK (Xcode 16.0), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja
7049 135 Mar 2025 Win 10+ deployment, Win 10+ build system w/ VS2022 17.9.2, Win 10.0.22621.2428 SDK, Ninja macOS 11.0+ deployment, 14.5+ build system w/ 15.0 base SDK (Xcode 16.0), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja
6998 134 Feb 2025 Win 10+ deployment, Win 10+ build system w/ VS2022 17.9.2, Win 10.0.22621.2428 SDK, Ninja macOS 11.0+ deployment, 14.5+ build system w/ 15.0 base SDK (Xcode 16.0), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja
6943 133 Jan 2025 Win 10+ deployment, Win 10+ build system w/ VS2022 17.9.2, Win 10.0.22621.2428 SDK, Ninja macOS 11.0+ deployment, 14.5+ build system w/ 15.0 base SDK (Xcode 16.0), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja
6834 132 Nov 2024 Win 10+ deployment, Win 10+ build system w/ VS2022 17.9.2, Win 10.0.22621.2428 SDK, Ninja macOS 11.0+ deployment, 14.5+ build system w/ 15.0 base SDK (Xcode 16.0), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja
6778 131 Oct 2024 Win 10+ deployment, Win 10+ build system w/ VS2022 17.9.2, Win 10.0.22621.2428 SDK, Ninja macOS 11.0+ deployment, 14.5+ build system w/ 15.0 base SDK (Xcode 16.0), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja
6723 130 Sep 2024 Win 10+ deployment, Win 10+ build system w/ VS2022 17.9.2, Win 10.0.22621.2428 SDK, Ninja macOS 11.0+ deployment, 13.5+ build system w/ 14.0 base SDK (Xcode 15.0), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja
6668 129 Aug 2024 Win 10+ deployment, Win 10+ build system w/ VS2022 17.9.2, Win 10.0.22621.2428 SDK, Ninja macOS 11.0+ deployment, 13.5+ build system w/ 14.0 base SDK (Xcode 15.0), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja
6613 128 Jul 2024 Win 10+ deployment, Win 10+ build system w/ VS2022 17.9.2, Win 10.0.22621 SDK, Ninja macOS 10.15+ deployment, 13.5+ build system w/ 14.0 base SDK (Xcode 15.0), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja
6533 127 Jun 2024 Win 10+ deployment, Win 10+ build system w/ VS2022 17.9.2, Win 10.0.22621 SDK, Ninja macOS 10.15+ deployment, 13.5+ build system w/ 14.0 base SDK (Xcode 15.0), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja
6478 126 May 2024 Win 10+ deployment, Win 10+ build system w/ VS2022 17.9.2, Win 10.0.22621 SDK, Ninja macOS 10.15+ deployment, 13.5+ build system w/ 14.0 base SDK (Xcode 15.0), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja
6422 125 Apr 2024 Win 10+ deployment, Win 10+ build system w/ VS2022 17.5+, Win 10.0.22621 SDK, Ninja macOS 10.15+ deployment, 13.5+ build system w/ 14.0 base SDK (Xcode 15.0), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja
6367 124 Mar 2024 Win 10+ deployment, Win 10+ build system w/ VS2022 17.5+, Win 10.0.22621 SDK, Ninja macOS 10.15+ deployment, 13.5+ build system w/ 14.0 base SDK (Xcode 15.0), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja
6312 123 Feb 2024 Win 10+ deployment, Win 10+ build system w/ VS2022 17.5+, Win 10.0.22621 SDK, Ninja macOS 10.15+ deployment, 13.5+ build system w/ 14.0 base SDK (Xcode 15.0), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja
6261 122 Jan 2024 Win 10+ deployment, Win 10+ build system w/ VS2022 17.5+, Win 10.0.22621 SDK, Ninja macOS 10.15+ deployment, 13.5+ build system w/ 14.0 base SDK (Xcode 15.0), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja
6167 121 Dec 2023 Win 10+ deployment, Win 10+ build system w/ VS2022 17.5+, Win 10.0.22621 SDK, Ninja macOS 10.15+ deployment, 13.5+ build system w/ 14.0 base SDK (Xcode 15.0), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja
6099 120 Nov 2023 Win 10+ deployment, Win 10+ build system w/ VS2022 17.5+, Win 10.0.22621 SDK, Ninja macOS 10.15+ deployment, 13.5+ build system w/ 14.0 base SDK (Xcode 15.0), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja
6045 119 Oct 2023 Win 10+ deployment, Win 10+ build system w/ VS2022 17.5+, Win 10.0.22621 SDK, Ninja macOS 10.15+ deployment, 13.5+ build system w/ 14.0 base SDK (Xcode 15.0), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja
5993 118 Sep 2023 Win 10+ deployment, Win 10+ build system w/ VS2022 17.5+, Win 10.0.22621 SDK, Ninja macOS 10.15+ deployment, 13.0+ build system w/ 13.3 base SDK (Xcode 14.3), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja
5938 117 Aug 2023 Win 10+ deployment, Win 10+ build system w/ VS2022 17.5+, Win 10.0.22621 SDK, Ninja macOS 10.15+ deployment, 13.0+ build system w/ 13.3 base SDK (Xcode 14.3), Ninja, 64-bit only Ubuntu 20.04+, Debian 10+, Ninja
5845 116 Jun 2023 Win 10+ deployment, Win 10+ build system w/ VS2022 17.5+, Win 10.0.22621 SDK, Ninja macOS 10.13+ deployment, 13.0+ build system w/ 13.3 base SDK (Xcode 14.3), Ninja, 64-bit only Ubuntu 18.04+, Debian 10+, Ninja
5790 115 May 2023 Win 10+ deployment, Win 10+ build system w/ VS2022 17.5+, Win 10.0.22621 SDK, Ninja macOS 10.13+ deployment, 13.0+ build system w/ 13.3 base SDK (Xcode 14.3), Ninja, 64-bit only Ubuntu 18.04+, Debian 10+, Ninja
5735 114 Apr 2023 Win 10+ deployment, Win 10+ build system w/ VS2022 17.5+, Win 10.0.22621 SDK, Ninja macOS 10.13+ deployment, 12.5+ build system w/ 13.0 base SDK (Xcode 14.0-14.3), Ninja, 64-bit only Ubuntu 18.04+, Debian 10+, Ninja
5672 113 Mar 2023 Win 10+ deployment, Win 10+ build system w/ VS2022 17.5+, Win 10.0.22621 SDK, Ninja macOS 10.13+ deployment, 12.5+ build system w/ 13.0 base SDK (Xcode 14.0), Ninja, 64-bit only Ubuntu 18.04+, Debian 10+, Ninja
5615 112 Feb 2023 Win 10+ deployment, Win 10+ build system w/ VS2022 17.5+, Win 10.0.22621 SDK, Ninja macOS 10.13+ deployment, 12.5+ build system w/ 13.0 base SDK (Xcode 14.0), Ninja, 64-bit only Ubuntu 18.04+, Debian 10+, Ninja
5563 111 Jan 2023 Win 10+ deployment, Win 10+ build system w/ VS2019 16.11.13+, Win 10.0.20348 SDK, Ninja macOS 10.13+ deployment, 12.5+ build system w/ 13.0 base SDK (Xcode 14.0), Ninja, 64-bit only Ubuntu 18.04+, Debian 10+, Ninja
5481 110 Dec 2022 Win 10+ deployment, Win 10+ build system w/ VS2019 16.11.13+, Win 10.0.20348 SDK, Ninja macOS 10.13+ deployment, 12.5+ build system w/ 13.0 base SDK (Xcode 14.0), Ninja, 64-bit only Ubuntu 18.04+, Debian 10+, Ninja
5414 109 Nov 2022 Win 7+ deployment, Win 10+ build system w/ VS2019 16.11.13+, Win 10.0.20348 SDK, Ninja macOS 10.13+ deployment, 12.5+ build system w/ 13.0 base SDK (Xcode 14.0), Ninja, 64-bit only Ubuntu 18.04+, Debian 10+, Ninja
5359 108 Oct 2022 Win 7+ deployment, Win 10+ build system w/ VS2019 16.11.13+, Win 10.0.20348 SDK, Ninja macOS 10.13+ deployment, 12+ build system w/ 12.3 base SDK (Xcode 13.3), Ninja, 64-bit only Ubuntu 18.04+, Debian 10+, Ninja
5304 107 Sep 2022 Win 7+ deployment, Win 10+ build system w/ VS2019 16.11.13+, Win 10.0.20348 SDK, Ninja macOS 10.13+ deployment, 12+ build system w/ 12.3 base SDK (Xcode 13.3), Ninja, 64-bit only Ubuntu 18.04+, Debian 10+, Ninja
5249 106 Aug 2022 Win 7+ deployment, Win 10+ build system w/ VS2019 16.11.13+, Win 10.0.20348 SDK, Ninja macOS 10.13+ deployment, 12+ build system w/ 12.3 base SDK (Xcode 13.3), Ninja, 64-bit only Ubuntu 18.04+, Debian 10+, Ninja
5195 105 Jul 2022 Win 7+ deployment, Win 10+ build system w/ VS2019 16.11.13+, Win 10.0.20348 SDK, Ninja macOS 10.13+ deployment, 12+ build system w/ 12.3 base SDK (Xcode 13.3), Ninja, 64-bit only Ubuntu 18.04+, Debian 10+, Ninja
5112 104 Jun 2022 Win 7+ deployment, Win 10+ build system w/ VS2019 16.11.13+, Win 10.0.20348 SDK, Ninja macOS 10.13+ deployment, 12+ build system w/ 12.3 base SDK (Xcode 13.3), Ninja, 64-bit only Ubuntu 18.04+, Debian 10+, Ninja
5060 103 May 2022 Win 7+ deployment, Win 10+ build system w/ VS2019 16.11.13+, Win 10.0.20348 SDK, Ninja macOS 10.11+ deployment, 12+ build system w/ 12.3 base SDK (Xcode 13.3), Ninja, 64-bit only Ubuntu 18.04+, Debian 10+, Ninja
5005 102 Apr 2022 Win 7+ deployment, Win 10+ build system w/ VS2019 16.11.13+, Win 10.0.20348 SDK, Ninja macOS 10.11+ deployment, 12+ build system w/ 12.3 base SDK (Xcode 13.3), Ninja, 64-bit only Ubuntu 18.04+, Debian 10+, Ninja
4951 101 Mar 2022 Win 7+ deployment, Win 10+ build system w/ VS2019 16.8.4+, Win 10.0.19041 SDK, Ninja macOS 10.11+ deployment, 11.3+ build system w/ 12.1 base SDK (Xcode 13.2.1), Ninja, 64-bit only Ubuntu 18.04+, Debian 10+, Ninja
4896 100 Feb 2022 Win 7+ deployment, Win 10+ build system w/ VS2019 16.8.4+, Win 10.0.19041 SDK, Ninja macOS 10.11+ deployment, 11.3+ build system w/ 12.1 base SDK (Xcode 13.2.1), Ninja, 64-bit only Ubuntu 18.04+, Debian 10+, Ninja
4844 99 Jan 2022 Win 7+ deployment, Win 10+ build system w/ VS2019 16.8.4+, Win 10.0.19041 SDK, Ninja macOS 10.11+ deployment, 10.15.4+ build system w/ 11.0 base SDK (Xcode 12.2-13.0), Ninja, 64-bit only Ubuntu 18.04+, Debian 10+, Ninja
4758 98 Dec 2021 Win 7+ deployment, Win 10+ build system w/ VS2019 16.8.4+, Win 10.0.19041 SDK, Ninja macOS 10.11+ deployment, 10.15.4+ build system w/ 11.0 base SDK (Xcode 12.2-13.0), Ninja, 64-bit only Ubuntu 18.04+, Debian 10+, Ninja
4692 97 Nov 2021 Win 7+ deployment, Win 10+ build system w/ VS2019 16.8.4+, Win 10.0.19041 SDK, Ninja macOS 10.11+ deployment, 10.15.4+ build system w/ 11.0 base SDK (Xcode 12.2-13.0), Ninja, 64-bit only Ubuntu 18.04+, Debian 10+, Ninja
4664 96 Oct 2021 Win 7+ deployment, Win 10+ build system w/ VS2019 16.8.4+, Win 10.0.19041 SDK, Ninja macOS 10.11+ deployment, 10.15.4+ build system w/ 11.0 base SDK (Xcode 12.2-13.0), Ninja, 64-bit only Ubuntu 18.04+, Debian 10+, Ninja
4638 95 Sep 2021 Win 7+ deployment, Win 10+ build system w/ VS2019 16.8.4+, Win 10.0.19041 SDK, Ninja macOS 10.11+ deployment, 10.15.4+ build system w/ 11.0 base SDK (Xcode 12.2-12.5), Ninja, 64-bit only Ubuntu 18.04+, Debian 10+, Ninja
4606 94 Aug 2021 Win 7+ deployment, Win 10+ build system w/ VS2019 16.8.4+, Win 10.0.19041 SDK, Ninja macOS 10.11+ deployment, 10.15.4+ build system w/ 11.0 base SDK (Xcode 12.2-12.5), Ninja, 64-bit only Ubuntu 18.04+, Debian 10+, Ninja
4577 93 Jul 2021 Win 7+ deployment, Win 10+ build system w/ VS2019 16.8.4+, Win 10.0.19041 SDK, Ninja macOS 10.11+ deployment, 10.15.4+ build system w/ 11.0 base SDK (Xcode 12.2-12.5), Ninja, 64-bit only Ubuntu 18.04+, Debian 10+, Ninja
4515 92 Jun 2021 Win 7+ deployment, Win 10+ build system w/ VS2019 16.8.4+, Win 10.0.19041 SDK, Ninja macOS 10.11+ deployment, 10.15.4+ build system w/ 11.0 base SDK (Xcode 12.2-12.5), Ninja, 64-bit only Ubuntu 16.04+, Debian Sid+, Ninja
4472 91 Apr 2021 Win 7+ deployment, Win 10+ build system w/ VS2019 16.8.4+, Win 10.0.19041 SDK, Ninja macOS 10.11+ deployment, 10.15.4+ build system w/ 11.0 base SDK (Xcode 12.2-12.5), Ninja, 64-bit only Ubuntu 16.04+, Debian Sid+, Ninja
4430 90 Mar 2021 Win 7+ deployment, Win 10+ build system w/ VS2019 16.8.4+, Win 10.0.19041 SDK, Ninja macOS 10.11+ deployment, 10.15.4+ build system w/ 11.0 base SDK (Xcode 12.2), Ninja, 64-bit only Ubuntu 16.04+, Debian Sid+, Ninja
4389 89 Feb 2021 Win 7+ deployment, Win 10+ build system w/ VS2017 15.7.1+, Win 10.0.19041 SDK, Ninja macOS 10.11+ deployment, 10.15.4+ build system w/ 11.0 base SDK (Xcode 12.2), Ninja, 64-bit only Ubuntu 16.04+, Debian Sid+, Ninja
4324 88 Dec 2020 Win 7+ deployment, Win 10+ build system w/ VS2017 15.7.1+, Win 10.0.19041 SDK, Ninja macOS 10.11+ deployment, 10.15.4+ build system w/ 11.0 base SDK (Xcode 12.2), Ninja, 64-bit only Ubuntu 16.04+, Debian Sid+, Ninja
4280 87 Oct 2020 Win 7+ deployment, Win 10+ build system w/ VS2017 15.7.1+, Win 10.0.19041 SDK, Ninja macOS 10.10+ deployment, 10.14.4+ build system w/ 10.15.1 base SDK (Xcode 11.2), Ninja, 64-bit only Ubuntu 16.04+, Debian Sid+, Ninja
4240 86 Sep 2020 Win 7+, VS2017 15.7.1+, Win 10.0.19041 SDK, Ninja macOS 10.10-10.15, 10.10+ deployment target, 10.14.4+ build system w/ 10.15.1 base SDK (Xcode 11.2), Ninja, 64-bit only Ubuntu 16.04+, Debian Sid+, Ninja
4183 85 Jul 2020 Win 7+, VS2017 15.7.1+, Win 10.0.19041 SDK, Ninja macOS 10.10-10.15, 10.10+ deployment target, 10.14.4+ build system w/ 10.15.1 base SDK (Xcode 11.2), Ninja, 64-bit only Ubuntu 14.04+, Debian Jessie+, Ninja
4147 84 Jun 2020 Win 7+, VS2017 15.7.1+, Win 10.0.18362 SDK, Ninja macOS 10.10-10.15, 10.10+ deployment target, 10.14.4+ build system w/ 10.15.1 base SDK (Xcode 11.2), Ninja, 64-bit only Ubuntu 14.04+, Debian Jessie+, Ninja
4103 83 Apr 2020 Win 7+, VS2017 15.7.1+, Win 10.0.18362 SDK, Ninja macOS 10.10-10.15, 10.10+ deployment target, 10.14.4+ build system w/ 10.15.1 base SDK (Xcode 11.2), Ninja, 64-bit only Ubuntu 14.04+, Debian Jessie+, Ninja
4044 81 Mar 2020 Win 7+, VS2017 15.7.1+, Win 10.0.18362 SDK, Ninja macOS 10.10-10.14, 10.10+ deployment target, 10.13.2+ build system w/ 10.13+ base SDK (Xcode 9.3.1+), Ninja, 64-bit only Ubuntu 14.04+, Debian Jessie+, Ninja
3987 80 Feb 2020 Win 7+, VS2017 15.7.1+, Win 10.0.18362 SDK, Ninja macOS 10.10-10.14, 10.10+ deployment target, 10.13.2+ build system w/ 10.13+ base SDK (Xcode 9.3.1+), Ninja, 64-bit only Ubuntu 14.04+, Debian Jessie+, Ninja
3945 79 Nov 2019 Win 7+, VS2017 15.7.1+, Win 10.0.18362 SDK, Ninja macOS 10.10-10.14, 10.10+ deployment target, 10.13.2+ build system w/ 10.13+ base SDK (Xcode 9.3.1+), Ninja, 64-bit only Ubuntu 14.04+, Debian Jessie+, Ninja
3904 78 Oct 2019 Win 7+, VS2017 15.7.1+, Win 10.0.18362 SDK, Ninja macOS 10.10-10.14, 10.10+ deployment target, 10.13.2+ build system w/ 10.13+ base SDK (Xcode 9.3.1+), Ninja, 64-bit only Ubuntu 14.04+, Debian Jessie+, Ninja
3865 77 Sep 2019 Win 7+, VS2017 15.7.1+, Win 10.0.18362 SDK, Ninja macOS 10.10-10.14, 10.10+ deployment target, 10.13.2+ build system w/ 10.13+ base SDK (Xcode 9.3.1+), Ninja, 64-bit only Ubuntu 14.04+, Debian Jessie+, Ninja
3809 76 Jul 2019 Win 7+, VS2017 15.7.1+, Win 10.0.17763 SDK, Ninja macOS 10.10-10.14, 10.10+ deployment target, 10.13.2+ build system w/ 10.13+ base SDK (Xcode 9.3.1+), Ninja, 64-bit only Ubuntu 14.04+, Debian Jessie+, Ninja
3770 75 Jun 2019 Win 7+, VS2017 15.7.1+, Win 10.0.17763 SDK, Ninja macOS 10.10-10.14, 10.10+ deployment target, 10.13.2+ build system w/ 10.13+ base SDK (Xcode 9.3.1+), Ninja, 64-bit only Ubuntu 14.04+, Debian Jessie+, Ninja
3729 74 (CEF 74, 3) Mar 2019 Win 7+, VS2017 15.7.1+, Win 10.0.17763 SDK, Ninja macOS 10.10-10.14, 10.10+ deployment target, 10.13.2+ build system w/ 10.13+ base SDK (Xcode 9.3.1+), Ninja, 64-bit only Ubuntu 14.04+, Debian Jessie+, Ninja
3683 73 (CEF 73, 3) Feb 2019 Win 7+, VS2017 15.7.1+, Win 10.0.17134.0 or 10.0.17763 SDK, Ninja macOS 10.10-10.14, 10.10+ deployment target, 10.12+ build system w/ 10.12+ base SDK (Xcode 8.3.2+), Ninja, 64-bit only Ubuntu 14.04+, Debian Jessie+, Ninja
3626 72 (CEF 3) Dec 2018 Win 7+, VS2017 15.7.1+, Win 10.0.17134.0 or 10.0.17763 SDK, Ninja macOS 10.10-10.14, 10.10+ deployment target, 10.12+ build system w/ 10.12+ base SDK (Xcode 8.3.2+), Ninja, 64-bit only Ubuntu 14.04+, Debian Jessie+, Ninja
3578 71 (CEF 3) Oct 2018 Win 7+, VS2017 15.7.1+, Win10.0.17134.0 SDK, Ninja macOS 10.10-10.14, 10.10+ deployment target, 10.12+ build system w/ 10.12+ base SDK (Xcode 8.3.2+), Ninja, 64-bit only Ubuntu 14.04+, Debian Jessie+, Ninja
3538 70 (CEF 3) Sep 2018 Win 7+, VS2017 15.7.1+, Win10.0.17134.0 SDK, Ninja macOS 10.10-10.13, 10.10+ deployment target, 10.12+ build system w/ 10.12+ base SDK (Xcode 8.3.2+), Ninja, 64-bit only Ubuntu 14.04+, Debian Jessie+, Ninja
3497 69 (CEF 3) Jul 2018 Win 7+, VS2017 15.7.1+, Win10.0.17134.0 SDK, Ninja macOS 10.10-10.13, 10.10+ deployment target, 10.12+ build system w/ 10.12+ base SDK (Xcode 8.3.2+), Ninja, 64-bit only Ubuntu 14.04+, Debian Jessie+, Ninja
3440 68 (CEF 3) Jun 2018 Win 7+, VS2017 15.7.1+, Win10.0.17134.0 SDK, Ninja macOS 10.10-10.13, 10.10+ deployment target, 10.12+ build system w/ 10.12+ base SDK (Xcode 8.3.2+), Ninja, 64-bit only Ubuntu 14.04+, Debian Jessie+, Ninja
3396 67 (CEF 3) Apr 2018 Win 7+, VS2017 15.3.2+, Win10.0.15063.468 SDK, Ninja macOS 10.10-10.13, 10.10+ deployment target, 10.12+ build system w/ 10.12+ base SDK (Xcode 8.3.2+), Ninja, 64-bit only Ubuntu 14.04+, Debian Jessie+, Ninja
3359 66 (CEF 3) Mar 2018 Win 7+, VS2017 15.3.2+, Win10.0.15063.468 SDK, Ninja macOS 10.10-10.13, 10.10+ deployment target, 10.12+ build system w/ 10.12+ base SDK (Xcode 8.3.2+), Ninja, 64-bit only Ubuntu 14.04+, Debian Jessie+, Ninja
3325 65 (CEF 3) Jan 2018 Win 7+, VS2017 15.3.2+, Win10.0.15063.468 SDK, Ninja macOS 10.10-10.13, 10.10+ deployment target, 10.12+ build system w/ 10.12+ base SDK (Xcode 8.3.2+), Ninja, 64-bit only Ubuntu 14.04+, Debian Jessie+, Ninja
3282 64 (CEF 3) Dec 2017 Win 7+, VS2017 15.3.2+, Win10.0.15063.468 SDK, Ninja macOS 10.10-10.13, 10.10+ deployment target, 10.12+ build system w/ 10.12+ base SDK (Xcode 8.3.2+), Ninja, 64-bit only Ubuntu 14.04+, Debian Jessie+, Ninja
3239 63 (CEF 3) Oct 2017 Win 7+, VS2017 15.3.2+, Win10.0.15063.468 SDK, Ninja macOS 10.10-10.13, 10.10+ deployment target, 10.12+ build system w/ 10.12+ base SDK (Xcode 8.3.2+), Ninja, 64-bit only Ubuntu 14.04+, Debian Jessie+, Ninja
3202 62 (CEF 3) Sep 2017 Win 7+, VS2015u3, Win10.0.14393 SDK, Ninja macOS 10.10-10.12, 10.10+ deployment target, 10.12+ build system w/ 10.12+ base SDK (Xcode 8.3.2+), Ninja, 64-bit only Ubuntu 14.04+, Debian Jessie+, Ninja
3163 61 (CEF 3) Jul 2017 Win 7+, VS2015u3, Win10.0.14393 SDK, Ninja macOS 10.9-10.12, 10.9+ deployment target, 10.9+ build system w/ 10.10+ base SDK (Xcode 8.3+), Ninja, 64-bit only Ubuntu 14.04+, Debian Jessie+, Ninja
3112 60 (CEF 3) Jun 2017 Win 7+, VS2015u3, Win10.0.14393 SDK, Ninja macOS 10.9-10.12, 10.9+ deployment target, 10.9+ build system w/ 10.10+ base SDK (Xcode 8.3+), Ninja, 64-bit only Ubuntu 14.04+, Debian Jessie+, Ninja
3071 59 (CEF 3) Apr 2017 Win 7+, VS2015u3, Win10.0.14393 SDK, Ninja macOS 10.9-10.12, 10.9+ deployment target, 10.9+ build system w/ 10.10+ base SDK (Xcode 8.3+), Ninja, 64-bit only Ubuntu 14.04+, Debian Jessie+, Ninja
3029 58 (CEF 3) Mar 2017 Win 7+, VS2015u3, Win10.0.14393 SDK, Ninja macOS 10.9-10.12, 10.9+ deployment target, 10.9+ build system w/ 10.10+ base SDK (Xcode 8.3+), Ninja, 64-bit only Ubuntu 14.04+, Debian Wheezy+, Ninja
2987 57 (CEF 3) Jan 2017 Win 7+, VS2015u3, Win10.0.14393 SDK, Ninja macOS 10.9-10.12, 10.9+ deployment target, 10.9+ build system w/ 10.10+ base SDK (Xcode 7.3.1+), 64-bit only Ubuntu 14.04+, Debian Wheezy+, Ninja
2924 56 (CEF 3) Nov 2016 Win 7+, VS2015u3, Win10.0.10586 SDK, Ninja macOS 10.9-10.12, 10.9+ deployment target, 10.9+ build system w/ 10.10+ base SDK (Xcode 7.3.1+), Ninja, 64-bit only Ubuntu 14.04+, Debian Wheezy+, Ninja
2883 55 (CEF 3) Oct 2016 Win 7+, VS2015u3, Win10.0.10586 SDK, Ninja macOS 10.9-10.12, 10.7+ deployment target, 10.9+ build system w/ 10.10+ base SDK (Xcode 7.3.1+), Ninja, 64-bit only Ubuntu 14.04+, Debian Wheezy+, Ninja
2840 54 (CEF 3) Aug 2016 Win 7+, VS2015u2 or VS2015u3, Win10.0.10586 SDK, Ninja macOS 10.9-10.12, 10.7+ deployment target, 10.9+ build system w/ 10.10+ base SDK (Xcode 7.3.1+), Ninja, 64-bit only Ubuntu 14.04+, Debian Wheezy+, Ninja
2785 53 (CEF 3) Jul 2016 Win 7+, VS2015u2 or VS2015u3, Win10.0.10586 SDK, Ninja macOS 10.9-10.11, 10.7+ deployment target, 10.9+ build system w/ 10.10+ base SDK (Xcode 7.3.1+), Ninja, 64-bit only Ubuntu 14.04+, Debian Wheezy+, Ninja
2743 52 (CEF 3) May 2016 Win 7+, VS2015u2 or VS2015u3, Win10.0.10586 SDK, Ninja macOS 10.9-10.11, 10.7+ deployment target, 10.9+ build system w/ 10.10+ base SDK (Xcode 7.1.1+), Ninja, 64-bit only Ubuntu 14.04+, Debian Wheezy+, Ninja
2704 51 (CEF 3) Apr 2016 Win 7+, VS2015u2, Win10.0.10586 SDK, Ninja macOS 10.9-10.11, 10.7+ deployment target, 10.9+ build system w/ 10.10+ base SDK (Xcode 7.1.1+), Ninja, 64-bit only Ubuntu 14.04+, Debian Wheezy+, Ninja
2623 49 (CEF 3) Jan 2016 WinXP+, VS2013u4 or VS2015u1 (experimental), Win10 SDK, Ninja macOS 10.6-10.11, 10.6+ deployment target, 10.7+ build system w/ 10.10+ base SDK (Xcode 7.1.1+), Ninja, 64-bit only Ubuntu 14.04+, Debian Wheezy+, Ninja
2526 47 (CEF 3) Oct 2015 WinXP+, VS2013u4 or VS2015u1 (experimental), Win8.1 SDK, Ninja macOS 10.6-10.11, 10.6+ deployment target, 10.10 base SDK, Xcode 6.1, Ninja, 64-bit only Ubuntu 12.04+, Debian Wheezy+, Ninja
2454 45 (CEF 3) Jul 2015 WinXP+, VS2013u4, Win8.1 SDK, Ninja macOS 10.6-10.10, 10.6+ deployment target, 10.9 base SDK, Xcode 6.1, Ninja, 64-bit only Ubuntu 12.04+, Debian Wheezy+, Ninja
2357 43 (CEF 3) Apr 2015 WinXP+, VS2013u4, Win8.1 SDK, Ninja macOS 10.6-10.10, 10.6+ deployment target, 10.9 base SDK, Xcode 6.1, Ninja, 64-bit only Ubuntu 12.04+, Debian Wheezy+, Ninja
2272 41 (CEF 3) Jan 2015 WinXP+, VS2013u4, Win8.1 SDK, Ninja macOS 10.6-10.10, 10.6+ deployment target, 10.9 base SDK, Xcode 6.1, Ninja, 64-bit only Ubuntu 12.04+, Debian Wheezy+, Ninja
2171 39 (CEF 3) Oct 2014 WinXP+, VS2013u4, Win8.1 SDK, Ninja macOS 10.6-10.9, 10.6+ SDK, Xcode 5.1.1, Ninja Ubuntu 12.04+, Debian Wheezy+, Ninja
2062 37 (CEF 3) Aug 2014 WinXP+, VS2013, Win8 SDK, Ninja macOS 10.6-10.9, 10.6+ SDK, Xcode 5.1.1, Ninja Ubuntu 12.04+, Debian Wheezy+, Ninja
1916 35 (CEF 3) Apr 2014 WinXP+, VS2013, Win8 SDK, Ninja macOS 10.6-10.9, 10.6+ SDK, Xcode 5.1.1, Ninja Ubuntu 12.04+, Debian Wheezy+, Ninja
1750 33 (CEF 3) Jan 2014 WinXP+, VS2010-2013, Win8 SDK, Ninja macOS 10.6-10.9, 10.6+ SDK, Xcode 5.1.1, Ninja Ubuntu 12.04+, Debian Wheezy+, Ninja
1650 31 (CEF 3) Oct 2013 WinXP+, VS2010-2012, Win8 SDK, Ninja (optional) macOS 10.6-10.9, 10.6+ SDK, Xcode 5.1.1, Ninja Ubuntu 12.04+, Debian Wheezy+, Ninja
1547 29 (CEF 3) Jul 2013 WinXP+, VS2010-2012, Win8 SDK, Ninja (optional) macOS 10.6-10.8, 10.6+ SDK, Xcode 3.2.6-4.x, Ninja (optional) Ubuntu 12.04+, Debian Squeeze+, Ninja
1453 27 (CEF 1, 3) Apr 2013 WinXP+, VS2010, Win8 SDK, Ninja (optional) macOS 10.6-10.8, 10.6+ SDK, Xcode 3.2.6-4.x, Ninja (optional) Ubuntu 12.04+, Debian Squeeze+, Ninja (optional)
1364 25 (CEF 1, 3) Jan 2013 WinXP+, VS2010, Win8 SDK, Ninja (optional) macOS 10.6-10.8, Xcode 3.2.6-4.x, Ninja (optional) Ubuntu 12.04+, Debian Squeeze+, Ninja (optional)
1271 23 (CEF 1, 3) Oct 2012 WinXP+, VS2010, Win7 SDK macOS 10.6-10.8, 10.6+ SDK, Xcode 3.2.6-4.x Ubuntu 12.04+, Debian Squeeze+
1180 21 (CEF 1, 3) Aug 2012 WinXP+, VS2010, Win7 SDK macOS 10.6-10.7, 10.5+ SDK, Xcode 3.2.6-4.x Ubuntu 12.04+, Debian Squeeze+
1084 19 (CEF 1) Apr 2012 WinXP+, VS2008, Win7 SDK macOS 10.6-10.7, 10.5+ SDK, Xcode 3.2.6-4.x Ubuntu 10.04+, Debian Squeeze+
1025 18 (CEF 1) Feb 2012 WinXP+, VS2008, Win7 SDK macOS 10.6-10.7, 10.5+ SDK, Xcode 3.2.6-4.x Ubuntu 10.04+, Debian Squeeze+
963 17 (CEF 1) Dec 2011 WinXP+, VS2008, Win7 SDK macOS 10.6-10.7, 10.5+ SDK, Xcode 3.2.6 Ubuntu 10.04+, Debian Squeeze+

The following URL should be used for downloading release versions of CEF where YYYY is the release branch number.

Note that 1025 and older branches contain only CEF1 source code and that 1547 and newer branches contain only CEF3 source code.

Building from Source

Building from source code is currently supported on Windows, macOS and Linux platforms. Use of the Automated Method described below is recommended. Building the current CEF/Chromium master branch for local development is described on the Master Build Quick Start page. Building the current CEF/Chromium stable branch automatically for production use is described on the Automated Build Setup page. For other branches see the build requirements listed in the "Release Branches" section above and the "Build Notes" section below.

Automated Method

CEF provides tools for automatically downloading, building and packaging Chromium and CEF source code. These tools are the recommended way of building CEF locally and can also be integrated with automated build systems as described on the Automated Build Setup page. See the Master Build Quick Start page for an example of the recommended workflow for local development builds.

These steps apply to the Git workflow only. The Git workflow is recommended for all users and supports CEF3 master and newer CEF3 release branches (1750+).

1. Download the automate-git.py script. Use the most recent master version of this script even when building release branches.

2. On Linux: Chromium requires that certain packages be installed. You can install them by running the install-build-deps.sh script or by explicitly running the necessary installation commands.

3. Run the automate-git.py script at whatever interval is appropriate (for each CEF commit, once per day, once per week, etc).

To build master:

sh
python /path/to/automate/automate-git.py --download-dir=/path/to/download

To build a release branch:

sh
python /path/to/automate/automate-git.py --download-dir=/path/to/download --branch=2785

By default the script will download depot_tools, Chromium and CEF source code, run Debug and Release builds of CEF, and create a binary distribution package containing the build artifacts in the "/path/to/download/chromium/src/cef/binary_distrib" directory. Future runs of the script will perform the minimum work necessary (unless otherwise configured using command-line flags). For example, if there are no pending CEF or Chromium updates the script will do nothing.

If you run the script and CEF or Chromium updates are pending the "/path/to/download/chromium/src/cef" directory will be removed and replaced with a clean copy from "/path/to/download/cef_(branch)" (specify the --no-update command-line flag to disable updates). Make sure to back up any changes that you made in the "/path/to/download/chromium/src/cef" directory before re-running the script.

The same download directory can be used for building multiple CEF branches (just specify a different --branch command-line value). The existing "/path/to/download/chromium/src/out" directory will be moved to "/path/to/download/out_(previousbranch)" so that the build output from the previous branch is not lost. When you switch back to a previous branch the out directory will be restored to its original location.

The script will create a 32-bit build on Windows by default. To create a 64-bit build on Windows, macOS or Linux specify the --x64-build command-line flag. 32-bit builds on macOS are no longer supported starting with 2272 branch so this flag is now required when building 2272+ on that platform.

If you receive Git errors when moving an existing checkout from one branch to another you can force a clean Chromium Git checkout (specify the --force-clean command-line flag) and optionally a clean download of Chromium dependencies (specify the --force-clean-deps command-line flag). Any build output that currently exists in the "src/out" directory will be deleted. Re-downloading the Chromium dependencies can take approximately 30 minutes with a reasonably fast internet connection.

Add the --help command-line switch to output a complete list of supported command-line options.

Manual Downloading

See the Master Build Quick Start page for an example of the recommended developer workflow.

Manual Building

See the Master Build Quick Start page for an example of the recommended developer workflow.

Manual Packaging

After building both Debug and Release configurations you can use the make_distrib tool (.bat on Windows, .sh on macOS and Linux) to create a binary distribution.

sh
cd /path/to/chromium/src/cef/tools
./make_distrib.sh --ninja-build

If the process succeeds a binary distribution package will be created in the /path/to/chromium/src/cef/binary_distrib directory.

See the make_distrib.py script for additional usage options.

The resulting binary distribution can then be built using CMake and platform toolchains. See the README.txt file included with the binary distribution for more information.

Build Notes

This section summarizes build-related requirements and options.

  • Building on most platforms will require at least 8GB of system memory.
  • Ninja must be used when building newer CEF/Chromium branches.
  • Clang is used by default for compiling/linking Chromium/CEF on macOS in all branches, Linux starting in 2063 branch, and Windows starting in 3282 branch.
  • GYP is supported by 2785 branch and older. GN is supported by 2785 branch and newer, and required starting with 2840 branch. Set CEF_USE_GN=1 to build 2785 branch with GN instead of GYP.
  • To perform a 64-bit build on Windows (any branch) or macOS (branch 2171 or older) set GYP_DEFINES=target_arch=x64 (GYP only) or build the out/[Debug|Release]_GN_x64 target (GN only). To perform a 32-bit Linux build on a 64-bit Linux system see instructions on the Automated Build Setup page.
  • To perform an "official" build set GYP_DEFINES=buildtype=Official (GYP only) or GN_DEFINES=is_official_build=true (GN only). This will disable debugging code and enable additional link-time optimizations in Release builds. See instructions on the Automated Build Setup page for additional official build recommendations.
  • Windows -
    • If multiple versions of Visual Studio are installed on your system you can set the GYP_MSVS_VERSION environment variable to create project files for that version. For example, set the value to "2015" for VS2015 or "2017" for VS2017. Check the Chromium documentation for the correct value when using other Visual Studio versions.
    • If you wish to use Visual Studio for debugging and compiling in combination with a Ninja build you can set GYP_GENERATORS=ninja,msvs-ninja (GYP only) or GN_ARGUMENTS=--ide=vs2017 --sln=cef --filters=//cef/* (GN only) to generate both Ninja and VS project files. Visual Studio is supported only for debugging and compiling individual source files -- it will not build whole targets successfully. You must use Ninja when building CEF/Chromium targets.
    • For best local developer (non-official debug) build-time performance:
      • When using VS2015 set GN_DEFINES=is_win_fastlink=true for improved compile and link time (branches <= 3202).
      • When using VS2017 or VS2019 set GN_DEFINES=use_jumbo_build=true for improved compile and link time (branches <= 4044).
      • Component builds are supported by 3202 branch and newer and significantly reduce link time. Add is_component_build=true to GN_DEFINES in combination with the above VS-version-specific values. Component builds cannot be used to create a CEF binary distribution. See issue #1617 for details.
  • macOS -
    • The combination of deployment target and base SDK version will determine the platforms supported by the resulting binaries. For proper functioning you must use the versions specified under build requirements for each branch.
    • 32-bit builds are no longer supported with 2272 branch and newer. See here for the Chromium announcement.
  • Linux -
    • CEF is developed and tested on the Ubuntu version specified in the "Release Branches" section. It should be possible to build and run CEF on other compatible Linux distributions but this is untested.
    • The libgtkglext1-dev package is required in branch 1547 and newer to support the off-screen rendering example in cefclient. This is only a requirement for cefclient and not a requirement for other applications using CEF.

Chromium Update (`docs/chromium_update.md`)

This page describes how to update CEF to use the newest Chromium revision.

Contents


Overview

The Chromium developers work very hard to introduce new features and capabilities as quickly as possible. Consequently, projects like CEF that depend on Chromium must also be updated regularly. The update process can be complicated and must be done carefully to avoid introducing new bugs and breakage. Below are the steps to follow when updating CEF to work with a new Chromium revision.

1\. Setup a local developer checkout

Setup a local developer checkout of the CEF/Chromium master branch as described on the Master Build Quick Start page. The standard automate-git.py parameters discussed on that page are represented by the [...] placeholder in the below examples.

2\. Check for changes in Chromium's build requirements

Chromium's build requirements change over time. You may need to update your build environment before proceeding.

  • Windows: Visual Studio and Windows SDK requirements are listed here and here. Visual Studio bootstrap installers for specific versions can be found here.
  • MacOS: SDK and Xcode version requirements are listed here and here. SDK versions can also be mapped to Xcode versions using this table.
  • Linux: Version and distro requirements are listed here. Most dependency version issues can be avoided by building with a sysroot image by setting GN_DEFINES=use_sysroot=true.

If the build requirements have changed the MasterBuildQuickStart page will also need to be updated.

3\. Start the update

A\. Identify the target Chromium version

The Chromium release cycle utilizes milestone versions (M98, M99, etc.) and release channels (canary, beta, stable). New milestone versions branch approximately every 4 weeks and begin life in the canary channel. Over the next 4 weeks they progress through the beta and stable channels to reach Chrome's ~3 billion users globally.

The CEF project has a master branch for ongoing development and creates a release branch that tracks each milestone version. Automated builds are provided for currently supported versions.

  • CEF master updates target new milestone versions as they are announced to the chromium-dev mailing list. For example, this email was sent for the M100 branch. These versions should have 0 as the last component (e.g. "100.0.4896.0"). Updates are performed manually and usually take around 1 week to complete. The "Branch" date for upcoming milestones can be found on the Chromium schedule.
  • CEF beta and stable channel updates use the version information published here. These versions should not have 0 as the last component (e.g. "99.0.4844.45"). Updates are performed automatically in most cases and a PR will be submitted to the public repository if the build succeeds.

To manually specify the Chromium version and update the checkout of master:

sh
python automate-git.py [...] --no-build --fast-update --log-chromium-changes --chromium-checkout=refs/tags/100.0.4896.0

To manually specify the Chromium version and update the checkout of an existing release branch:

sh
python automate-git.py [...] --no-build --fast-update --force-cef-update --branch=4844 --chromium-checkout=refs/tags/99.0.4844.45

B\. Run CEF's `patch_updater.py` script

This will update the Chromium patch files in the patch/patches directory. If patch files cannot be updated automatically the automate-git.py script will fail.

Claude Code can assist with updating patches. See tools/claude/README.md.

If one or more patch files have unresolved conflicts you will get a failure like the following:

text
--------------------------------------------------------------------------------
!!!! FAILED PATCHES, fix manually and run with --resave
content_2015:
  Hunk #4 FAILED at 4220.
  1 out of 5 hunks FAILED -- saving rejects to file content/browser/frame_host/render_frame_host_impl.cc.rej
storage_partition_1973:
  Hunk #1 FAILED at 167.
  1 out of 1 hunk FAILED -- saving rejects to file content/browser/shared_worker/shared_worker_service_impl.cc.rej
--------------------------------------------------------------------------------

Use a text editor to manually fix the specified files, and then re-run the patch_updater.py script:

sh
$ cd /path/to/chromium_git/chromium/src/cef/tools
$ python patch_updater.py

If a patched file is missing (moved or deleted) you will get a failure like the following:

cpp
--> Reading patch file /path/to/chromium_git/chromium/src/cef/patch/patches/content_mojo_3123.patch
--> Skipping non-existing file /path/to/chromium_git/chromium/src/content/public/browser/document_service_base.h
--> Applying patch to /path/to/chromium_git/chromium/src
(Stripping trailing CRs from patch; use --binary to disable.)
can't find file to patch at input line 5
...
Skip this patch? [y] 
Skipping patch.
2 out of 2 hunks ignored
--> Saving changes to /path/to/chromium_git/chromium/src/cef/patch/patches/content_mojo_3123.patch
Traceback (most recent call last):
  File "/path/to/chromium_git/chromium/src/cef/tools/patch_updater.py", line 294, in <module>
    raise Exception('Failed to add paths: %s' % result['err'])
Exception: Failed to add paths: fatal: pathspec '/path/to/chromium_git/chromium/src/content/public/browser/document_service_base.h' did not match any files

You can use this Git command to discover what happened to the missing Chromium file:

sh
$ cd /path/to/chromium_git/chromium/src
$ git log --full-history -1 -- content/public/browser/document_service_base.h

Once you know the offending Git commit hash you can use the git show <hash> command or load https://crrev.com/<hash> in a web browser to see the contents of the change. Edit the patch file manually in a text editor to fix the paths and then re-run the patch_updater.py script as shown above.

To create a new patch file use this command:

sh
$ cd /path/to/chromium_git/chromium/src
$ git diff --no-prefix --relative [path1] [path2] > [name].patch

Copy the resulting [name].patch file to the src/cef/patch/patches directory and add an appropriate entry to the src/cef/patch/patch.cfg file.

To add additional files to an existing patch file use this command:

sh
$ cd /path/to/chromium_git/chromium/src/cef/tools
$ python patch_updater.py --resave --patch [name] --add [path1] --add [path2]

All paths are relative to the src directory by default.

After all patch files have been fixed you can re-run automate-git.py with the additional --resave command-line flag to resave the patch files and continue the update process:

sh
python automate-git.py [...] --resave

C\. Identify potentially relevant Chromium changes

This step creates a chromium_update_changes.diff file in your download directory that will act as your guide when updating the CEF source code. CEF began life as a customized version of content_shell and there's still a one-to-one relationship between many of the files. The list of relevant paths is taken from CEF's CHROMIUM_UPDATE.txt file.

D\. Identify problematic patterns in the Chromium source code

This step looks for patterns in Chromium src files that may cause issues for CEF. If these patterns are found then the automate-git.py script will create a chromium_update_patterns.txt file in your download directory and fail with output like the following:

text
Evaluating pattern: static_cast<StoragePartitionImpl\*>(
ERROR Matches found. See chromium_update_changes.diff for output.

In that case the contents of chromium_update_patterns.txt might look like this:

text
!!!! WARNING: FOUND PATTERN: static_cast<StoragePartitionImpl\*>(
New instances in non-test files should be converted to call StoragePartition methods.
See storage_partition_1973.patch.

content/browser/loader/navigation_url_loader_impl.cc:1295:  auto* partition = static_cast<StoragePartitionImpl*>(storage_partition);
content/browser/renderer_interface_binders.cc:189:        static_cast<StoragePartitionImpl*>(host->GetStoragePartition())

Use the process described in part B to fix these failures.

4\. Build CEF and fix whatever else is broken

The diff file created in step C above will assist you in this process. Refer to the Master Build Quick Start page for build instructions. If Chromium changes are required refer to the "Resolving Chromium breakage" section below.

Claude Code can assist with fixing compile/build errors. See tools/claude/README.md.

5\. Run CEF tests

After updating Chromium you should run some tests to verify that everything is working as expected. The tests should eventually be run on all supported platforms.

A. The cefclient sample application supports various runtime modes that should be tested for startup/shutdown crashes or strange runtime behaviors (on MacOS use open cefclient.app --args <args>):

sh
# Chrome bootstrap:
$ cefclient

# Chrome bootstrap + Alloy style:
$ cefclient --use-alloy-style
$ cefclient --use-alloy-style --use-native
$ cefclient --off-screen-rendering-enabled

# Multi-threaded message loop (Windows/Linux only):
$ cefclient --multi-threaded-message-loop
$ cefclient --use-alloy-style --multi-threaded-message-loop

# Chrome style + native parent (Windows/Linux only):
$ cefclient --use-native

B. Run the various manual tests available via the Tests menu in cefclient to verify that everything still works. If any particular subsystem had significant changes (like printing, for example) then make sure to give that subsystem additional testing.

C. Unit tests included in the ceftests application are expected to pass on all development platforms.

To run unit tests:

sh
# Test Chrome style (default)
$ ceftests --use-views

# Test Alloy style
$ ceftests --use-views --use-alloy-style

On macOS use ./ceftests.app/Contents/MacOS/ceftests as the test executable.

Some tests require synthetic events (mouse/key input, focus/activation, fullscreen, etc), device access, or timing considerations that are known to be flaky on headless/CI systems. To disable these tests during automated test runs add the --gtest_filter=-[test:...] command-line argument shown below.

On Windows:

text
:: Disabled tests:
:: - AudioOutputTest.*: Flaky
:: - DisplayTest.AutoResize: Flaky
:: - DisplayTest.Title: Flaky
:: - DraggableRegionsTest.*: Flaky
:: - NavigationTest.History: Flaky
:: - NavigationTest.Load*CtrlLeftClick*: Flaky
:: - NavigationTest.Load*MiddleClick*: Flaky
:: - PdfViewerTest.*: Flaky
:: - ScopedTempDir.TempDir: Flaky
:: - ViewsButtonTest.*Click*: Flaky
:: - ViewsButtonTest.MenuButtonCustomPopup*: Flaky
:: - ViewsTextfieldTest.TextfieldKeyEvent: Flaky
:: - ViewsWindowTest.WindowCreateWithOrigin: Flaky
ceftests --use-views [--use-alloy-style] ^
  --gtest_filter=-AudioOutputTest.*:DisplayTest.AutoResize:DisplayTest.Title:DraggableRegionsTest.*:NavigationTest.History:NavigationTest.Load*CtrlLeftClick*:NavigationTest.Load*MiddleClick*:PdfViewerTest.*:ScopedTempDir.TempDir:ViewsButtonTest.*Click*:ViewsButtonTest.MenuButtonCustomPopup*:ViewsTextfieldTest.TextfieldKeyEvent:ViewsWindowTest.WindowCreateWithOrigin

On macOS:

sh
# Disabled tests:
# - DisplayTest.AutoResize: Flaky
# - ViewsButtonTest.MenuButtonCustomPopupActivate: Flaky
# - ViewsWindowTest.Window*Fullscreen*: Fails/crashes on headless agents
./ceftests.app/Contents/MacOS/ceftests --use-views [--use-alloy-style] \
  --gtest_filter=-DisplayTest.AutoResize:ViewsButtonTest.MenuButtonCustomPopupActivate:ViewsWindowTest.Window\*Fullscreen\*

On Linux:

sh
# Headless agents often run using `xvfb-run`.
xvfb-run -a ceftests --use-views [--use-alloy-style] \

# Docker containers may not support the Chromium sandbox.
  --no-sandbox \

# Docker containers often default to 64MB for /dev/shm, which is far too small for Chromium.
# Run docker with `--shm-size=2g` or add `--disable-dev-shm-usage` to increase available space.
  --disable-dev-shm-usage \

# Disabled tests:
# - OSRTest.*: Flaky
# - ViewsWindowTest.WindowMaximize*: Flaky
# - ViewsWindowTest.WindowMinimize*: Flaky
  --gtest_filter=-OSRTest.*:ViewsWindowTest.WindowMaximize*:ViewsWindowTest.WindowMinimize*

If automated test runs are slow (tests timing out) you can additionally add --test-timeout-multiplier=3 for e.g. 3x longer test timeouts.

6\. Update the compatible Chromium version

Update the compatible Chromium version listed in CEF's CHROMIUM_BUILD_COMPATIBILITY.txt file.

7\. Create and submit a PR

Create a commit with your changes and submit a PR as described on the Contributing With Git page. See the CEF commit history for examples of the expected commit message format.

8\. Test your changes on other supported platforms

You can update, build and run tests automatically on those platforms with the following command:

sh
python automate-git.py [...] --run-tests --build-failure-limit=1000 --fast-update --force-cef-update

If running in headless mode on Linux install the xvfb package and add the --test-prefix=xvfb-run --test-args="--no-sandbox" command-line flags.

Access to automated builders as described on the Automated Build Setup page is recommended for this step.

Resolving automate_git.py update failures

The automate_git.py script can sometimes fail to update an existing Chromium checkout due to errors while running Chromium tooling (git, gclient, etc). When that occurs you can perform the update steps manually.

The following example assumes the usual file structure with path/to/chromium_git as your existing automate_git.py download directory.

sh
# Check out the required Chromium version.
$ cd path/to/chromium_git/chromium/src
$ git checkout refs/tags/100.0.4896.0

# Copy the `cef` directory to `chromium/src/cef` if it doesn't already exist.
$ cd path/to/chromium_git
$ cp -R cef chromium/src/

# Windows only. Apply the patch for `runhooks`.
$ cd path/to/chromium_git/chromium/src/cef/tools
$ python patch_updater.py --reapply --patch runhooks

# Windows only. Don't expect VS to be installed via depot_tools.
$ set DEPOT_TOOLS_WIN_TOOLCHAIN=0

# Update Chromium third_party dependencies and execute runhooks.
$ cd path/to/chromium_git/chromium/src
$ gclient sync --with_branch_heads

You can then run cef_create_projects.[bat|sh] and ninja to build CEF in the usual way.

Resolving Chromium breakage

In most cases (say, 90% of the time) any code breakage will be due to naming changes, minor code reorganization and/or project name/location changes. The remaining 10% can require pretty significant changes to CEF, usually due to the ongoing refactoring in Chromium code. If you identify a change to Chromium that has broken a required feature for CEF, and you can't work around the breakage by making reasonable changes to CEF, then you should work with the Chromium team to resolve the problem.

1. Identify the specific Chromium revision that broke the feature and make sure you understand why the change was made.

2. Post a message to the chromium-dev mailing list explaining why the change broke CEF and either seeking additional information or suggesting a fix that works for both CEF and Chromium.

3. After feedback from the Chromium developers create an issue in the Chromium issue tracker and a code review with the fix and publish it with the appropriate (responsible) developer(s) as reviewers.

4. Follow through with the Chromium developer(s) to get the code review committed.

The CEF build currently contains a patch capability that should be used only as a last resort or as a stop-gap measure if you expect the code review to take a while. The best course of action is always to get your Chromium changes accepted into the Chromium trunk if possible.


Code Signing (`docs/code_signing.md`)

This page explains how to sign CEF-based applications for distribution on Windows and macOS, including macOS notarization.

Contents


Overview

Code signing identifies the publisher and allows signature verification to detect changes to signed content. Signing is a release packaging step: finish compiling, stripping, combining architectures, editing resources and constructing the final layout before signing. Generate archive hashes and update metadata from the final signed artifacts.

Use your own signing identity and protect its private key. A signing service or hardware-backed key can perform signing without exposing the key to the build machine. Keep credentials outside the source tree and build logs, and verify the returned files before packaging them.

On Windows, executable files use Authenticode signatures; a signed catalog can also authenticate resource files. On macOS, nested code and the enclosing bundles are signed in order, with entitlements assigned to the appropriate executable processes. Distribution outside the Mac App Store additionally involves Apple's notarization service.

File lists and entitlements can change between Chromium versions. Check the sources corresponding to the Chromium revision used by your CEF build. The guidance below reflects the sources inspected as of October 2026; the Chromium links point to the main branch and may change over time.

Windows

Windows prerequisites

Install the Windows SDK and make signtool.exe available in your command prompt. Catalog creation also uses makecat.exe. Obtain an Authenticode code-signing certificate and configure access to its private key through the certificate store, a hardware token, or a signing service.

The commands below run in Command Prompt, with an identity in the current user's Personal (My) certificate store. Replace CERTIFICATE_THUMBPRINT and TIMESTAMP_URL with your certificate's thumbprint and an RFC 3161 timestamp endpoint supported by your certificate provider. A machine-store identity additionally requires /sm; hardware or remote signing may require provider-specific configuration.

See Microsoft's SignTool reference for certificate selection and timestamp options. Chromium's Windows updater signing script demonstrates signing embedded binaries before the enclosing installer. That script handles Chromium updater packaging and is a reference, rather than a general CEF application signing tool.

Sign application binaries and installers

Inventory the executable binaries shipped with your application: the main executable, subprocess executables, application DLLs, CEF DLLs and any native dependencies. Sign unsigned binaries that you distribute under your identity. Preserve valid third-party signatures when your packaging and loading policy permit them.

For the CEF bootstrap configuration described in Sandbox Setup and Installer, the bootstrap executable and its adjacent chrome_elf.dll must use the same signing certificate. Sign these with your application identity, along with the client DLL. A separately downloaded CEF shared installation retains its distribution certificate and catalog.

After all resource modifications, sign each binary:

bat
signtool sign /s My /sha1 CERTIFICATE_THUMBPRINT /fd SHA256 /tr TIMESTAMP_URL /td SHA256 /v "MyApp.exe"
signtool sign /s My /sha1 CERTIFICATE_THUMBPRINT /fd SHA256 /tr TIMESTAMP_URL /td SHA256 /v "MyApp.dll"
signtool sign /s My /sha1 CERTIFICATE_THUMBPRINT /fd SHA256 /tr TIMESTAMP_URL /td SHA256 /v "chrome_elf.dll"

Here /sha1 selects a certificate by thumbprint; /fd SHA256 selects the signature digest. Verify each resulting file under the Authenticode policy:

bat
signtool verify /pa /all /tw /v "MyApp.exe"
signtool verify /pa /all /tw /v "MyApp.dll"
signtool verify /pa /all /tw /v "chrome_elf.dll"

Treat failures and warnings as release failures, including a missing timestamp. Repeat for every shipped binary. Then build your installer from the signed payload, sign the resulting installer .exe or .msi, and verify it with the same commands. Signing the installer does not sign its contained binaries. ZIP and tar archives carry the already signed files.

Test with a self-signed certificate

For local development, you can test bootstrap signing using a self-signed certificate. This certificate is trusted only on machines where you explicitly install it; use a publicly trusted code-signing certificate for release distribution.

Run the following in PowerShell under the account that will launch the application. It creates a test certificate and trusts it in that user's certificate stores:

powershell
$certificate = New-SelfSignedCertificate -DnsName "cef-signing-test.invalid" `
  -Type CodeSigning -CertStoreLocation Cert:\CurrentUser\My
$certificatePath = Join-Path $env:TEMP "cef-signing-test.cer"
Export-Certificate -Cert $certificate -FilePath $certificatePath
Import-Certificate -FilePath $certificatePath -CertStoreLocation Cert:\CurrentUser\TrustedPublisher
Import-Certificate -FilePath $certificatePath -CertStoreLocation Cert:\CurrentUser\Root

Replace the application directory and filenames below with your staged bootstrap executable, client DLL and adjacent chrome_elf.dll. Sign each with the newly created certificate:

powershell
$appDir = "C:\path\to\app"
foreach ($name in "MyApp.exe", "MyApp.dll", "chrome_elf.dll") {
  $path = Join-Path $appDir $name
  Set-AuthenticodeSignature -FilePath $path -Certificate $certificate -HashAlgorithm SHA256
  signtool verify /pa /all /v $path
  if ($LASTEXITCODE -ne 0) { throw "Signature verification failed: $path" }
}

This local test omits timestamping. The release signing commands above include a timestamp and check for its presence. Keeping the returned certificate object also avoids accidentally selecting another certificate from the Personal store.

After testing, open certmgr.msc and remove this test certificate from Personal, Trusted Root Certification Authorities and Trusted Publishers, identifying it by $certificate.Thumbprint. Remove the exported .cer file as well. Removing the trust entries makes these test signatures untrusted on the machine; rebuild or re-sign the test binaries before subsequent use.

Create a signed catalog

A catalog contains hashes for an explicit set of files, including data files such as .pak, .dat and .json that do not have embedded Authenticode signatures. For CEF shared installations, read the Windows Installer guide, particularly Sign your binaries, and the bootstrap installer implementation and configuration. Its download-time distribution verification checks the catalog and the expected certificate; creating a catalog alone does not configure an application to trust a new publisher. The implementation guide's Extended Configuration describes certificate_thumbprint for distributions signed with another identity, including the requirement for a distinct installation path and matching client-side load policy.

For a distribution you publish yourself, first stage the complete payload and sign its executable files. Then create a Catalog Definition File (catalog.cdf). This small example assumes C:\staging is the distribution root:

ini
[CatalogHeader]
Name=catalog.cat
ResultDir=C:\staging
PublicVersion=0x00000200
EncodingType=0x00010001
CatalogVersion=2
HashAlgorithms=SHA256

[CatalogFiles]
<HASH>libcef=C:\staging\Release\libcef.dll
<HASH>resources=C:\staging\Resources\resources.pak
<HASH>icudtl=C:\staging\Resources\icudtl.dat

Generate one uniquely named entry for every payload file, using its actual staged path. The paths above are illustrative. Exclude the catalog itself and the temporary CDF. End the CDF with a newline. MakeCat computes catalog hashes from these files; do not substitute ordinary SHA-256 checksums for its file-hashing procedure.

bat
makecat -v "C:\staging\catalog.cdf"
signtool sign /s My /sha1 CERTIFICATE_THUMBPRINT /fd SHA256 /tr TIMESTAMP_URL /td SHA256 /v "C:\staging\catalog.cat"
signtool verify /pa /all /tw /v "C:\staging\catalog.cat"
signtool verify /pa /v /c "C:\staging\catalog.cat" "C:\staging\Release\libcef.dll"
signtool verify /pa /v /c "C:\staging\catalog.cat" "C:\staging\Resources\resources.pak"

Verify every member against the signed catalog. Package the signed payload and catalog.cat together after removing the temporary CDF. If payload content changes, regenerate and re-sign the catalog. For an existing CEF shared distribution, preserve its files and catalog and follow the installer's configured certificate verification policy.

macOS

macOS prerequisites

Use a macOS signing machine with Xcode installed and selected through xcode-select. Obtain a Developer ID Application certificate through the Apple Developer Program for distribution outside the Mac App Store. Import it and its private key into a keychain accessible to the signing user, and confirm the identity:

sh
security find-identity -v -p codesigning

For an unattended build, configure the signing keychain's unlock and private-key access before invoking codesign. If you change the user's keychain search list, save and restore it after signing. Lock a dedicated signing keychain when finished. Keep keychain passwords in your build system's secret store.

Use a Release, non-component CEF distribution. Assemble the final .app with its framework, resources and helper apps before signing. Preserve framework symlinks when copying it. The supplied CMake configuration constructs a versioned framework for newer Xcode versions; see the binary distribution's README.txt and CEF framework copy macro.

Chromium's macOS signing documentation, component definitions and signing implementation provide reference behavior. sign_chrome.py expects Chrome's bundle layout and configuration, so a CEF application's packaging script must supply its own component paths.

Choose entitlements

Enable the Hardened Runtime when signing executable processes. Start with the entitlement files from the matching Chromium revision and adapt them to your application's features:

Component Chromium reference Application guidance
Main application app-entitlements.plist Start with this file from the matching Chromium revision. Review capability changes against the features your application supports.
Renderer helper helper-renderer-entitlements.plist As of October 2026, Chromium enables com.apple.security.cs.allow-jit for V8.
GPU helper helper-gpu-entitlements.plist As of October 2026, Chromium also enables com.apple.security.cs.allow-jit here. Check the matching revision.
Generic, Alerts and Plugin helpers parts.py Review each role. The example below assigns an empty entitlement dictionary to these helpers; older releases or custom helper roles may need different settings.
Framework and dylibs parts.py Process entitlements belong on executables, rather than on their loaded libraries.

Use the unbranded app-entitlements.plist listed above. Chromium also has an app-entitlements-chrome.plist for Google Chrome, but its identity-specific entitlements are tied to Google's signing identity and should not be copied into a CEF application. Chromium's development signing mode adds com.apple.security.get-task-allow to permit debugging; omit that entitlement from release signatures. See the Chromium signing documentation for these distinctions.

For the example below, prepare app-entitlements.plist, renderer-entitlements.plist, gpu-entitlements.plist and helper-entitlements.plist beside your signing script. Copy Chromium's app-entitlements.plist from the matching revision for the main application. As of October 2026, it enables audio input, Bluetooth, camera, printing, USB, location and photo-library access. Review these capabilities against your application's supported features before changing them. Capability entitlements do not replace required Info.plist usage descriptions or user consent.

The renderer and GPU files can start with this dictionary:

xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>com.apple.security.cs.allow-jit</key>
  <true/>
</dict>
</plist>

For the generic helper's empty entitlement file, replace the dictionary with <dict/>. Keep the main application's separate app-entitlements.plist prepared above. If a custom subprocess executable serves multiple roles, its entitlements must cover the roles it actually executes. Entitlements on the outer application do not grant those capabilities to separate helper processes.

Chromium's renderer and GPU entitlement files provide a starting point, rather than a promise of compatibility with every CEF version. Avoid adding allow-unsigned-executable-memory, disable-executable-page-protection or disable-library-validation indiscriminately. Investigate a demonstrated requirement before granting an exception.

Sign the application bundle

Sign from the inside out: nested libraries and executables, their containing bundles, then the outer application. Explicitly sign Mach-O libraries in the CEF framework's Libraries directory. Avoid --deep for signing: it does not reliably discover code in nonstandard locations and cannot assign the correct entitlements to each process. See Apple's code signing guidance.

Signing an .app bundle also signs its main executable, identified by CFBundleExecutable in Contents/Info.plist and located in Contents/MacOS. You do not need a separate signing command for that binary. This applies to both the main application and each helper app. Additional executables inside a bundle must be signed explicitly before signing the enclosing bundle.

This Bash example handles the usual CEF sample layout. Replace MyApp and the signing identity; run it from the directory containing your entitlement files. The helper names must match the names and process roles produced by your build.

text
/* Detailed source-code truncated for AI context efficiency. */

The loop skips helpers absent from the selected CEF release. Confirm that every helper your build requires is present; the loop is not a bundle-completeness check. Extend the signing inventory for additional frameworks, crash handlers, launchers, native modules or other embedded code. Sign these before their containing bundle. For a standalone executable, use --options runtime to enable the Hardened Runtime and pass --entitlements if the process needs additional capabilities. For example, an executable that uses V8's JIT needs the com.apple.security.cs.allow-jit entitlement; a launcher that does not need additional capabilities can omit the entitlement file. Dylibs and native library modules do not need these process-specific options or entitlements. Traverse actual files once rather than signing both a framework symlink and its target. A framework with multiple versions needs each version signed separately.

Use --keychain /path/to/signing.keychain-db on signing commands if the identity resides in a dedicated keychain. Inspect the main application and each helper to confirm the identity, runtime flags and entitlements:

sh
codesign --display --verbose=4 "/path/to/MyApp.app"
codesign --display --entitlements :- "/path/to/MyApp.app/Contents/Frameworks/MyApp Helper (Renderer).app"

Notarize and staple

Use notarytool for notarization. Apple's notarization overview and custom workflow guide describe authentication, submission and ticket attachment.

Store credentials in a keychain profile. This command prompts for your Apple Account, team and app-specific password; an App Store Connect API key is another supported authentication method:

sh
xcrun notarytool store-credentials "myapp-notary"

Submit an archive of the signed app, and record the returned submission ID:

sh
ditto -c -k --sequesterRsrc --keepParent "/path/to/MyApp.app" "/path/to/MyApp-notarization.zip"
xcrun notarytool submit "/path/to/MyApp-notarization.zip" \
  --keychain-profile "myapp-notary" --wait --output-format json

Require status Accepted before proceeding. Retrieve the log if rejected or if you need to investigate warnings:

sh
xcrun notarytool log SUBMISSION_ID --keychain-profile "myapp-notary" notarization-log.json

Staple the ticket to the submitted app, validate it, and assess Gatekeeper acceptance:

sh
xcrun stapler staple "/path/to/MyApp.app"
xcrun stapler validate "/path/to/MyApp.app"
codesign --verify --deep --strict --verbose=2 "/path/to/MyApp.app"
spctl --assess --type execute --verbose=2 "/path/to/MyApp.app"

ZIP files cannot be stapled. For ZIP distribution, create a new archive from the stapled app and distribute that archive. Preserve symlinks and bundle metadata. A timeout while waiting does not necessarily mean rejection; query the existing submission with notarytool info SUBMISSION_ID before resubmitting.

Disk images and installer packages

For a DMG, put the signed and stapled app into the final image, then sign the image with your Developer ID Application identity:

sh
codesign --force --sign "Developer ID Application: Example Company (TEAMID)" \
  --timestamp "/path/to/MyApp.dmg"

Submit the DMG with notarytool submit, require acceptance, then run stapler staple and stapler validate on the DMG. Creating or signing a DMG does not replace signing the enclosed application.

A flat .pkg uses a separate Developer ID Installer identity. Build it from the signed app, then sign it with productsign:

sh
productsign --sign "Developer ID Installer: Example Company (TEAMID)" \
  "/path/to/MyApp-unsigned.pkg" "/path/to/MyApp.pkg"
pkgutil --check-signature "/path/to/MyApp.pkg"

Submit and staple the signed package using the same notarization workflow, then check it with spctl --assess --type install --verbose=2. Chromium's signing documentation explains the separate installer identity, and Apple's code signing guide covers framework and container signing.

Release validation and troubleshooting

Test the final downloaded artifact on a clean system, including installation, first launch, renderer startup, GPU rendering, enabled media features, normal exit and relaunch. Exercise every architecture you distribute. A valid signature does not by itself prove the application works.

On Windows, verify each embedded signature, the installer and any catalog members. Check that the bootstrap and adjacent chrome_elf.dll use the same certificate, and that shared-install catalog verification uses the expected distribution certificate. A successful signature check does not guarantee the absence of SmartScreen reputation warnings.

On macOS, verify the extracted or installed app as well as its shipping container. Test launch through Finder after a real browser download so quarantine and Gatekeeper behavior are exercised. Check a stapled app on a machine without network access when offline launch is required.

If macOS reports unsigned code or an invalid sealed resource, inspect the specific path in the verification output or notarization log. Check framework libraries and executable code in resource directories, finish all bundle changes before signing, and repeat signing from the affected inner component outward. For helper crashes, inspect that helper's own entitlements and the system crash report. See Apple's common notarization issues.

The Hardened Runtime's library validation can reject libraries signed by another team. Prefer signing your application and its bundled libraries with the same Team ID. For library-validation failures, check the loaded libraries' signatures and Team IDs before adding a disable-library-validation exception.

Automated signing should stop on verification failure. Retry temporary timestamp-service errors with a bounded delay, while preserving certificate, signature and notarization errors for investigation. Publish only after signature, catalog or notarization checks and application smoke tests succeed.


Contributing With Git (`docs/contributing_with_git.md`)

This page provides information about using Git to contribute code changes to CEF.

Contents


Overview

The CEF project uses the Git source code management system hosted via GitHub. The easiest way to contribute changes to CEF is by creating your own personal fork of the CEF Git repository and submitting pull requests with your proposed modifications. This document is intended as a quick-start guide for developers who already have some familiarity with Git. If you are completely new to Git you might want to review the series of Git tutorials provided by GitHub.

Initial Setup

Git can maintain your changes both locally and on a remote server. To work with Git efficiently the remote server locations must be configured properly.

1. Log into GitHub and create a public forked version of the cef.git repository using the Fork button at https://github.com/chromiumembedded/cef. This public linked fork will be used to create and submit pull requests.

2. Optionally, create a private unlinked fork using git commands directly. This private fork can be used for day-to-day development without exposing your work publicly.

3. Check out CEF/Chromium source code as described on the Master Build Quick Start or Branches And Building page. Note that if you use the automate-git.py tool you will want to back up your customized CEF checkout before changing branches or performing a clean build.

4. Change the remote origin of the CEF checkout so that it points to your private forked repository. This is the remote server location that the git push and git pull commands will operate on by default.

sh
cd /path/to/chromium/src/cef

# Replace <UserName> with your GitHub user name.
# If you don't have a private fork, point origin to your public fork instead.
git remote set-url origin https://<UserName>@github.com/<UserName>/cef_private.git

5. Add the remote public location pointing to your public forked repository. This is used to push branches for pull request submission.

sh
# Replace <UserName> with your GitHub user name.
git remote add public https://<UserName>@github.com/<UserName>/cef.git

6. Set the remote upstream of the CEF checkout so that you can merge changes from the main CEF repository.

sh
git remote add upstream https://github.com/chromiumembedded/cef.git

7. Verify that the remotes are configured correctly.

sh
git remote -v

You should see output like the following:

cpp
origin    https://<UserName>@github.com/<UserName>/cef_private.git (fetch)
origin    https://<UserName>@github.com/<UserName>/cef_private.git (push)
public    https://<UserName>@github.com/<UserName>/cef.git (fetch)
public    https://<UserName>@github.com/<UserName>/cef.git (push)
upstream    https://github.com/chromiumembedded/cef.git (fetch)
upstream    https://github.com/chromiumembedded/cef.git (push)

8. Configure your name and email address.

sh
git config user.name "User Name"
git config user.email [email protected]

9. Configure the correct handling of line endings in the repository.

sh
# Use this value on all platforms. Files will be unchanged in the working
# directory and converted to LF line endings in the object database.
git config core.autocrlf input

# Cause Git to abort actions on files with mixed line endings if the change is
# not reversible (e.g. changes to binary files that are accidentally classified
# as text).
git config core.safecrlf true

Working With Private Changes

You can now commit changes to your personal repository and merge upstream changes from the main CEF repository. To facilitate creation of a pull request or the sharing of your code changes with other developers you should make your changes in a branch. Use git push origin to push changes to your private fork for day-to-day development, and git push public when you're ready to submit a pull request.

Creating a Branch

Create a new personal branch for your changes.

sh
# Start with the branch that your changes will be based upon.
git checkout master

# Create a new personal branch for your changes.
# Replace <BranchName> with your new branch name.
git checkout -b <BranchName>

Creating a Commit

After making local modifications you can commit them to your personal branch.

sh
# For example, add a specified file by path.
git add path/to/file.txt

# For example, add all existing files that have been modified or deleted.
git add -u

# Commit the modifications locally.
git commit -m "A good description of the fix (issue #1234)"

# Push the modifications to your personal remote repository.
git push origin <BranchName>

Modifying a Commit

You can also modify an existing commit if you need to make additional changes.

sh
# For example, add all existing files that have been modified or deleted.
git add -u

# Update the current HEAD commit with the changes.
git commit --amend

# Push the modifications to your personal remote repository.
# Using the `--force` argument is not recommended if multiple people are sharing the
# same branch.
git push origin <BranchName> --force

Rebasing on Upstream Changes

The main CEF repository will receive additional commits over time. You will want to include these changes in your personal repository. To keep Git history correct (showing upstream CEF commits on the CEF branch instead of on your personal branch) you will need to rebase the local CEF branch before rebasing your local personal branch.

sh
# Fetch changes from the main CEF repository. This does not apply them to any
# particular branch.
git fetch upstream

# Check out the local CEF branch that tracks the upstream CEF branch.
# Replace "master" with a different branch name as appropriate (e.g. "2171", "2272", etc).
git checkout master

# Rebase your local CEF branch on top of the upstream CEF branch.
# After this command your local CEF branch should be identical to the upstream CEF branch.
git rebase upstream/master

# Check out the personal branch that you want to update with changes from the CEF branch.
# Replace <BranchName> with the name of your branch.
git checkout <BranchName>

# Rebase your personal branch on top of the local CEF branch.
# After this command your local commits will come after all CEF commits on the same branch.
git rebase master

# Push the modifications to your personal remote repository.
git push origin <BranchName>

You may get merge conflicts if your personal changes conflict with changes made to the main CEF respository. For instructions on resolving merge conflicts see this articicle.

For more information on using the rebase command go here.

Deleting a Branch

Once you no longer need a branch you can delete it both locally and remotely. Do not delete branches that are associated with open pull requests.

sh
# Delete the branch locally.
git branch -D <BranchName>

# Delete the branch remotely.
git push origin --delete <BranchName>

Cleaning a Checkout

You can remove all local changes from your checkout using the below commands.

sh
# Check the current state of the repository before deleting anything.
git status

# Remove all non-committed files and directories from the local checkout.
# If you run this command with JCEF it will also remove all third_party directories and you will
# need to re-run the `gclient runhooks` command.
git clean -dffx

# Remove all local commits from the current branch and reset branch state to match
# origin/master. Replace "origin/master" with a different remote branch name as appropriate.
git reset --hard origin/master

Working With Pull Requests

Once your personal changes are complete you can request that they be merged into the main CEF (or JCEF) repository. This is done using a pull request. Before submitting a pull request you should:

  • Rebase your changes on the upstream CEF (or JCEF) branch (see "Rebasing on Upstream Changes").
  • Fix any coding style issues (see "Coding Style").
  • Make sure your changes follow API versioning guidelines.
  • Find or create an appropriate issue in the CEF issue tracker (or JCEF issue tracker if the change targets that project). Make sure the issue number is referenced in your commit description.
  • Include new or modified unit tests as appropriate to the functionality.
  • Remove unnecessary or unrelated changes.

Coding Style

CEF uses the Chromium coding style. All C/C++, ObjC, Java and Python code must be formatted using the fix_style tool. For example, to fix the style of the unstaged files in your CEF Git checkout:

sh
cd /path/to/chromium/src/cef
./tools/fix_style.sh unstaged

The fix_style tool supports file paths and git hashes as input. Run tools/fix_style.[bat|sh] without arguments for complete usage information.

Changes to other types of files should match the existing style in that file.

Creating a Pull Request

Pull requests can only be created from a public repository that was forked using the GitHub interface. Push your branch to your public fork and then create the pull request as described here. Pull requests will only be accepted if they meet the requirements described above.

sh
# Push your branch to your public fork for PR submission.
git push public <BranchName>

Reviewing a Pull Request

Your pull request will be reviewed by one or more CEF developers. Please address any comments and update your pull request. The easiest way to update a pull request is by pushing new commits to the same branch -- those new commits will be automatically reflected in the pull request. Once your changes are deemed acceptable they will be squashed and merged into the main CEF repository.

The contents of a pull request can also be downloaded as a patch file and applied to your local Git checkout:

sh
# Download the patch file (replace {pull_no} with the PR number).
curl -L https://github.com/chromiumembedded/cef/pull/{pull_no}.diff -o name.patch

# Apply the patch file to your local Git checkout.
git apply name.patch

Crash Reporting (`docs/crash_reporting.md`)

This page explains how to handle crashes in CEF-based applications.

Contents


Overview

As a developer or vendor that cares about software quality you would like to know when and how your application is crashing in the wild. A crash reporting system is an important part of that feedback loop. Applications using CEF version 3.2883.1543 or newer have the option to save crash reports locally or upload them to a web server for later analysis. CEF generates minidump files that include exception state, callstacks, stack memory, and loaded modules. The crash report upload, if enabled, includes the minidump file and configurable crash metadata information such as product state, command-line flags and crash keys. Minidump files can be analyzed using existing systems such as Socorro or command-line tools (see the "Decoding Minidumps" section).

CEF provides a simple HTTP server script written in Python that can be used for testing crash report upload (see the Testing section below). See the Socorro documentation for details on how to build a robust crash reporting server for use with production applications.

The crash reporting capability in CEF/Chromium is implemented using Crashpad on Windows and macOS, and Breakpad on Linux.

Configuration

Crash reporting is configured using an INI-style config file named "crash_reporter.cfg". On Windows and Linux this file must be placed next to the main application executable. On macOS this file must be placed in the top-level app bundle Resources directory (e.g. ".app/Contents/Resources"). See comments in include/cef_crash_util.h for the most up-to-date version of this documentation.

The "crash_reporter.cfg" file contents are defined as follows:

sh
# Comments start with a hash character and must be on their own line.

[Config]
ProductName=<Value of the "prod" crash key; defaults to "cef">
ProductVersion=<Value of the "ver" crash key; defaults to the CEF version>
AppName=<Windows only; App-specific folder name component for storing crash
         information; default to "CEF">
ExternalHandler=<Windows only; Name of the external handler exe to use
                 instead of re-launching the main exe; default to empty>
BrowserCrashForwardingEnabled=<macOS only; True if browser process crashes
                              should be forwarded to the system crash
                              reporter; default to false>
ServerURL=<crash server URL; default to empty>
RateLimitEnabled=<True if uploads should be rate limited; default to true>
MaxUploadsPerDay=<Max uploads per 24 hours, used if rate limit is enabled;
                  default to 5>
MaxDatabaseSizeInMb=<Total crash report disk usage greater than this value
                     will cause older reports to be deleted; default to 20>
MaxDatabaseAgeInDays=<Crash reports older than this value will be deleted;
                      default to 5>

[CrashKeys]
my_key1=<small|medium|large>
my_key2=<small|medium|large>

[Config] section

Values in the "[Config]" section work as follows:

  • If "ProductName" and/or "ProductVersion" are set then the specified values will be included in the crash dump metadata. On macOS if these values are set to empty then they will be retrieved from the Info.plist file using the "CFBundleName" and "CFBundleShortVersionString" keys respectively.
  • If "AppName" is set on Windows then crash report information (metrics, database and dumps) will be stored locally on disk under the "C:\Users\[CurrentUser]\AppData\Local\[AppName]\User Data" folder. On other platforms the CefSettings.user_data_path value will be used.
  • If "ExternalHandler" is set on Windows then the specified exe will be launched as the crashpad-handler instead of re-launching the main process exe. The value can be an absolute path or a path relative to the main exe directory. On Linux the CefSettings.browser_subprocess_path value will be used. On macOS the existing subprocess app bundle will be used.
  • If "BrowserCrashForwardingEnabled" is set to true on macOS then browser process crashes will be forwarded to the system crash reporter. This results in the crash UI dialog being displayed to the user and crash reports being logged under "~/Library/Logs/DiagnosticReports". Forwarding of crash reports from non-browser processes and Debug builds is always disabled.
  • If "ServerURL" is set then crashes will be uploaded as a multi-part POST request to the specified URL. Otherwise, reports will only be stored locally on disk.
  • If "RateLimitEnabled" is set to true then crash report uploads will be rate limited as follows:
    • If "MaxUploadsPerDay" is set to a positive value then at most the specified number of crashes will be uploaded in each 24 hour period.
    • If crash upload fails due to a network or server error then an incremental backoff delay up to a maximum of 24 hours will be applied for retries.
    • If a backoff delay is applied and "MaxUploadsPerDay" is > 1 then the "MaxUploadsPerDay" value will be reduced to 1 until the client is restarted. This helps to avoid an upload flood when the network or server error is resolved.
    • Rate limiting is not supported on Linux.
  • If "MaxDatabaseSizeInMb" is set to a positive value then crash report storage on disk will be limited to that size in megabytes. For example, on Windows each dump is about 600KB so a "MaxDatabaseSizeInMb" value of 20 equates to about 34 crash reports stored on disk. Not supported on Linux.
  • If "MaxDatabaseAgeInDays" is set to a positive value then crash reports older than the specified age in days will be deleted. Not supported on Linux.

[CrashKeys] section

A maximum of 26 crash keys of each size can be specified for use by the application. Crash key values will be truncated based on the specified size (small = 64 bytes, medium = 256 bytes, large = 1024 bytes). The value of crash keys can be set from any thread or process using the CefSetCrashKeyValue function. These key/value pairs will be sent to the crash server along with the crash dump file.

Testing

CEF provides a tools/crash_server.py script that implements a simple HTTP server for receiving crash report uploads from a Breakpad/Crashpad client (any application using CEF version 3.2883.1543 or newer). This script is intended for testing purposes only. An HTTPS server and a system such as Socorro should be used when uploading crash reports from production applications. See comments in the crash_server.py script for the most up-to-date version of this documentation.

Usage of the crash_server.py script is as follows:

1. Run the script from the command-line. The first argument is the server port number and the second argument is the directory where uploaded report information will be saved:

sh
python crash_server.py 8080 /path/to/dumps

2. Create a "crash_reporter.cfg" file at the required platform-specific location. On Windows and Linux this file must be placed next to the main application executable. On macOS this file must be placed in the top-level app bundle Resources directory (e.g. ".app/Contents/Resources"). At a minimum it must contain a "ServerURL=http://localhost:8080" line under the "[Config]" section (make sure the port number matches the value specified in step 1). See the Configuration section above for a complete specification of this file's contents.

Example file contents:

text
[Config]
# Product information.
ProductName=cefclient
ProductVersion=1.0.0

# Required to enable crash dump upload.
ServerURL=http://localhost:8080

# Disable rate limiting so that all crashes are uploaded.
RateLimitEnabled=false
MaxUploadsPerDay=0

[CrashKeys]
# The cefclient sample application sets these values (see step 5 below).
testkey_small1=small
testkey_small2=small
testkey_medium1=medium
testkey_medium2=medium
testkey_large1=large
testkey_large2=large

3. Load one of the following URLs in the CEF-based application to cause a crash:

  • Main (browser) process crash: chrome://inducebrowsercrashforrealz
  • Renderer process crash: chrome://crash
  • GPU process crash: chrome://gpucrash

4. When the script successfully receives a crash report upload you will see console output like the following:

text
01/10/2017 12:31:23: Dump <id>

The "" value is a 16 digit hexadecimal string that uniquely identifies the dump. Crash minidump contents and metadata (product state, command-line flags and crash keys) will be written to the ".dmp" and ".json" files respectively underneath the directory specified in step 1.

On Linux, Breakpad uses the wget utility to upload crash dumps, so make sure that utility is installed. If the crash is handled correctly then you should see console output like the following when the client uploads a crash dump:

text
--2017-01-10 12:31:22--  http://localhost:8080/
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:8080... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [text/html]
Saving to: '/dev/fd/3'
Crash dump id: <id>

On macOS, when uploading a crash report to the script over HTTP you may receive an error like the following:

text
Transport security has blocked a cleartext HTTP (http://) resource load
since it is insecure. Temporary exceptions can be configured via your app's
Info.plist file.

You can work around this error by adding the "NSAppTransportSecurity" key to the Info.plist file in your application's Helper.app bundle (e.g. "tests/cefclient/resources/mac/helper-Info.plist" before building cefclient, or ".app/Contents/Frameworks/ Helper. app/Contents/Info.plist" in the resulting app bundle):

html
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  
  <key>NSAppTransportSecurity</key>
  <dict>
    
    <key>NSAllowsArbitraryLoads</key>
    <true/>
  </dict>
</dict>
</plist>

5. The cefclient sample application sets test crash key values in the browser and renderer processes. To work properly these values must also be defined in the "[CrashKeys]" section of "crash_reporter.cfg" as shown above.

In tests/cefclient/browser/client_browser.cc (browser process):

text
CefSetCrashKeyValue("testkey_small1", "value1_small_browser");
CefSetCrashKeyValue("testkey_small2", "value2_small_browser");
CefSetCrashKeyValue("testkey_medium1", "value1_medium_browser");
CefSetCrashKeyValue("testkey_medium2", "value2_medium_browser");
CefSetCrashKeyValue("testkey_large1", "value1_large_browser");
CefSetCrashKeyValue("testkey_large2", "value2_large_browser");

In tests/cefclient/renderer/client_renderer.cc (renderer process):

text
CefSetCrashKeyValue("testkey_small1", "value1_small_renderer");
CefSetCrashKeyValue("testkey_small2", "value2_small_renderer");
CefSetCrashKeyValue("testkey_medium1", "value1_medium_renderer");
CefSetCrashKeyValue("testkey_medium2", "value2_medium_renderer");
CefSetCrashKeyValue("testkey_large1", "value1_large_renderer");
CefSetCrashKeyValue("testkey_large2", "value2_large_renderer");

When crashing the browser or renderer processes with cefclient you should verify that the test crash key values are included in the metadata (".json") file. Some values may be chunked as described in the Configuration section above.

Example metadata for a browser process crash:

json
{
  "LOG_FATAL": "debug_urls.cc:208: Check failed: false. \n0   Chromium Embedded Framework         0x00000001161a1a0e _ZN4base5debug10StackTraceC2Ev + 30\n1   Chromium Embedded Framework         0x00000001161a1a75 _ZN4base5debug10StackTraceC1Ev + 21\n2   Chromium Embedded Fr",
  "guid": "6ada20da-7306-477e-a715-ada36448a23f",
  "num-switches": "3",
  "pid": "2073",
  "platform": "macos",
  "product": "cefclient",
  "ptype": "browser",
  "switch-1": "--url=chrome:\/\/inducebrowsercrashforrealz",
  "switch-2": "--lang=en-US",
  "testkey_large1": "value1_large_browser", 
  "testkey_large2": "value2_large_browser", 
  "testkey_medium1": "value1_medium_browser", 
  "testkey_medium2": "value2_medium_browser", 
  "testkey_small1": "value1_small_browser", 
  "testkey_small2": "value2_small_browser", 
  "version": "1.0.0",
}

Example metadata for a renderer process crash:

json
{
  "discardable-memory-allocated": "4194304",
  "discardable-memory-free": "3198976",
  "guid": "6ada20da-7306-477e-a715-ada36448a23f",
  "num-switches": "15",
  "origin_mismatch_origin": "https:\/\/www.google.com",
  "origin_mismatch_redirects": "2",
  "origin_mismatch_same_page": "0"
  "origin_mismatch_transition": "1",
  "origin_mismatch_url-1": "https:\/\/www.google.com\/?gws_rd=ssl",
  "pid": "2706",
  "platform": "macos",
  "product": "cefclient",
  "ptype": "renderer",
  "rvinit_main_frame_id": "2",
  "rvinit_proxy_id": "-2",
  "rvinit_view_id": "1",
  "switch-1": "--disable-databases",
  "switch-2": "--lang=en-US",
  "switch-3": "--lang=en-US",
  "switch-4": "--enable-pinch",
  "switch-5": "--num-raster-threads=4",
  "switch-6": "--enable-gpu-rasterization",
  "switch-7": "--enable-zero-copy",
  "switch-8": "--enable-gpu-memory-buffer-compositor-resources",
  "switch-9": "--enable-main-frame-before-activation",
  "switch-10": "--renderer-client-id=3",
  "testkey_large1": "value1_large_renderer", 
  "testkey_large2": "value2_large_renderer", 
  "testkey_medium1": "value1_medium_renderer", 
  "testkey_medium2": "value2_medium_renderer", 
  "testkey_small1": "value1_small_renderer", 
  "testkey_small2": "value2_small_renderer", 
  "v8-ignition": "N",
  "version": "1.0.0",
}

Example metadata for a GPU process crash:

json
{
  "guid": "6ada20da-7306-477e-a715-ada36448a23f",
  "num-switches": "17",
  "pid": "1664",
  "platform": "macos",
  "product": "cefclient",
  "ptype": "gpu-process",
  "switch-1": "--lang=en-US",
  "switch-2": "--supports-dual-gpus=true",
  "switch-3": "--gpu-driver-bug-workarounds=0,1,10,23,25,35,38,45,51,59,61,62,63,64,66,70,71,73,81,82,83,86,88,89",
  "switch-4": "--gpu-vendor-id=0x1002",
  "switch-5": "--gpu-device-id=0x6821",
  "switch-6": "--gpu-driver-vendor",
  "switch-7": "--gpu-driver-version",
  "switch-8": "--gpu-driver-date",
  "switch-9": "--gpu-secondary-vendor-ids=0x8086",
  "switch-10": "--gpu-secondary-device-ids=0x0d26"
  "switch-11": "--gpu-active-vendor-id=0x8086",
  "switch-12": "--gpu-active-device-id=0x0d26",
  "switch-13": "--lang=en-US",
  "version": "1.0.0",
}

Decoding Minidumps

The crash report upload, if enabled, includes the minidump file with exception state, callstacks, stack memory, and loaded modules. The minidump file can be decoded using the dump_syms and minidump_stackwalk tools from Breakpad as described here.

The Breakpad tools can be built from a local Chromium checkout on Windows, MacOS and Linux as follows:

1. Download the Chromium code (for example, by using CEF tooling).

2. Write the following contents to the out/Release/args.gn file:

text
is_component_build=false
is_debug=false
target_cpu="x64"
is_official_build=true

3. Generate the config and build:

sh
gn gen out/Release
ninja -C out/Release dump_syms minidump_stackwalk

4. Windows requires the msdia140.dll COM DLL. It must be placed next to dump_syms.exe or registered with the system:

text
regsvr32 /s msdia140.dll

Visual Studio 2022 includes a 64-bit version of this DLL at "C:\Program Files\Microsoft Visual Studio\2022\Professional\DIA SDK\bin\amd64". The 64-bit version 14.29.30133.0 or newer is recommended to avoid failures when loading large (> 4GB) PDB files (details).


General Usage (`docs/general_usage.md`)

This page provides an overview of CEF3 and general usage information.

Contents


Introduction

The Chromium Embedded Framework (CEF) is an open source project based on the Google Chromium project. Unlike the Chromium project itself, which focuses mainly on Google Chrome application development, CEF focuses on facilitating embedded browser use cases in third-party applications. CEF insulates the user from the underlying Chromium and Blink code complexity by offering production-quality stable APIs, release branches tracking specific Chromium releases, and binary distributions. Most features in CEF have default implementations that provide rich functionality while requiring little or no integration work from the user. As of this article's publication there are over 100 million installed instances of CEF around the world embedded in products from a wide range of companies and industries. A partial list of companies and products using CEF is available on the CEF Wikipedia page. Some use cases for CEF include:

  • Embedding an HTML5-compliant Web browser control in an existing native application.
  • Creating a light-weight native "shell" application that hosts a user interface developed primarily using Web technologies.
  • Rendering Web content "off-screen" in applications that have their own custom drawing frameworks.
  • Acting as a host for automated testing of existing Web properties and applications.

CEF3 is the next generation of CEF based on the multi-process Chromium Content API. Advantages to CEF3's multi-process architecture include:

  • Improved performance and stability (JavaScript and plugins run in a separate process).
  • Support for Retina displays.
  • GPU acceleration for WebGL and 3D CSS.
  • Cool new features like WebRTC (webcam support) and speech input.
  • Better automated UI testing via the DevTools remote debugging protocol and ChromeDriver2.
  • Faster access to current and future Web features and standards.

This document introduces the general concepts involved when developing an application using CEF3.

Getting Started

Using a Binary Distribution

Binary distributions of CEF3 are available from the project Downloads page. They contain all files necessary to build a particular version of CEF3 on a particular platform (Windows, MacOS or Linux). See the Tutorial page for detailed instructions on how to create a simple application using the CEF3 binary distribution.

No matter the platform all binary distributions share the same general structure:

  • CMakeLists.txt provides the CMake configuration for building the test applications included with the binary distribution. Platform-specific build instructions are provided as comments at the top of this file.
  • Debug contains a debug build the CEF shared library (libcef) and any other libraries required to run on the platform.
  • include contains all required CEF header files.
  • libcef_dll contains the source code for the libcef_dll_wrapper static library that all applications using the CEF C++ API must link against. See the "C++ Wrapper" section for more information.
  • Release contains a release build the CEF shared library (libcef) and any other libraries required to run on the platform.
  • Resources contains resources required by applications using CEF (Windows and Linux only). This includes .pak files (binary files with globbed resources) and potentially other files such depending on the platform.
  • tests/cefclient contains the cefclient sample application configured to build using the files in the binary distribution. This application demonstrates a wide range of CEF functionalities.
  • tests/cefsimple contains the cefsimple sample application configured to build using the files in the binary distribution. This application demonstrates the minimal functionality required to create a browser window.
  • tests/ceftests contains the ceftests sample application configured to build using the files in the binary distribution. This application provides unit test coverage for CEF APIs and functionality.

Each binary distribution also contains a README.txt file that describes the platform-specific distribution in greater detail and a LICENSE.txt file that contains CEF's BSD license. When distributing an application based on CEF you should include the license text somewhere in your application's distribution. For example, you can list it on an "About" or "Credits" page in your application's UI, or in the documentation bundled with your application. License and credit information is also available inside of a CEF3 browser window by loading "about:license" and "about:credits" respectively.

Applications based on CEF binary distributions can be built using standard platform build tools. This includes Visual Studio on Windows, Xcode on MacOS and gcc/make on Linux. The project Downloads page contains information about the OS and build tool versions required for specific binary releases. When building on Linux also pay careful attention to the listed package dependencies.

Building from Source Code

CEF can be built from source code either locally or using automated build systems like TeamCity. This requires the download of Chromium and CEF source code via Git. The Chromium code base is quite large and building Chromium from source code is only recommended on moderately powerful machines with more than 8GB of RAM. Detailed instructions for building Chromium and CEF from source code are available on the Branches And Building page.

Sample Application

The cefclient sample application is a complete working example of CEF integration and is included in source code form with each binary distribution. The easiest way to create a new application using CEF is to start with the cefclient application and remove the parts that you don't need. Many of the examples in this document originate from the cefclient application.

Important Concepts

There are some important underlying concepts to developing CEF3-based applications that should be understood before proceeding.

C++ Wrapper

The libcef shared library exports a C API that isolates the user from the CEF runtime and code base. The libcef_dll_wrapper project, which is distributed in source code form as part of the binary release, wraps this exported C API in a C++ API that is then linked into the client application. The code for this C/C++ API translation layer is automatically generated by the translator tool. Direct usage of the C API is described on the Using The CAPI page.

Processes

CEF3 runs using multiple processes. The main process which handles window creation, UI and network access is called the "browser" process. This is generally the same process as the host application and the majority of the application logic will run in the browser process. Blink rendering and JavaScript execution occur in a separate "render" process. Some application logic, such as JavaScript bindings and DOM access, will also run in the render process. The default process model will spawn a new render process for each unique origin (scheme + domain). Other processes will be spawned as needed, such as the "gpu" process to handle accelerated compositing.

By default the main application executable will be spawned multiple times to represent separate processes. This is handled via command-line flags that are passed into the CefExecuteProcess function. If the main application executable is large, takes a long time to load, or is otherwise unsuitable for non-browser processes the host can use a separate executable for those other processes. This can be configured via the CefSettings.browser_subprocess_path variable. See the "Application Structure" section for more information.

The separate processes spawned by CEF3 communicate using Inter-Process Communication (IPC). Application logic implemented in the browser and render processes can communicate by sending asynchronous messages back and forth. Java Script Integration in the render process can expose asynchronous APIs that are handled in the browser process. See the "Inter-Process Communication" section for more information.

Platform-specific debugging tips are also available for Windows, MacOS and Linux.

Threads

Each process in CEF3 runs multiple threads. For a complete list of threads see the cef_thread_id_t enumeration. These are some of the commonly-referenced threads:

  • TID_UI thread is the main thread in the browser process. This thread will be the same as the main application thread if CefInitialize() is called with a CefSettings.multi_threaded_message_loop value of false.
  • TID_IO thread is used in the browser process to handle IPC and network messages.
  • TID_FILE_* threads are used in the browser process to interact with the file system. Blocking operations should only be performed on this thread or a CefThread created by the client application.
  • TID_RENDERER thread is the main thread in the renderer process. All Blink and V8 interation must take place on this thread.

Due to the multi-threaded nature of CEF it's important to use message passing or locking to protect data members from access on multiple threads. The CefPostTask family of functions support easy asynchronous message passing between threads. See the "Posting Tasks" section for more information.

The current thread can be verified using the CefCurrentlyOn() function. The CEF sample applications use the following defines to verify that methods are executed on the expected thread. These defines are included in the include/wrapper/cef_helpers.h header file.

cpp
#define CEF_REQUIRE_UI_THREAD() DCHECK(CefCurrentlyOn(TID_UI));
#define CEF_REQUIRE_IO_THREAD() DCHECK(CefCurrentlyOn(TID_IO));
#define CEF_REQUIRE_FILE_BACKGROUND_THREAD() \
 DCHECK(CefCurrentlyOn(TID_FILE_BACKGROUND));
#define CEF_REQUIRE_FILE_USER_VISIBLE_THREAD() \
 DCHECK(CefCurrentlyOn(TID_FILE_USER_VISIBLE));
#define CEF_REQUIRE_FILE_USER_BLOCKING_THREAD() \
 DCHECK(CefCurrentlyOn(TID_FILE_USER_BLOCKING));
#define CEF_REQUIRE_RENDERER_THREAD() DCHECK(CefCurrentlyOn(TID_RENDERER));

To support synchronized access to blocks of code CEF provides base::Lock and base::AutoLock types via the include/base/cef_lock.h header file. For example:

cpp
// Include the necessary header.
#include "include/base/cef_lock.h"

// Class declaration.
class MyClass : public CefBaseRefCounted {
 public:
  MyClass() : value_(0) {}
  // Method that may be called on multiple threads.
  void IncrementValue();
 private:
  // Value that may be accessed on multiple theads.
  int value_;
  // Lock used to protect access to |value_|.
  base::Lock lock_;
  IMPLEMENT_REFCOUNTING(MyClass);
};

// Class implementation.
void MyClass::IncrementValue() {
  // Acquire the lock for the scope of this method.
  base::AutoLock lock_scope(lock_);
  // |value_| can now be modified safely.
  value_++;
}

Reference Counting

All framework classes implement the CefBase[RefCounted|Scoped] interface and all instance pointers are handled using the CefRefPtr smart pointer implementation that automatically handles reference counting via calls to AddRef() and Release(). The easiest way to implement these classes is as follows:

cpp
class MyClass : public CefBaseRefCounted {
 public:
  // Various class methods here...

 private:
  // Various class members here...

  IMPLEMENT_REFCOUNTING(MyClass);  // Provides atomic refcounting implementation.
};

// References a MyClass instance
CefRefPtr<MyClass> my_class = new MyClass();

Strings

CEF defines its own data structure for representing strings. This is for a few different reasons:

  • The libcef library and the host application may use different runtimes for managing heap memory. All objects, including strings, need to be freed using the same runtime that allocated the memory.
  • The libcef library can be compiled to support different underlying string types (UTF8, UTF16 or wide). The default is UTF16 but it can be changed by modifying the defines in cef_string.h and recompiling CEF. When choosing the wide string type keep in mind that the size will vary depending on the platform.

For UTF16 the string structure looks like this:

cpp
typedef struct _cef_string_utf16_t {
  char16_t* str;  // Pointer to the string
  size_t length;  // String length
  void (*dtor)(char16_t* str);  // Destructor for freeing the string on the correct heap
} cef_string_utf16_t;

The selected string type is then typedef'd to the generic type:

cpp
typedef char16_t cef_char_t;
typedef cef_string_utf16_t cef_string_t;

CEF provides a number of C API functions for operating on the CEF string types (mapped via #defines to the type-specific functions). For example:

  • cef_string_set will assign a string value to the structure with or without copying the value.
  • cef_string_clear will clear the string value.
  • cef_string_cmp will compare two string values.

CEF also provides functions for converting between all supported string types (ASCII, UTF8, UTF16 and wide). See the cef_string.h and cef_string_types.h headers for the complete list of functions.

Usage of CEF strings in C++ is simplified by the CefString class. CefString provides automatic conversion to and from std::string (UTF8) and std::wstring (wide) types. It can also be used to wrap an existing cef_string_t structure for assignment purposes.

Assignment to and from std::string:

cpp
std::string str = "Some UTF8 string";

// Equivalent ways of assigning |str| to |cef_str|. Conversion from UTF8 will occur if necessary.
CefString cef_str(str);
cef_str = str;
cef_str.FromString(str);

// Equivalent ways of assigning |cef_str| to |str|. Conversion to UTF8 will occur if necessary.
str = cef_str;
str = cef_str.ToString();

Assignment to and from std::wstring:

cpp
std::wstring str = "Some wide string";

// Equivalent ways of assigning |str| to |cef_str|. Conversion from wide will occur if necessary.
CefString cef_str(str);
cef_str = str;
cef_str.FromWString(str);

// Equivalent ways of assigning |cef_str| to |str|. Conversion to wide will occur if necessary.
str = cef_str;
str = cef_str.ToWString();

Use the FromASCII() method if you know that the format is ASCII:

cpp
const char* cstr = "Some ASCII string";
CefString cef_str;
cef_str.FromASCII(cstr);

Some structures like CefSettings have cef_string_t members. CefString can be used to simplify the assignment to those members:

cpp
CefSettings settings;
const char* path = "/path/to/log.txt";

// Equivalent assignments.
CefString(&settings.log_file).FromASCII(path);
cef_string_from_ascii(path, strlen(path), &settings.log_file);

Command Line Arguments

Many features in CEF3 and Chromium can be configured using command line arguments. These arguments take the form "--some-argument[=optional-param]" and are passed into CEF via CefExecuteProcess() and the CefMainArgs structure (see the "Application Structure" section below).

  • To disable processing of arguments from the command line set CefSettings.command_line_args_disabled to true before passing the CefSettings structure into CefInitialize().
  • To specify CEF/Chromium command line arguments inside the host application implement the CefApp::OnBeforeCommandLineProcessing() method.
  • To pass application-specific (non-CEF/Chromium) command line arguments to sub-processes implement the CefBrowserProcessHandler::OnBeforeChildProcessLaunch() method.

See comments in shared/common/client_switches.cc for more information on how to discover supported CEF/Chromium command line switches.

Coordinate Systems

Browser applications use different coordinate systems depending on the context. Screen/window coordinates are generally represented as density independent pixel (DIP) coordinates with upper-left origin. These DIP coordinates will be passed to Views APIs and browser callbacks such as OnAutoResize and OnContentsBoundsChange. JavaScript and CSS mostly use CSS pixels which are similar to DIP coordinates but also reflect browser zoom settings (details). Applications using other platform APIs/toolkits may also need to convert to/from specific device coordinate systems.

Important details related to coordinate system usage in CEF:

  • DIP coordinate and CSS pixel factors will be the same when browser zoom is set to 100%.
  • Views-hosted browsers are aware of the containing CefWindow and browser callbacks such as OnContentsBoundsChange will report frame bounds for that window. Same for browsers with a native parent NSWindow on macOS.
  • Windowless (off-screen) browsers can be made aware of containing screen/window bounds by implementing the appropriate CefRenderHandler methods (GetScreenInfo, GetRootScreenRect, GetViewRect).
  • Other browsers (native hosted or windowless) will report bounds for the browser content only.
    • The client application is responsible for converting bounds and enlarging to include native controls and/or window frame (see below).
  • DIP conversion requirements differ by platform API/toolkit.
    • DIP coordinates convert to device coordinates based on the device scale factor.
      • By default the device scale factor will match the associated display (e.g. user/system-level display settings).
      • The device scale factor can be changed to a non-default value globally by using the --force-device-scale-factor=[float] command-line flag.
      • On macOS a non-default device scale factor impacts rendering quality but does not impact the DIP conversion itself.
      • Windowed browsers should convert DIP coordinates using CefDisplay methods.
      • Windowless browsers should convert DIP coordinates using the device_scale_factor that was last configured via GetScreenInfo.
    • Views API methods use DIP coordinates directly.
    • Windows API methods (SetWindowPos, AdjustWindowRectEx, etc.) expect device (pixel) coordinates.
      • Use the static CefDisplay conversion methods on Windows for best (multi-dpi-aware) results.
      • The DIP conversion factor may be different from the DPI awareness settings used with native controls. WinAPI functions like GetDpiForWindow should be used to convert native control coordinates.
    • Linux platform toolkits (GTK, etc) usually expect root window (pixel) coordinates.
    • MacOS (Cocoa) API methods use DIP coordinates as device coordinates but with a different (lower-left) origin.
  • JavaScript can convert CSS pixels to device coordinates using window.devicePixelRatio.

Coordinate system behavior can be explored further in the cefclient sample app by running with the --url=https://tests/window command-line flag.

Application Layout

Application layout can differ significantly depending on the platform. For example, on MacOS your application layout must follow a specific app bundle structure. Windows and Linux are more flexible, allowing you to customize the location where CEF libraries and resources are stored. For a complete working example of the required layout you can download a "Sample Application" from the project Downloads page. Some distributed files are required and some are optional. Requirements and additional information for each file can be found in the binary distribution README.txt file.

Windows

On Windows the default layout places the libcef library and related resources next to the application executable. The directory structure looks like this for 4692 branch:

text
Application/
    cefclient.exe  <= cefclient application executable
    libcef.dll <= main CEF library
    icudtl.dat <= unicode support data
    chrome_100_percent.pak, chrome_200_percent.pak, resources.pak <= non-localized resources and strings
    snapshot_blob.bin, v8_context_snapshot.bin <= V8 initial snapshot
    locales/
        en-US.pak, ... <= locale-specific resources and strings

The location of the CEF libraries and resource files can be customized using the CefSettings structure (see the README.txt file or "CefSettings" section for details). The cefclient application on Windows compiles in resources via the BINARY resource type in cefclient/resources/win/cefclient.rc but an application could just as easily load resources from the local file system.

Linux

On Linux the default layout places the libcef library and related resources next to the application executable. Note however that there's a discrepancy between where libcef.so is located in the client distribution and where it's located in the binary distribution that you build yourself. The location depends on how the linker rpath value is set when building the application executable. For example, a value of "-Wl,-rpath,." ("." meaning the current directory) will allow you to place libcef.so next to the application executable. The path to libcef.so can also be specified using the LD_LIBRARY_PATH environment variable. The directory structure looks like this for 4692 branch:

text
Application/
    cefclient  <= cefclient application executable
    chrome-sandbox <= sandbox support binary
    libcef.so <= main CEF library
    icudtl.dat <= unicode support data
    chrome_100_percent.pak, chrome_200_percent.pak, resources.pak <= non-localized resources and strings
    snapshot_blob.bin, v8_context_snapshot.bin <= V8 initial snapshot
    locales/
        en-US.pak, ... <= locale-specific resources and strings
    files/
        binding.html, ... <= cefclient application resources

The location of the CEF libraries and resource files can be customized using the CefSettings structure (see the README.txt file of "CefSettings" section for details).

MacOS

The application (app bundle) layout on MacOS is mandated by the Chromium implementation and consequently is not very flexible. The directory structure looks like this for 4692 branch:

text
cefclient.app/
    Contents/
        Frameworks/
            Chromium Embedded Framework.framework/
                Chromium Embedded Framework <= main application library
                Resources/
                    chrome_100_percent.pak, chrome_200_percent.pak, resources.pak, ... <= non-localized resources and strings
                    icudtl.dat <= unicode support data
                    snapshot_blob.bin, v8_context_snapshot.bin <= V8 initial snapshot
                    en.lproj/, ... <= locale-specific resources and strings
            cefclient Helper.app/
                Contents/
                    Info.plist
                    MacOS/
                        cefclient Helper <= helper executable
                    Pkginfo
        Info.plist
        MacOS/
            cefclient <= cefclient application executable
        Pkginfo
        Resources/
            binding.html, ... <= cefclient application resources

The "Chromium Embedded Framework.framework" is an unversioned framework that contains all CEF binaries and resources. Executables (cefclient, cefclient Helper, etc) dynamically load the CEF Framework as described here.

The "cefclient Helper" app is used for executing separate processes (renderer, plugin, etc) with different characteristics. It needs to have a separate app bundle and Info.plist file so that, among other things, it doesn't show dock icons.

Application Structure

Every CEF3 application has the same general structure.

  • Provide an entry-point function that initializes CEF and runs either sub-process executable logic or the CEF message loop.
  • Provide an implementation of CefApp to handle process-specific callbacks.
  • Provide an implementation of CefClient to handle browser-instance-specific callbacks.
  • Call CefBrowserHost::CreateBrowser() to create a browser instance and manage the browser life span using CefLifeSpanHandler.

Entry-Point Function

As described in the "Processes" section a CEF3 application will run multiple processes. The processes can all use the same executable or a separate executable can be specified for the sub-processes. Execution of the process begins in the entry-point function. Complete platform-specific examples for Windows, Linux and MacOS are available in cefclient/cefclient_win.cc, cefclient/cefclient_gtk.cc and cefclient/cefclient_mac.mm respectively.

When launching sub-processes CEF will specify configuration information using the command-line that must be passed into the CefExecuteProcess function via the CefMainArgs structure. The definition of CefMainArgs is platform-specific. On Linux and MacOS it accepts the argc and argv values which are passed into the main() function.

text
CefMainArgs main_args(argc, argv);

On Windows it accepts the instance handle (HINSTANCE) which is passed into the wWinMain() function. The instance handle is also retrievable via GetModuleHandle(nullptr).

text
CefMainArgs main_args(hInstance);

Single Executable

When running as a single executable the entry-point function is required to differentiate between the different process types. The single executable structure is supported on Windows and Linux but not on MacOS.

cpp
// Program entry-point function.
int main(int argc, char* argv[]) {
  // Structure for passing command-line arguments.
  // The definition of this structure is platform-specific.
  CefMainArgs main_args(argc, argv);

  // Implementation of the CefApp interface.
  CefRefPtr<MyApp> app(new MyApp);

  // Execute the sub-process logic, if any. This will either return immediately for the browser
  // process or block until the sub-process should exit.
  int exit_code = CefExecuteProcess(main_args, app.get());
  if (exit_code >= 0) {
    // The sub-process terminated, exit now.
    return exit_code;
  }

  // Populate this structure to customize CEF behavior.
  CefSettings settings;

  // Initialize CEF in the main process.
  CefInitialize(main_args, settings, app.get());

  // Run the CEF message loop. This will block until CefQuitMessageLoop() is called.
  CefRunMessageLoop();

  // Shut down CEF.
  CefShutdown();

  return 0;
}

Separate Sub-Process Executable

When using a separate sub-process executable you need two separate executable projects and two separate entry-point functions.

Main application entry-point function:

cpp
// Program entry-point function.
int main(int argc, char* argv[]) {
  // Load the CEF framework library at runtime instead of linking directly
  // as required by the macOS sandbox implementation.
  CefScopedLibraryLoader library_loader;
  if (!library_loader.LoadInMain())
    return 1;

  // Structure for passing command-line arguments.
  // The definition of this structure is platform-specific.
  CefMainArgs main_args(argc, argv);

  // Implementation of the CefApp interface.
  CefRefPtr<MyApp> app(new MyApp);

  // Populate this structure to customize CEF behavior.
  CefSettings settings;

  // Specify the path for the sub-process executable.
  CefString(&settings.browser_subprocess_path).FromASCII("/path/to/subprocess");

  // Initialize CEF in the main process.
  CefInitialize(main_args, settings, app.get());

  // Run the CEF message loop. This will block until CefQuitMessageLoop() is called.
  CefRunMessageLoop();

  // Shut down CEF.
  CefShutdown();

  return 0;
}

Sub-process application entry-point function:

cpp
// Program entry-point function.
int main(int argc, char* argv[]) {
  // Initialize the macOS sandbox for this helper process.
  CefScopedSandboxContext sandbox_context;
  if (!sandbox_context.Initialize(argc, argv))
    return 1;

  // Load the CEF framework library at runtime instead of linking directly
  // as required by the macOS sandbox implementation.
  CefScopedLibraryLoader library_loader;
  if (!library_loader.LoadInHelper())
    return 1;

  // Structure for passing command-line arguments.
  // The definition of this structure is platform-specific.
  CefMainArgs main_args(argc, argv);

  // Implementation of the CefApp interface.
  CefRefPtr<MyApp> app(new MyApp);

  // Execute the sub-process logic. This will block until the sub-process should exit.
  return CefExecuteProcess(main_args, app.get());
}

Message Loop Integration

CEF can also integrate with an existing application message loop instead of running its own message loop. There are two ways to do this.

  1. Call CefDoMessageLoopWork() on a regular basis instead of calling CefRunMessageLoop(). Each call performs a single iteration of the CEF message loop. Call it on the main application thread, with CefSettings.multi_threaded_message_loop = false, while continuing to process the host application's native events. Calling too infrequently will starve CEF work and negatively impact browser performance; calling too frequently will increase CPU usage.
  2. Set CefSettings.multi_threaded_message_loop = true (Windows and Linux only). This will cause CEF to run the browser UI thread on a separate thread from the main application thread. With this approach neither CefDoMessageLoopWork() nor CefRunMessageLoop() need to be called. CefInitialize() and CefShutdown() should still be called on the main application thread. You will need to provide your own mechanism for communicating with the main application thread (see for example the message window usage in cefclient_win.cpp). You can test this mode in cefclient on Windows or Linux by running with the "--multi-threaded-message-loop" command-line flag.

When using CefDoMessageLoopWork(), enabling CefSettings.external_message_pump lets CefBrowserProcessHandler::OnScheduleMessagePumpWork request earlier processing. These callbacks supplement periodic pumping: they do not provide a complete sequence of requests for processing all CEF work. The application must continue scheduling CefDoMessageLoopWork() calls even when no callback is received. This requirement applies on all platforms.

The callback may run on any thread. Marshal each scheduling request to the main application thread. For a delay of zero or less, schedule processing reasonably soon. For a positive delay, replace the pending scheduled call with one after the requested delay or the application's maximum pumping interval, whichever is shorter. After processing, ensure that another call remains scheduled within that interval, even if CEF has requested no further work. Avoid recursive calls to CefDoMessageLoopWork(); if a request is handled while a call is active, defer the additional work until that call returns.

The shared external-pump example uses one-shot timers that are rearmed as needed, with a maximum delay of approximately 33 milliseconds (1000 / 30). This interval is the example's scheduling policy, not a guaranteed rendering frame rate. You can test this mode in cefclient with the "--external-message-pump" command-line flag. The example is a simple integration and does not handle all cases. If a nested native message loop is entered during a CefDoMessageLoopWork() call, the example's reentrancy guard defers further CEF processing until that call returns. Applications that require CEF work to continue during such loops need additional integration. See the example's header and Chromium's platform-specific base/message_loop/message_pump_* implementations for additional guidance.

CefSettings

The CefSettings structure allows configuration of application-wide CEF settings. Some commonly configured members include:

  • browser_subprocess_path The path to a separate executable that will be launched for sub-processes. See the "Separate Sub-Process Executable" section for more information.
  • multi_threaded_message_loop Set to true to have the browser process message loop run in a separate thread. See the "Message Loop Integration" section for more information.
  • command_line_args_disabled Set to true to disable configuration of browser process features using standard CEF and Chromium command-line arguments. See the "Command Line Arguments" section for more information.
  • cache_path The location where cache data will be stored on disk. If empty an in-memory cache will be used for some features and a temporary disk cache will be used for others. HTML5 databases such as localStorage will only persist across sessions if a cache path is specified.
  • locale The locale string that will be passed to Blink. If empty the default locale of "en-US" will be used. This value is ignored on Linux where locale is determined using environment variable parsing with the precedence order: LANGUAGE, LC_ALL, LC_MESSAGES and LANG. Also configurable using the "lang" command-line switch.
  • log_file The directory and file name to use for the debug log. If empty, the default name of "debug.log" will be used and the file will be written to the application directory. Also configurable using the "log-file" command-line switch.
  • log_severity The log severity. Only messages of this severity level or higher will be logged. Also configurable using the "log-severity" command-line switch with a value of "verbose", "info", "warning", "error", "error-report" or "disable".
  • resources_dir_path The fully qualified path for the resources directory. If this value is empty the cef.pak and/or devtools_resources.pak files must be located in the module directory on Windows/Linux or the app bundle Resources directory on MacOS. Also configurable using the "resources-dir-path" command-line switch.
  • locales_dir_path The fully qualified path for the locales directory. If this value is empty the locales directory must be located in the module directory. This value is ignored on MacOS where pack files are always loaded from the app bundle Resources directory. Also configurable using the "locales-dir-path" command-line switch.
  • remote_debugging_port Set to a value between 1024 and 65535 to enable remote debugging on the specified port. For example, if 8080 is specified the remote debugging URL will be http://localhost:8080. CEF can be remotely debugged from any CEF or Chrome browser window. Also configurable using the "remote-debugging-port" command-line switch.

CefBrowser and CefFrame

The CefBrowser and CefFrame objects are used for sending commands to the browser and for retrieving state information in callback methods. Each CefBrowser object will have a single main CefFrame object representing the top-level frame and zero or more CefFrame objects representing sub-frames. For example, a browser that loads two iframes will have three CefFrame objects (the top-level frame and the two iframes).

To load a URL in the browser main frame:

text
browser->GetMainFrame()->LoadURL(some_url);

To navigate the browser backwards:

text
browser->GoBack();

To retrieve the main frame HTML contents:

cpp
// Implementation of the CefStringVisitor interface.
class Visitor : public CefStringVisitor {
 public:
  Visitor() {}

  // Called asynchronously when the HTML contents are available.
  virtual void Visit(const CefString& string) override {
    // Do something with |string|...
  }

  IMPLEMENT_REFCOUNTING(Visitor);
};

browser->GetMainFrame()->GetSource(new Visitor());

CefBrowser and CefFrame objects exist in both the browser process and the render process. Host behavior can be controlled in the browser process via the CefBrowser::GetHost() method. For example, the native handle for a windowed browser can be retrieved as follows:

cpp
// CefWindowHandle is defined as HWND on Windows, NSView* on MacOS
// and (usually) X11 Window on Linux.
CefWindowHandle window_handle = browser->GetHost()->GetWindowHandle();

Other methods are available for history navigation, loading of strings and requests, sending edit commands, retrieving text/html contents, and more. See the class documentation for the complete list of supported methods.

CefApp

The CefApp interface provides access to process-specific callbacks. Important callbacks include:

  • OnBeforeCommandLineProcessing which provides the opportunity to programmatically set command-line arguments. See the "Command Line Arguments" section for more information.
  • OnRegisterCustomSchemes which provides an opportunity to register custom schemes. See the ""Request Handling" section for more information.
  • GetBrowserProcessHandler which returns the handler for functionality specific to the browser process including the OnContextInitialized() method.
  • GetRenderProcessHandler which returns the handler for functionality specific to the render process. This includes JavaScript-related callbacks and process messages. See the Java Script Integration page and the "Inter-Process Communication" section for more information.

An example CefApp implementation can be seen in cefsimple/simple_app.h and cefsimple/simple_app.cc.

CefClient

The CefClient interface provides access to browser-instance-specific callbacks. A single CefClient instance can be shared among any number of browsers. Important callbacks include:

  • Handlers for things like browser life span, context menus, dialogs, display notifications, drag events, focus events, keyboard events and more. The majority of handlers are optional. See the class documentation for the side effects, if any, of not implementing a specific handler.
  • OnProcessMessageReceived which is called when an IPC message is received from the render process. See the "Inter-Process Communication" section for more information.

An example CefClient implementation can be seen in cefsimple/simple_handler.h and cefsimple/simple_handler.cc.

Browser Life Span

Browser life span begins with a call to CefBrowserHost::CreateBrowser() or CefBrowserHost::CreateBrowserSync(). Convenient places to execute this logic include the CefBrowserProcessHandler::OnContextInitialized() callback or platform-specific message handlers like WM_CREATE on Windows.

cpp
// Information about the window that will be created including parenting, size, etc.
// The definition of this structure is platform-specific.
CefWindowInfo info;
// On Windows for example...
info.SetAsChild(parent_hwnd, client_rect);

// Customize this structure to control browser behavior.
CefBrowserSettings settings;

// CefClient implementation.
CefRefPtr<MyClient> client(new MyClient);

// Create the browser asynchronously. Initially loads the Google URL.
CefBrowserHost::CreateBrowser(info, client.get(), "http://www.google.com", settings, nullptr, nullptr);

The CefLifeSpanHandler class provides the callbacks necessary for managing browser life span. Below is an extract of the relevant methods and members.

cpp
class MyClient : public CefClient,
                 public CefLifeSpanHandler,
                 ... {
  // CefClient methods.
  virtual CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override {
    return this;
  }

  // CefLifeSpanHandler methods.
  void OnAfterCreated(CefRefPtr<CefBrowser> browser) override;
  bool DoClose(CefRefPtr<CefBrowser> browser) override;
  void OnBeforeClose(CefRefPtr<CefBrowser> browser) override;

  // Member accessors.
  CefRefPtr<CefBrowser> GetBrower() const { return browser_; }
  bool IsClosing() const { return is_closing_; }

 private:
  CefRefPtr<CefBrowser> browser_;
  int browser_id_ = -1;  // invalid value
  int browser_count_ = 0;
  bool is_closing_ = false;

  IMPLEMENT_REFCOUNTING(MyClient);
};

The OnAfterCreated() method will be called immediately after the browser object is created. The host application can use this method to keep a reference to the main browser object.

cpp
void MyClient::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
  // Must be executed on the UI thread.
  REQUIRE_UI_THREAD();
 
  if (!browser_) {
    // Keep a reference to the main browser.
    browser_ = browser;
    browser_id_ = browser->GetIdentifier();
  }

  // Keep track of how many browsers currently exist.
  browser_count_++;
}

To destroy the browser call CefBrowserHost::CloseBrowser().

cpp
// Notify the browser window that we would like to close it. This will result in a call to 
// MyHandler::DoClose() if the JavaScript 'onbeforeunload' event handler allows it.
browser->GetHost()->CloseBrowser(false);

If the browser is parented to another window then the close event may originate in the OS function for that parent window (for example, by clicking the X on the parent window). The parent window then needs to call CloseBrowser(false) and wait for a second OS close event to indicate that the browser has allowed the close. The second OS close event will not be sent if the close is canceled by a JavaScript 'onbeforeunload' event handler or by the DoClose() callback. Notice the IsClosing() check in the below examples -- it will return false for the first OS close event and true for the second (after DoClose is called).

Handling in the parent window WndProc on Windows:

cpp
case WM_CLOSE:
  if (g_handler && !g_handler->IsClosing()) {
    CefRefPtr<CefBrowser> browser = g_handler->GetBrowser();
    if (browser) {
      // Notify the browser window that we would like to close it. This will result in a call to 
      // MyHandler::DoClose() if the JavaScript 'onbeforeunload' event handler allows it.
      browser->GetHost()->CloseBrowser(false);

      // Cancel the close.
      return 0;
    }
  }

  // Allow the close.
  break;

case WM_DESTROY:
  // Quitting CEF is handled in MyHandler::OnBeforeClose().
  return 0;
}

Handling the "delete_event" signal on Linux:

cpp
gboolean delete_event(GtkWidget* widget, GdkEvent* event,
                      GtkWindow* window) {
  if (g_handler && !g_handler->IsClosing()) {
    CefRefPtr<CefBrowser> browser = g_handler->GetBrowser();
    if (browser) {
      // Notify the browser window that we would like to close it. This will result in a call to 
      // MyHandler::DoClose() if the JavaScript 'onbeforeunload' event handler allows it.
      browser->GetHost()->CloseBrowser(false);

      // Cancel the close.
      return TRUE;
    }
  }

  // Allow the close.
  return FALSE;
}

Shutdown on OS X is more complicated. See comments in cefsimple/cefsimple_mac.mm for a complete understanding of how shutdown works on that platform.

The DoClose() method sets the is_closing_ flag and returns false to send the second OS close event.

cpp
bool MyClient::DoClose(CefRefPtr<CefBrowser> browser) {
  // Must be executed on the UI thread.
  REQUIRE_UI_THREAD();

  // Closing the main window requires special handling. See the DoClose()
  // documentation in the CEF header for a detailed description of this
  // process.
  if (browser_id_ == browser->GetIdentifier()) {
    // Set a flag to indicate that the window close should be allowed.
    is_closing_ = true;
  }

  // Allow the close. For windowed browsers this will result in the OS close
  // event being sent.
  return false;
}

When the OS function receives the second OS close event it allows the parent window to actually close. This then results in a call to OnBeforeClose(). Make sure to release any references to the browser object in the OnBeforeClose() callback.

cpp
void MyHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
  // Must be executed on the UI thread.
  REQUIRE_UI_THREAD();

  if (browser_id_ == browser->GetIdentifier()) {
    // Free the browser pointer so that the browser can be destroyed.
    browser_ = nullptr;
  }

  if (--browser_count_ == 0) {
    // All browser windows have closed. Quit the application message loop.
    CefQuitMessageLoop();
  }
}

See the cefclient application for complete working examples on each platform.

Off-Screen Rendering

With off-screen rendering CEF does not create a native browser window. Instead, CEF provides the host application with invalidated regions and a pixel buffer and the host application notifies CEF of input events such as mouse, keyboard and focus. Off-screen rendering does not currently support accelerated compositing so performance may suffer as compared to a windowed browser. Off-screen browsers will receive the same notifications as windowed browsers including the life span notifications described in the previous section. To use off-screen rendering:

  1. Implement the CefRenderHandler interface. All methods are required unless otherwise indicated.
  2. Call CefWindowInfo::SetAsWindowless() before passing the CefWindowInfo structure to CefBrowserHost::CreateBrowser(). If no parent window is passed to SetAsWindowless some functionality like context menus may not be available.
  3. The CefRenderHandler::GetViewRect() method will be called to retrieve the desired view rectangle.
  4. The CefRenderHandler::OnPaint() method will be called to provide invalid regions and the updated pixel buffer. The cefclient application draws the buffer using OpenGL but your application can use whatever technique you prefer.
  5. To resize the browser call CefBrowserHost::WasResized(). This will result in a call to GetViewRect() to retrieve the new size followed by a call to OnPaint().
  6. Call the CefBrowserHost::SendXXX() methods to notify the browser of mouse, keyboard and focus events.
  7. Call CefBrowserHost::CloseBrowser() to destroy browser.

Run cefclient with the "--off-screen-rendering-enabled" command-line flag for a working example.

Posting Tasks

Tasks can be posted between the various threads in a single process using the CefPostTask family of methods (see the include/cef_task.h header file for the complete list). The task will execute asynchronously on the message loop of the target thread.

CEF provides base::Bind[Once|Repeating] and base::[Once|Repeating]Callback templated callback classes for passing bound methods, objects and arguments to CefPostTask. For complete base::Bind and base::Callback usage information see the Chromium documentation. The include/wrapper/cef_closure_task.h header provides helpers for converting a base::[Once|Repeating]Closure to a CefTask. For example:

cpp
// Include the necessary headers.
#include "include/base/cef_callback.h"
#include "include/wrapper/cef_closure_task.h"

// To execute a bound function:

// Define a function.
void MyFunc(int arg) { /* do something with |arg| on the UI thread */ }

// Post a task that will execute MyFunc on the UI thread and pass an |arg|
// value of 5.
CefPostTask(TID_UI, base::BindOnce(&MyFunc, 5));

// To execute a bound method:

// Define a class.
class MyClass : public CefBaseRefCounted {
 public:
  MyClass() {}
  void MyMethod(int arg) { /* do something with |arg| on the UI thread */ }
 private:
  IMPLEMENT_REFCOUNTING(MyClass);
};

// Create an instance of MyClass.
CefRefPtr<MyClass> instance = new MyClass();

// Post a task that will execute MyClass::MyMethod on the UI thread and pass
// an |arg| value of 5. |instance| will be kept alive until after the task
// completes.
CefPostTask(TID_UI, base::BindOnce(&MyClass::MyMethod, instance, 5));

If the host application needs to keep a reference to a run loop it can use the CefTaskRunner class. For example, to get the task runner for the UI thread:

cpp
CefRefPtr<CefTaskRunner> task_runner = CefTaskRunner::GetForThread(TID_UI);

Inter-Process Communication (IPC)

Since CEF3 runs in multiple processes it is necessary to provide mechanisms for communicating between those processes. CefBrowser and CefFrame objects exist in both the browser and render processes which helps to facilitate this process. Each CefBrowser and CefFrame object also has a unique ID value associated with it that will match on both sides of the process boundary.

Process Startup Messages

Startup data can be associated with a specific CefBrowser instance at creation time via the CefRefPtr<CefDictionaryValue> extra_info parameter to CefBrowserHost::CreateBrowser. That extra_info data will be delivered to every renderer process associated with that CefBrowser via the CefRenderProcessHandler::OnBrowserCreated callback. See the "Processes" section for more information about when and how a new renderer process will be spawned.

Process Runtime Messages

Messages can be passed between processes at runtime using the CefProcessMessage class. These messages are associated with a specific CefBrowser and CefFrame instance and sent using the CefFrame::SendProcessMessage() method. The process message should contain whatever state information is required via CefProcessMessage::GetArgumentList().

cpp
// Create the message object.
CefRefPtr<CefProcessMessage> msg= CefProcessMessage::Create("my_message");

// Retrieve the argument list object.
CefRefPtr<CefListValue> args = msg>GetArgumentList();

// Populate the argument values.
args->SetString(0, "my string");
args->SetInt(0, 10);

// Send the process message to the main frame in the render process.
// Use PID_BROWSER instead when sending a message to the browser process.
browser->GetMainFrame()->SendProcessMessage(PID_RENDERER, msg);

A message sent from the browser process to the render process will arrive in CefRenderProcessHandler::OnProcessMessageReceived(). A message sent from the render process to the browser process will arrive in CefClient::OnProcessMessageReceived().

cpp
bool MyHandler::OnProcessMessageReceived(
    CefRefPtr<CefBrowser> browser,
    CefRefPtr<CefFrame> frame,
    CefProcessId source_process,
    CefRefPtr<CefProcessMessage> message) {
  // Check the message name.
  const std::string& message_name = message->GetName();
  if (message_name == "my_message") {
    // Handle the message here...
    return true;
  }
  return false;
}

Asynchronous JavaScript Bindings

Java Script Integration is implemented in the render process but frequently need to communicate with the browser process. The JavaScript APIs themselves should be designed to work asynchronously using closures and promises.

Generic Message Router

CEF provides a generic implementation for routing asynchronous messages between JavaScript running in the renderer process and C++ running in the browser process. An application interacts with the router by passing it data from standard CEF C++ callbacks (OnBeforeBrowse, OnProcessMessageRecieved, OnContextCreated, etc). The renderer-side router supports generic JavaScript callback registration and execution while the browser-side router supports application-specific logic via one or more application-provided Handler instances. See the message_router example for a stand-alone example application that demonstates CefMessageRouter usage. See include/wrapper/cef_message_router.h for complete usage documentation.

Custom Implementation

A CEF-based application can also provide its own custom implementation of asynchronous JavaScript bindings. A simplistic implementation could work as follows:

1. The JavaScript binding in the render process is passed a callback function.

cpp
// In JavaScript register the callback function.
app.setMessageCallback('binding_test', function(name, args) {
  document.getElementById('result').value = "Response: "+args[0];
});

2. The render process keeps a reference to the callback function.

cpp
// Map of message callbacks.
typedef std::map<std::pair<std::string, int>,
                 std::pair<CefRefPtr<CefV8Context>, CefRefPtr<CefV8Value> > >
                 CallbackMap;
CallbackMap callback_map_;

// In the CefV8Handler::Execute implementation for "setMessageCallback".
if (arguments.size() == 2 && arguments[0]->IsString() &&
    arguments[1]->IsFunction()) {
  std::string message_name = arguments[0]->GetStringValue();
  CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
  int browser_id = context->GetBrowser()->GetIdentifier();
  callback_map_.insert(
      std::make_pair(std::make_pair(message_name, browser_id),
                     std::make_pair(context, arguments[1])));
}

3. The render process sends an asynchronous IPC message to the browser process requesting that work be performed.

4. The browser process receives the IPC message and performs the work.

5. Upon completion of the work the browser process sends an asynchronous IPC message back to the render process with the result.

6. The render process receives the IPC message and executes the callback function with the result.

cpp
// Execute the registered JavaScript callback if any.
if (!callback_map_.empty()) {
  const CefString& message_name = message->GetName();
  CallbackMap::const_iterator it = callback_map_.find(
      std::make_pair(message_name.ToString(),
                     browser->GetIdentifier()));
  if (it != callback_map_.end()) {
    // Keep a local reference to the objects. The callback may remove itself
    // from the callback map.
    CefRefPtr<CefV8Context> context = it->second.first;
    CefRefPtr<CefV8Value> callback = it->second.second;

    // Enter the context.
    context->Enter();

    CefV8ValueList arguments;

    // First argument is the message name.
    arguments.push_back(CefV8Value::CreateString(message_name));

    // Second argument is the list of message arguments.
    CefRefPtr<CefListValue> list = message->GetArgumentList();
    CefRefPtr<CefV8Value> args = CefV8Value::CreateArray(list->GetSize());
    SetList(list, args);  // Helper function to convert CefListValue to CefV8Value.
    arguments.push_back(args);

    // Execute the callback.
    CefRefPtr<CefV8Value> retval = callback->ExecuteFunction(nullptr, arguments);
    if (retval.get()) {
      if (retval->IsBool())
        handled = retval->GetBoolValue();
    }

    // Exit the context.
    context->Exit();
  }
}

7. Release any V8 references associated with the context in CefRenderProcessHandler::OnContextReleased().

cpp
void MyHandler::OnContextReleased(CefRefPtr<CefBrowser> browser,
                                  CefRefPtr<CefFrame> frame,
                                  CefRefPtr<CefV8Context> context) {
  // Remove any JavaScript callbacks registered for the context that has been released.
  for (auto it = callback_map_.begin(); it != callback_map_.end();) {
    if (it->second.first->IsSame(context)) {
      it = callback_map_.erase(it);
    } else {
      ++it;
    }
  }
}

Synchronous Requests

In rare cases it may be necessary to implement synchronous communication between the browser and render processes. This should be avoided whenever possible because it will negatively impact performance in the render process. However, if you must have synchronous communication consider using synchronous XMLHttpRequests which will block the render process while awaiting handling in the browser process network layer. The browser process can handle the requests using a custom scheme handler or network interception. See the "Network Layer" section for more information.

Network Layer

By default network requests in CEF3 will be handled in a manner transparent to the host application. For applications wishing for a closer relationship with the network layer CEF3 exposes a range of network-related functionalities.

Network-related callbacks can occur on different threads so make sure to pay attention to the documentation and properly protect your data members (see the "Threads" section for background).

Custom Requests

The simplest way to load a URL in a browser frame is via the CefFrame::LoadURL() method.

text
browser->GetMainFrame()->LoadURL(some_url);

Applications wishing to send more complex requests containing custom request headers or upload data can use the CefFrame::LoadRequest() method. This method accepts a CefRequest object as the single argument.

WARNING: The LoadRequest method will fail with "bad IPC message" reason INVALID_INITIATOR_ORIGIN (213) unless you first navigate to the request origin (scheme + domain) using some other mechanism (LoadURL, link click, etc).

cpp
// Create a CefRequest object.
CefRefPtr<CefRequest> request = CefRequest::Create();

// Set the request URL.
request->SetURL(some_url);

// Set the request method. Supported methods include GET, POST, HEAD, DELETE and PUT.
request->SetMethod("POST");

// Optionally specify custom headers.
CefRequest::HeaderMap headerMap;
headerMap.insert(
    std::make_pair("X-My-Header", "My Header Value"));
request->SetHeaderMap(headerMap);

// Optionally specify upload content.
// The default "Content-Type" header value is "application/x-www-form-urlencoded".
// Set "Content-Type" via the HeaderMap if a different value is desired.
const std::string& upload_data = "arg1=val1&arg2=val2";
CefRefPtr<CefPostData> postData = CefPostData::Create();
CefRefPtr<CefPostDataElement> element = CefPostDataElement::Create();
element->SetToBytes(upload_data.size(), upload_data.c_str());
postData->AddElement(element);
request->SetPostData(postData);

Browser-Independent Requests

Applications can send network requests via the CefURLRequest class. Implement the CefURLRequestClient interface to handle the resulting response.

  • Requests not associated with a particular CefBrowser/CefFrame can be sent via the CefURLRequest::Create method. These types of requests can only be used in the browser process.
  • Requests associated with a particular CefBrowser/CefFrame can be sent via the CefFrame::CreateURLRequest method. These types of requests can be used in both the browser and the renderer process.

See documentation on the above methods for additional usage restrictions.

Here is an example of CefURLRequest usage:

text
/* Detailed source-code truncated for AI context efficiency. */

To send the request:

cpp
// Set up the CefRequest object.
CefRefPtr<CefRequest> request = CefRequest::Create();
// Populate |request| as shown above...

// Create the client instance.
CefRefPtr<MyRequestClient> client = new MyRequestClient();

// Start the request. MyRequestClient callbacks will be executed asynchronously.
CefRefPtr<CefURLRequest> url_request = CefURLRequest::Create(request, client.get(), nullptr);
// To cancel the request: url_request->Cancel();

Requests made with CefURLRequest can also specify custom behaviors via the CefRequest::SetFlags() method. Supported bit flags include:

  • UR_FLAG_SKIP_CACHE If set the cache will be skipped when handling the request.
  • UR_FLAG_ALLOW_STORED_CREDENTIALS If set cookies may be sent with the request and saved from the response.
  • UR_FLAG_REPORT_UPLOAD_PROGRESS If set upload progress events will be generated when a request has a body.
  • UR_FLAG_NO_DOWNLOAD_DATA If set the CefURLRequestClient::OnDownloadData method will not be called.
  • UR_FLAG_NO_RETRY_ON_5XX If set 5XX redirect errors will be propagated to the observer instead of automatically re-tried. This currently only applies for requests originated in the browser process.

See cef_urlrequest_flags_t for the complete list of supported flags.

For example, to skip the cache and not report download data:

text
request->SetFlags(UR_FLAG_SKIP_CACHE | UR_FLAG_NO_DOWNLOAD_DATA);

Request Handling

CEF3 supports two approaches for handling network requests inside of an application. The scheme handler approach allows registration of a handler for requests targeting a particular origin (scheme + domain). The request interception approach allows handling of arbitrary requests at the application's discretion.

WARNING: Use the HTTP scheme instead of a custom scheme to avoid a range of potential issues.

If you choose to use a custom scheme (anything other than "HTTP", "HTTPS", etc) you must register it with CEF so that it will behave as expected. If you would like your custom scheme to behave similar to HTTP (support POST requests and enforce HTTP access control (CORS) restrictions) then it should be registered as a "standard" scheme. If you are planning to perform cross-origin requests to other schemes or send POST requests via XMLHttpRequest to your scheme handler then you should use the HTTP scheme instead of a custom scheme to avoid potential issues. If you wish to use custom schemes the attributes are registered via the CefApp::OnRegisterCustomSchemes() callback which must be implemented in all processes.

cpp
void MyApp::OnRegisterCustomSchemes(CefRefPtr<CefSchemeRegistrar> registrar) {
  // Register "client" as a standard scheme.
  int options = CEF_SCHEME_OPTION_STANDARD;
  registrar->AddCustomScheme("client", options);
}

See cef_scheme_options_t for the complete list of supported flags.

Generic Resource Manager

CEF provides a generic implementation for managing resource requests from one or more data sources. This user registers handlers for different data sources, such as directories on disk, zip archives or custom implementations, and the manager handles the requests. An application interacts with the router by passing it data from standard CEF C++ callbacks (OnBeforeResourceLoad, GetResourceHandler). See the resource_manager example for a stand-alone example application that demonstates CefResourceManager usage. See include/wrapper/cef_resource_manager.h for complete usage documentation.

Scheme Handler

A scheme handler is registered via the CefRegisterSchemeHandlerFactory() function. A good place to call this function is from CefBrowserProcessHandler::OnContextInitialized(). For example, you can register a handler for "client://myapp/" requests:

text
CefRegisterSchemeHandlerFactory("client", "myapp", new MySchemeHandlerFactory());

Handlers can be used with both built-in schemes (HTTP, HTTPS, etc) and custom schemes. When using a built-in scheme choose a domain name unique to your application (like "myapp" or "internal"). Implement the CefSchemeHandlerFactory and CefResourceHandler classes to handle the request and provide response data. If using custom schemes don't forget to implement the CefApp::OnRegisterCustomSchemes method as described above. See the scheme_handler example for a stand-alone example application that demonstates CefSchemeHandlerFactory usage. See include/cef_scheme.h for complete usage documentation.

If the response data is known at request time the CefStreamResourceHandler class provides a convenient default implementation of CefResourceHandler.

cpp
// CefStreamResourceHandler is part of the libcef_dll_wrapper project.
#include "include/wrapper/cef_stream_resource_handler.h"

const std::string& html_content = "<html><body>Hello!</body></html>";

// Create a stream reader for |html_content|.
CefRefPtr<CefStreamReader> stream =
    CefStreamReader::CreateForData(
        static_cast<void*>(const_cast<char*>(html_content.c_str())),
        html_content.size());

// Constructor for HTTP status code 200 and no custom response headers.
// There's also a version of the constructor for custom status code and response headers.
return new CefStreamResourceHandler("text/html", stream);

Request Interception

The CefRequestHandler::GetResourceHandler() method supports the interception of arbitrary requests. It uses the same CefResourceHandler class as the scheme handler approach. If using custom schemes don't forget to implement the CefApp::OnRegisterCustomSchemes method as described above.

cpp
CefRefPtr<CefResourceHandler> MyHandler::GetResourceHandler(
      CefRefPtr<CefBrowser> browser,
      CefRefPtr<CefFrame> frame,
      CefRefPtr<CefRequest> request) {
  // Evaluate |request| to determine proper handling...
  if (...)
    return new MyResourceHandler();

  // Return nullptr for default handling of the request.
  return nullptr;
}

Response Filtering

The CefRequestHandler::GetResourceResponseFilter() method supports filtering of request response data. See cefclient/browser/response_filter_test.cc (accessible via the Tests menu > Other Tests > Response Filtering from inside the cefclient sample application) for a working example.

Other Callbacks

The CefRequestHandler interface provides callbacks for various network-related events incuding authentication, cookie handling, external protocol handling, certificate errors and so on.

Proxy Resolution

Proxy settings are configured in CEF3 using the same command-line flags as Google Chrome.

text
--proxy-server=host:port
      Specify the HTTP/SOCKS4/SOCKS5 proxy server to use for requests. An individual proxy
      server is specified using the format:

        [<proxy-scheme>://]<proxy-host>[:<proxy-port>]

      Where <proxy-scheme> is the protocol of the proxy server, and is one of:

        "http", "socks", "socks4", "socks5".

      If the <proxy-scheme> is omitted, it defaults to "http". Also note that "socks" is equivalent to
      "socks5".

      Examples:

        --proxy-server="foopy:99"
            Use the HTTP proxy "foopy:99" to load all URLs.

        --proxy-server="socks://foobar:1080"
            Use the SOCKS v5 proxy "foobar:1080" to load all URLs.

        --proxy-server="sock4://foobar:1080"
            Use the SOCKS v4 proxy "foobar:1080" to load all URLs.

        --proxy-server="socks5://foobar:66"
            Use the SOCKS v5 proxy "foobar:66" to load all URLs.

      It is also possible to specify a separate proxy server for different URL types, by prefixing
      the proxy server specifier with a URL specifier:

      Example:

        --proxy-server="https=proxy1:80;http=socks4://baz:1080"
            Load https://* URLs using the HTTP proxy "proxy1:80". And load http://*
            URLs using the SOCKS v4 proxy "baz:1080".

--no-proxy-server
      Disables the proxy server.

--proxy-auto-detect
      Autodetect  proxy  configuration.

--proxy-pac-url=URL
      Specify proxy autoconfiguration URL.

If the proxy requires authentication the CefRequestHandler::GetAuthCredentials() callback will be executed with an isProxy value of true to retrieve the username and password.

cpp
bool MyHandler::GetAuthCredentials(
    CefRefPtr<CefBrowser> browser,
    CefRefPtr<CefFrame> frame,
    bool isProxy,
    const CefString& host,
    int port,
    const CefString& realm,
    const CefString& scheme,
    CefRefPtr<CefAuthCallback> callback) {
  if (isProxy) {
    // Provide credentials for the proxy server connection.
    callback->Continue("myuser", "mypass");
    return true;
  }
  return false;
}

Web content loading during application startup can be delayed due to network proxy resolution (for example, if "Automatically detect proxy settings" is checked on Windows). For best user experience consider designing your application to first show a static splash page and then redirect to the actual website using meta refresh -- the redirect will be blocked until proxy resolution completes. For testing purposes proxy resolution can be disabled using the "--no-proxy-server" command-line flag. Proxy resolution delays can also be duplicated in Google Chrome by running "chrome --url=..." from the command line.


Hands On Tutorial (`docs/hands_on_tutorial.md`)

This page provides a hands-on tutorial for building a CEF application by progressively modifying the cefsimple example.

Contents


Introduction

This tutorial teaches CEF development by modifying the cefsimple example step-by-step. You'll start with a working application and progressively add features, learning CEF concepts along the way.

Who is this for?

  • Developers new to CEF who want hands-on learning
  • C++ developers with basic knowledge (classes, pointers, inheritance)
  • Those who prefer learning by doing rather than reading reference documentation
  • Anyone who wants to understand CEF architecture through practice

Why start from cefsimple?

  • ✅ CMake already configured correctly
  • ✅ Resources (.pak files, frameworks) already in the right place
  • ✅ macOS app bundle structure already set up
  • ✅ Platform-specific entry points already working
  • ✅ You can verify your build environment works before making changes

Prerequisites:

  • CEF binary distribution for your platform (download here)
  • C++ compiler:
    • Windows: Visual Studio 2022
    • macOS: Xcode with command-line tools
    • Linux: GCC/Clang, build-essential, and libgtk-3-dev packages
  • CMake 3.19+

What you'll build:

Starting from cefsimple, you'll progressively add features to create a custom browser with JavaScript integration and popup window customization.

Using the C API? This tutorial uses the C++ API. For pure C implementation, see cefsimple_capi and Using The CAPI.

CEF Quick Concepts

Before diving into code, here are the key CEF concepts you'll encounter:

Multi-process architecture: CEF runs multiple processes for security and stability:

  • Browser process - Main process, manages UI and coordinates other processes
  • Render processes - Isolated processes for web content (one per site)
  • GPU process - Hardware-accelerated graphics
  • Other processes - Network service, audio service, etc.

Reference counting: CEF objects use reference counting for memory management:

  • CefRefPtr<T> - Smart pointer that automatically manages reference counts
  • IMPLEMENT_REFCOUNTING macro - Adds thread-safe reference counting to your classes
  • Objects are deleted when their reference count reaches zero

Threading: CEF callbacks run on specific threads:

  • UI thread - Browser process main thread (most callbacks run here)
  • IO thread - Network operations
  • Renderer thread - JavaScript execution (in render process)
  • Use CEF_REQUIRE_UI_THREAD(), CEF_REQUIRE_RENDERER_THREAD(), etc. to assert correct thread usage

Browser styles:

  • Chrome style - Full Chrome UI with tabs, address bar, etc. (default in unmodified cefsimple)
  • Alloy style - Lightweight mode, you control all UI (we switch to this in Step 1)
  • Unmodified cefsimple supports both styles: use --use-alloy-style command-line flag for Alloy
  • Set via GetBrowserRuntimeStyle() and GetWindowRuntimeStyle()

Views framework: Cross-platform UI system:

  • CefWindow - Top-level window
  • CefBrowserView - Embeds the browser in a view
  • Delegates (CefWindowDelegate, CefBrowserViewDelegate) - Control window/browser behavior
  • Alternative: Platform-native UI or off-screen rendering (OSR)

For deeper coverage of CEF architecture and concepts, see Tutorial.md.

Step 0: Setup and Verify

Download and Build cefsimple

  1. Download CEF binary distribution:

    Visit https://cef-builds.spotifycdn.com/index.html and download the appropriate binary distribution for your platform (e.g., cef_binary_<version>_windows64.tar.bz2).

  2. Extract and enter the directory:

    bash
    tar -xjf cef_binary_<version>_<platform>.tar.bz2
    cd cef_binary_<version>_<platform>
text
3. **Build cefsimple as-is:**

    **Windows:**
    ```bash
    # Build with sandbox disabled for simplicity
    cmake -B build -G "Visual Studio 17 2022" -A x64 -DUSE_SANDBOX=OFF
    cmake --build build --config Release --target cefsimple
    build\tests\cefsimple\Release\cefsimple.exe
text
**Note:** This tutorial builds with sandbox disabled (`-DUSE_SANDBOX=OFF`) for simplicity. For production applications, you should enable the sandbox using the [bootstrap.exe approach](sandbox_setup.md#windows). The sandbox provides important security isolation for renderer processes.

**Linux:**
```bash
cmake -B build
cmake --build build --target cefsimple
build/tests/cefsimple/cefsimple
text
**macOS:**
    ```bash
    cmake -B build -G Xcode
    cmake --build build --config Release --target cefsimple
    open build/tests/cefsimple/Release/cefsimple.app
text
**Using an IDE (optional):**

Instead of building from the command line, you can open the generated project in your IDE:

- **Windows**: Open `build/cef.sln` in Visual Studio 2022
- **macOS**: Open `build/cef.xcodeproj` in Xcode
- **Linux**: Open the `build` directory in CLion or another CMake-supporting IDE
  1. Verify it works:

    • You should see a window displaying google.com
    • Window title should update with the page title
    • Closing the window should exit the app cleanly

If this works, your environment is set up correctly! Now we can start learning CEF by modifying the code.

Understanding cefsimple Structure

text
tests/cefsimple/
├── simple_app.h                # Application handler (process-level)
├── simple_app.cc               # Browser creation logic
├── simple_handler.h            # Browser handler interface
├── simple_handler.cc           # Browser-level event handling
├── simple_handler_win.cc       # Windows-specific (window title)
├── simple_handler_linux.cc     # Linux-specific (window title)
├── simple_handler_mac.mm       # macOS-specific (window title)
├── cefsimple_win.cc            # Windows entry point
├── cefsimple_linux.cc          # Linux entry point
├── cefsimple_mac.mm            # macOS browser process entry point
└── process_helper_mac.cc       # macOS helper process entry point

Cross-platform code: simple_app.*, simple_handler.h/cc work identically on all platforms.

Platform-specific code: Entry points and window title updates for platform-specific implementations.

Step 1: Simplify to Absolute Minimum

Goal: Strip cefsimple down to the bare minimum (~150 lines total) to understand the essential parts.

What We're Removing

cefsimple has features we don't need yet:

  • ❌ Command-line URL argument handling
  • ❌ Window title updates
  • ❌ Error page handling
  • ❌ Multiple browser tracking

Let's remove these to see the core clearly.

Architecture Overview

Before we dive into the code, here's what each piece does:

SimpleApp - Application-level handler

  • Creates the browser and window when CEF is initialized
  • Uses the Views framework (cross-platform UI)

SimpleHandler - Browser-level event handler

  • Handles browser lifecycle events (created, closed)
  • Implements singleton pattern (global instance accessed by platform entry points)
  • Quits the app when the browser closes

Views Framework Delegates (in simple_app.cc)

  • SimpleWindowDelegate - Controls window size, close behavior, runtime style
  • SimpleBrowserViewDelegate - Specifies Alloy browser style

Platform Entry Point Methods

  • GetInstance() - Returns the singleton instance (fully functional)
  • CloseAllBrowsers(), IsClosing() - Empty stubs for now (implemented in Step 7 for multi-browser support)
  • ShowMainWindow() - Called on macOS when user clicks dock icon. Empty stub in this tutorial (a proper implementation would show/activate the main window)

Now let's see the actual code:

Modified Files

tests/cefsimple/simple_handler.h

This is the browser-level event handler. Note the singleton pattern (GetInstance()) and stub methods for platform entry points.

Replace the entire file with:

cpp
#ifndef CEF_TESTS_CEFSIMPLE_SIMPLE_HANDLER_H_
#define CEF_TESTS_CEFSIMPLE_SIMPLE_HANDLER_H_

#include "include/cef_client.h"

// Simple browser event handler
// Inherits from CefClient (main handler interface) and CefLifeSpanHandler
// (lifecycle events). This allows one class to handle multiple callback types.
class SimpleHandler : public CefClient, public CefLifeSpanHandler {
 public:
  SimpleHandler();
  ~SimpleHandler() override;

  // Provide access to the single global instance of this object.
  // Used by platform entry points (cefsimple_mac.mm, etc.)
  static SimpleHandler* GetInstance();

  // CefClient methods:
  CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override { return this; }

  // CefLifeSpanHandler methods:
  void OnBeforeClose(CefRefPtr<CefBrowser> browser) override;

  // Request that all existing browser windows close.
  // Used by platform entry points (cefsimple_mac.mm, etc.)
  void CloseAllBrowsers(bool force_close);

  // Always returns false - stub for platform entry points
  bool IsClosing() const { return false; }

  void ShowMainWindow();

 private:
  // Provides thread-safe reference counting (CefRefPtr).
  IMPLEMENT_REFCOUNTING(SimpleHandler);
  // Prevents copying (CEF objects shouldn't be copied).
  DISALLOW_COPY_AND_ASSIGN(SimpleHandler);
};

#endif  // CEF_TESTS_CEFSIMPLE_SIMPLE_HANDLER_H_

tests/cefsimple/simple_handler.cc

Implementation of SimpleHandler. The key method is OnBeforeClose() which quits the app when the browser closes.

Replace the entire file with:

cpp
#include "tests/cefsimple/simple_handler.h"

#include "include/cef_app.h"
#include "include/wrapper/cef_helpers.h"

namespace {
// Anonymous namespace - variables here are only visible in this file
SimpleHandler* g_instance = nullptr;

}  // namespace

SimpleHandler::SimpleHandler() {
  DCHECK(!g_instance);  // Debug assertion - crashes if instance already exists
  g_instance = this;
}

SimpleHandler::~SimpleHandler() {
  g_instance = nullptr;
}

// static
SimpleHandler* SimpleHandler::GetInstance() {
  return g_instance;
}

void SimpleHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
  CEF_REQUIRE_UI_THREAD();

  // Browser closed - quit the application
  // This simplified version only supports a single browser
  CefQuitMessageLoop();
}

void SimpleHandler::CloseAllBrowsers(bool force_close) {
  // Empty stub - required by platform entry points but not used
  // in this simplified Views-based version
}

void SimpleHandler::ShowMainWindow() {
  // Empty stub. A proper implementation would show/activate the main window.
  // Called on macOS when user clicks the dock icon while app is running.
}

tests/cefsimple/simple_app.h

Application-level handler that receives callbacks when CEF is initialized. Declares OnContextInitialized() where we create the browser.

Replace the entire file with:

cpp
#ifndef CEF_TESTS_CEFSIMPLE_SIMPLE_APP_H_
#define CEF_TESTS_CEFSIMPLE_SIMPLE_APP_H_

#include "include/cef_app.h"

// Implement application-level callbacks for the browser process.
class SimpleApp : public CefApp, public CefBrowserProcessHandler {
 public:
  SimpleApp();

  // CefApp methods:
  CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler() override {
    return this;
  }

  // CefBrowserProcessHandler methods:
  void OnContextInitialized() override;

 private:
  IMPLEMENT_REFCOUNTING(SimpleApp);
  DISALLOW_COPY_AND_ASSIGN(SimpleApp);
};

#endif  // CEF_TESTS_CEFSIMPLE_SIMPLE_APP_H_

tests/cefsimple/simple_app.cc

Implementation of SimpleApp. Creates the browser and window using Views framework. Contains inline delegate classes that control window and browser behavior.

Replace the entire file with:

text
/* Detailed source-code truncated for AI context efficiency. */

Delete Platform-Specific Handler Files

Since we removed window title updates, delete these files:

  • tests/cefsimple/simple_handler_win.cc
  • tests/cefsimple/simple_handler_linux.cc
  • tests/cefsimple/simple_handler_mac.mm

Update CMakeLists.txt

CMakeLists.txt tells CMake which source files to compile. Since we deleted the platform-specific handler files, we need to remove them from the build configuration.

Update tests/cefsimple/CMakeLists.txt to remove references to the deleted files:

Find these lines:

cmake
set(CEFSIMPLE_SRCS_LINUX
  cefsimple_linux.cc
  simple_handler_linux.cc
  )
set(CEFSIMPLE_SRCS_MAC
  cefsimple_mac.mm
  simple_handler_mac.mm
  )
set(CEFSIMPLE_SRCS_WINDOWS
  cefsimple_win.cc
  resource.h
  simple_handler_win.cc
  )

Replace with:

cmake
set(CEFSIMPLE_SRCS_LINUX
  cefsimple_linux.cc
  )
set(CEFSIMPLE_SRCS_MAC
  cefsimple_mac.mm
  )
set(CEFSIMPLE_SRCS_WINDOWS
  cefsimple_win.cc
  resource.h
  )

This removes the references to simple_handler_linux.cc, simple_handler_mac.mm, and simple_handler_win.cc that we deleted.

Keep Unchanged

Don't modify these files - they contain platform-specific entry point code that's already correct:

Limitations of This Simplified Version

This simplified code has the following limitations:

  • Alloy style only: Hardcoded to CEF_RUNTIME_STYLE_ALLOY in both SimpleWindowDelegate and SimpleBrowserViewDelegate. The original cefsimple supports both Chrome and Alloy styles via command-line flag.
  • Single browser only: The app exits immediately when the browser window closes. Multi-browser support requires tracking browser instances (added in Step 7).
  • No window title updates: We removed the handlers that update window titles based on page titles (added back in Step 4).
  • No command-line URL support: The URL is hardcoded to "https://www.google.com" (added in Step 3).
  • Quit menu doesn't work: On macOS, the Quit option in the application menu doesn't work (the stub CloseAllBrowsers() implementation does nothing). You must close the window to quit the app. (Fixed in Step 7)
  • Dock icon doesn't restore window: On macOS, clicking the dock icon when the app is running doesn't restore the window (the stub ShowMainWindow() implementation does nothing). This remains a stub throughout the tutorial.
  • Empty stub methods: CloseAllBrowsers() and IsClosing() are empty stubs (implemented in Step 7 for multi-browser support). ShowMainWindow() remains a stub throughout (a proper implementation would show/activate the main window on macOS).

These limitations are intentional to keep the code minimal for learning. We'll add features back in subsequent steps.

Build and Run

bash
# Rebuild using the same commands as Step 0

# Windows
cmake --build build --config Release --target cefsimple

# Linux
cmake --build build --target cefsimple

# macOS
cmake --build build --config Release --target cefsimple

Run: See Step 0 for platform-specific run instructions.

You should see a window open with google.com. Note: The window title won't update with the page title anymore (we removed that feature to simplify), but otherwise the app works the same as the original. Now you understand every line of code!

What's Happening

Three key pieces:

  1. SimpleApp (application-level handler)

    • Inherits from CefApp and CefBrowserProcessHandler
    • OnContextInitialized() - Called when CEF is ready to create browsers
    • Creates the browser and window using Views framework
  2. SimpleHandler (browser-level event handler)

    • Inherits from CefClient and CefLifeSpanHandler
    • GetLifeSpanHandler() - Returns this handler for lifecycle events
    • OnBeforeClose() - Called when browser is closing, quits the app
  3. Views Framework Delegates

    • SimpleWindowDelegate - Controls window size, close behavior, runtime style
    • SimpleBrowserViewDelegate - Specifies Alloy browser style
    • Both are cross-platform - same code works on Windows, Linux, macOS

Why Alloy style? Simpler than Chrome style - you control the UI completely. We explicitly set CEF_RUNTIME_STYLE_ALLOY in both delegates.

Entry points: Platform-specific files (cefsimple_*.cc/mm) handle:

  • CEF initialization (CefInitialize)
  • Message loop (CefRunMessageLoop)
  • Shutdown (CefShutdown)
  • macOS-specific: Framework loading, NSApplication setup

Program Flow: Here's the complete execution sequence:

  1. main() (in entry point file) - Program starts
  2. CefInitialize() - Initializes CEF with SimpleApp instance
  3. SimpleApp::OnContextInitialized() - CEF ready, creates browser:
    • Creates SimpleHandler instance
    • Creates CefBrowserView with URL
    • Creates CefWindow with SimpleWindowDelegate
  4. CefRunMessageLoop() - Enters message loop, processes events
  5. User closes window → SimpleWindowDelegate::CanClose() → TryCloseBrowser()
  6. SimpleHandler::OnBeforeClose() - Called when browser is closing
    • Calls CefQuitMessageLoop() to exit the message loop
  7. CefShutdown() - Cleanup and shutdown CEF
  8. main() exits - Program terminates

Common Issues

  • Build errors about missing files - Make sure you:
    1. Deleted the three platform handler files (simple_handler_*.cc/mm)
    2. Updated CMakeLists.txt to remove references to those files (see instructions above)

Step 2: Custom URL from Code

Goal: Load different URLs by modifying the code (not command-line yet).

What We're Adding

Just change one line to demonstrate how browser creation works.

Modified Files

tests/cefsimple/simple_app.cc

In OnContextInitialized(), replace the URL:

From: "https://www.google.com"
To: "https://www.wikipedia.org"

Build and Run

bash
# Rebuild using the same commands as Step 0

# Windows
cmake --build build --config Release --target cefsimple

# Linux
cmake --build build --target cefsimple

# macOS
cmake --build build --config Release --target cefsimple

Run: See Step 0 for platform-specific run instructions.

Now it loads Wikipedia instead! This shows that the URL is set when creating the CefBrowserView.

Try these:

  • file:///path/to/local.html - Local files (use absolute path)
  • data:text/html,<h1>Hello CEF!</h1> - Inline HTML
  • Any HTTPS URL

What's Happening

The second parameter to CefBrowserView::CreateBrowserView is the initial URL. The browser navigates to this URL immediately after creation.

Step 3: Command-Line URL Support

Goal: Pass custom URLs via command-line: cefsimple --url=https://example.com

What We're Adding

Read the command-line and use it to set the URL.

Modified Files

tests/cefsimple/simple_app.cc

Add at the top:

cpp
#include "include/cef_command_line.h"

Replace OnContextInitialized with:

cpp
void SimpleApp::OnContextInitialized() {
  CEF_REQUIRE_UI_THREAD();

  // Get command-line arguments
  CefRefPtr<CefCommandLine> command_line =
      CefCommandLine::GetGlobalCommandLine();

  // Check for --url= parameter
  std::string url = command_line->GetSwitchValue("url");
  if (url.empty()) {
    url = "https://www.google.com";  // Default URL
  }

  CefRefPtr<SimpleHandler> handler(new SimpleHandler());
  CefBrowserSettings browser_settings;

  CefRefPtr<CefBrowserView> browser_view = CefBrowserView::CreateBrowserView(
      handler, url, browser_settings, nullptr, nullptr,
      new SimpleBrowserViewDelegate());

  CefWindow::CreateTopLevelWindow(new SimpleWindowDelegate(browser_view));
}

Build and Run

bash
# Rebuild using the same commands as Step 0

# Windows
cmake --build build --config Release --target cefsimple

# Linux
cmake --build build --target cefsimple

# macOS
cmake --build build --config Release --target cefsimple

Run with custom URL:

bash
# Windows
build\tests\cefsimple\Release\cefsimple.exe --url=https://github.com

# Linux
build/tests/cefsimple/cefsimple --url=https://github.com

# macOS
open build/tests/cefsimple/Release/cefsimple.app --args --url=https://github.com

What's Happening

New concepts:

  • CefCommandLine::GetGlobalCommandLine() - Access command-line arguments parsed by CEF
  • GetSwitchValue("url") - Read the value of --url= parameter
  • CEF automatically parses command-line in the entry point before calling OnContextInitialized

Try it:

bash
# Load local file
cefsimple --url=file:///home/user/test.html

# Load data URL (note the quotes to escape shell characters)
cefsimple --url="data:text/html,<h1>Hello!</h1>"

Step 4: Window Title Updates

Goal: Update window title to match the page title as it loads.

What We're Adding

  • Display handler to receive title change events
  • Get window from browser to update its title

Modified Files

tests/cefsimple/simple_handler.h

Update to add display handler:

cpp
#ifndef CEF_TESTS_CEFSIMPLE_SIMPLE_HANDLER_H_
#define CEF_TESTS_CEFSIMPLE_SIMPLE_HANDLER_H_

#include "include/cef_client.h"

class SimpleHandler : public CefClient,
                      // NEW: Add display handler for title change events
                      public CefDisplayHandler,
                      public CefLifeSpanHandler {
 public:
  SimpleHandler();
  ~SimpleHandler() override;

  static SimpleHandler* GetInstance();

  // CefClient methods:
  // NEW: Return display handler
  CefRefPtr<CefDisplayHandler> GetDisplayHandler() override { return this; }
  CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override { return this; }

  // CefDisplayHandler methods:
  // NEW: Handle page title changes
  void OnTitleChange(CefRefPtr<CefBrowser> browser,
                     const CefString& title) override;

  // CefLifeSpanHandler methods:
  void OnBeforeClose(CefRefPtr<CefBrowser> browser) override;

  void CloseAllBrowsers(bool force_close);
  bool IsClosing() const { return false; }
  void ShowMainWindow();

 private:
  IMPLEMENT_REFCOUNTING(SimpleHandler);
  DISALLOW_COPY_AND_ASSIGN(SimpleHandler);
};

#endif  // CEF_TESTS_CEFSIMPLE_SIMPLE_HANDLER_H_

tests/cefsimple/simple_handler.cc

Add new includes at the top:

cpp
#include "include/views/cef_browser_view.h"
#include "include/views/cef_window.h"

Add the OnTitleChange implementation (after the existing methods, before OnBeforeClose()):

cpp
void SimpleHandler::OnTitleChange(CefRefPtr<CefBrowser> browser,
                                   const CefString& title) {
  CEF_REQUIRE_UI_THREAD();

  // Get the browser view from the browser
  CefRefPtr<CefBrowserView> browser_view =
      CefBrowserView::GetForBrowser(browser);
  if (browser_view) {
    // Get the window and update its title
    CefRefPtr<CefWindow> window = browser_view->GetWindow();
    if (window) {
      window->SetTitle(title);
    }
  }
}

Note: OnBeforeClose() and other methods from Step 1 remain unchanged.

Build and Run

bash
# Rebuild using the same commands as Step 0

# Windows
cmake --build build --config Release --target cefsimple

# Linux
cmake --build build --target cefsimple

# macOS
cmake --build build --config Release --target cefsimple

Run: See Step 0 for platform-specific run instructions.

Now the window title updates to match the page title as you navigate!

What's Happening

New concepts:

  • CefDisplayHandler::OnTitleChange() - Called when the page title changes
  • CefBrowserView::GetForBrowser() - Get the browser view from a browser instance
  • CefBrowserView::GetWindow() - Get the window containing the browser view
  • CefWindow::SetTitle() - Update the window title

Flow:

  1. Page loads or navigates → OnTitleChange() called with new title
  2. We get the browser view from the browser parameter
  3. We get the window from the browser view and update its title

Step 5: JavaScript Execution

Goal: Execute JavaScript code in the page from C++.

What We're Adding

  • Load handler to know when page is ready
  • Execute JavaScript after page loads

Modified Files

tests/cefsimple/simple_handler.h

Add load handler:

cpp
#ifndef CEF_TESTS_CEFSIMPLE_SIMPLE_HANDLER_H_
#define CEF_TESTS_CEFSIMPLE_SIMPLE_HANDLER_H_

#include "include/cef_client.h"

class SimpleHandler : public CefClient,
                      public CefDisplayHandler,
                      public CefLifeSpanHandler,
                      // NEW: Add load handler for page load events
                      public CefLoadHandler {
 public:
  SimpleHandler();
  ~SimpleHandler() override;

  static SimpleHandler* GetInstance();

  // CefClient methods:
  CefRefPtr<CefDisplayHandler> GetDisplayHandler() override { return this; }
  CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override { return this; }
  // NEW: Return load handler
  CefRefPtr<CefLoadHandler> GetLoadHandler() override { return this; }

  // CefDisplayHandler methods:
  void OnTitleChange(CefRefPtr<CefBrowser> browser,
                     const CefString& title) override;

  // CefLifeSpanHandler methods:
  void OnBeforeClose(CefRefPtr<CefBrowser> browser) override;

  // CefLoadHandler methods:
  // NEW: Handle page load completion
  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
                 CefRefPtr<CefFrame> frame,
                 int httpStatusCode) override;

  void CloseAllBrowsers(bool force_close);
  bool IsClosing() const { return false; }
  void ShowMainWindow();

 private:
  IMPLEMENT_REFCOUNTING(SimpleHandler);
  DISALLOW_COPY_AND_ASSIGN(SimpleHandler);
};

#endif  // CEF_TESTS_CEFSIMPLE_SIMPLE_HANDLER_H_

tests/cefsimple/simple_handler.cc

Add the OnLoadEnd implementation (after OnTitleChange(), before OnBeforeClose()):

cpp
void SimpleHandler::OnLoadEnd(CefRefPtr<CefBrowser> browser,
                               CefRefPtr<CefFrame> frame,
                               int httpStatusCode) {
  CEF_REQUIRE_UI_THREAD();

  // Only execute on the main frame
  if (!frame->IsMain()) {
    return;
  }

  // Execute JavaScript to change the page background color
  frame->ExecuteJavaScript(
      "document.body.style.backgroundColor = 'lightblue';",
      frame->GetURL(), 0);
}

Build and Run

bash
# Rebuild using the same commands as Step 0

# Windows
cmake --build build --config Release --target cefsimple

# Linux
cmake --build build --target cefsimple

# macOS
cmake --build build --config Release --target cefsimple

Run: See Step 0 for platform-specific run instructions.

Now when pages load, the background turns light blue!

What's Happening

New concepts:

  • CefLoadHandler::OnLoadEnd() - Called when a frame finishes loading
  • CefFrame::IsMain() - Check if this is the main frame (not an iframe)
  • CefFrame::ExecuteJavaScript() - Execute arbitrary JavaScript code
  • Parameters: JavaScript code, source URL (for debugging), start line number

Try modifying the JavaScript:

cpp
// Alert box
frame->ExecuteJavaScript("alert('Hello from C++!');", frame->GetURL(), 0);

// DOM manipulation
frame->ExecuteJavaScript(
    "var h1 = document.createElement('h1');"
    "h1.textContent = 'Added by CEF!';"
    "document.body.insertBefore(h1, document.body.firstChild);",
    frame->GetURL(), 0);

// Console log
frame->ExecuteJavaScript("console.log('CEF executed this');",
                         frame->GetURL(), 0);

Important notes:

  • JavaScript executes asynchronously
  • ExecuteJavaScript() doesn't return the result directly
  • To get results, use EvaluateJavaScript() (not shown) or process messages (next step)

Step 6: JavaScript-to-C++ Communication

Goal: Call C++ functions from JavaScript code in the page.

What We're Adding

  • Process message handler to receive messages from the renderer process
  • V8 context handler to register JavaScript functions
  • Bidirectional communication between C++ and JavaScript

This requires implementing both browser process and renderer process handlers.

Modified Files

tests/cefsimple/simple_app.h

Update to add renderer process handler:

cpp
#ifndef CEF_TESTS_CEFSIMPLE_SIMPLE_APP_H_
#define CEF_TESTS_CEFSIMPLE_SIMPLE_APP_H_

#include "include/cef_app.h"

class SimpleApp : public CefApp,
                  public CefBrowserProcessHandler,
                  // NEW: Add renderer process handler for JavaScript integration
                  public CefRenderProcessHandler {
 public:
  SimpleApp();

  // CefApp methods:
  CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler() override {
    return this;
  }
  // NEW: Return renderer process handler
  CefRefPtr<CefRenderProcessHandler> GetRenderProcessHandler() override {
    return this;
  }

  // CefBrowserProcessHandler methods:
  void OnContextInitialized() override;

  // NEW: CefRenderProcessHandler methods for JavaScript integration
  // NEW: Called when the JavaScript context is created in the renderer process
  void OnContextCreated(CefRefPtr<CefBrowser> browser,
                        CefRefPtr<CefFrame> frame,
                        CefRefPtr<CefV8Context> context) override;

 private:
  IMPLEMENT_REFCOUNTING(SimpleApp);
  DISALLOW_COPY_AND_ASSIGN(SimpleApp);
};

#endif  // CEF_TESTS_CEFSIMPLE_SIMPLE_APP_H_

tests/cefsimple/simple_app.cc

Add new includes at the top (after the existing includes):

cpp
#include "include/cef_v8.h"

Add V8 handler class in the anonymous namespace (add after the includes, before the existing SimpleWindowDelegate and SimpleBrowserViewDelegate classes which remain unchanged):

cpp
// V8 handler to handle JavaScript function calls in the renderer process
class MyV8Handler : public CefV8Handler {
 public:
  MyV8Handler() = default;

  bool Execute(const CefString& name,
               CefRefPtr<CefV8Value> object,
               const CefV8ValueList& arguments,
               CefRefPtr<CefV8Value>& retval,
               CefString& exception) override {
    if (name == "sayHello") {
      // Get the argument
      if (arguments.size() == 1 && arguments[0]->IsString()) {
        CefString message = arguments[0]->GetStringValue();

        // Send message to browser process
        CefRefPtr<CefProcessMessage> msg =
            CefProcessMessage::Create("greeting");
        msg->GetArgumentList()->SetString(0, message);

        CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
        CefRefPtr<CefFrame> frame = context->GetFrame();

        // Results in an asynchronous call to
        // SimpleHandler::OnProcessMessageReceived in the browser process
        frame->SendProcessMessage(PID_BROWSER, msg);

        // Return value to JavaScript
        retval = CefV8Value::CreateString("Message sent to C++!");
        return true;
      }
    }

    return false;
  }

 private:
  IMPLEMENT_REFCOUNTING(MyV8Handler);
  DISALLOW_COPY_AND_ASSIGN(MyV8Handler);
};

Add the OnContextCreated implementation (after OnContextInitialized, before the end of the file):

cpp
// Called when the JavaScript context is created in the renderer process
void SimpleApp::OnContextCreated(CefRefPtr<CefBrowser> browser,
                                  CefRefPtr<CefFrame> frame,
                                  CefRefPtr<CefV8Context> context) {
  // Debug assertion - crashes if called on wrong thread
  // V8 APIs must only be called from the renderer thread
  CEF_REQUIRE_RENDERER_THREAD();

  // Register JavaScript function 'sayHello' on the global object (window.sayHello)
  CefRefPtr<CefV8Value> object = context->GetGlobal();
  CefRefPtr<CefV8Handler> handler = new MyV8Handler();

  CefRefPtr<CefV8Value> func =
      CefV8Value::CreateFunction("sayHello", handler);
  object->SetValue("sayHello", func, V8_PROPERTY_ATTRIBUTE_NONE);
}

Note: The SimpleWindowDelegate, SimpleBrowserViewDelegate, and OnContextInitialized implementations remain unchanged from previous steps.

Platform Entry Points

Important: For JavaScript-to-C++ communication to work, the renderer process needs access to SimpleApp::OnContextCreated to register the JavaScript function.

  • macOS: Uses separate helper executables for sub-processes (renderer, GPU, etc.) - update process_helper_mac.cc
  • Windows and Linux: Reuse the main executable for sub-processes - update cefsimple_win.cc and cefsimple_linux.cc

tests/cefsimple/cefsimple_linux.cc

Update the main function to create SimpleApp before CefExecuteProcess:

Find:

cpp
// CEF applications have multiple sub-processes (render, GPU, etc) that share
  // the same executable. This function checks the command-line and, if this is
  // a sub-process, executes the appropriate logic.
  int exit_code = CefExecuteProcess(main_args, nullptr, nullptr);
  if (exit_code >= 0) {
    // The sub-process has completed so return here.
    return exit_code;
  }

Replace with:

cpp
// SimpleApp implements application-level callbacks. It will create the first
  // browser instance in OnContextInitialized() after CEF has initialized.
  CefRefPtr<SimpleApp> app(new SimpleApp);

  // CEF applications have multiple sub-processes (render, GPU, etc) that share
  // the same executable. This function checks the command-line and, if this is
  // a sub-process, executes the appropriate logic.
  int exit_code = CefExecuteProcess(main_args, app, nullptr);
  if (exit_code >= 0) {
    // The sub-process has completed so return here.
    return exit_code;
  }

Then find the later SimpleApp creation:

cpp
// SimpleApp implements application-level callbacks for the browser process.
  // It will create the first browser instance in OnContextInitialized() after
  // CEF has initialized.
  CefRefPtr<SimpleApp> app(new SimpleApp);

Delete these lines (since we now create app earlier).

tests/cefsimple/cefsimple_win.cc

Update the RunMain function to create SimpleApp before CefExecuteProcess:

Find:

cpp
// CEF applications have multiple sub-processes (render, GPU, etc) that share
  // the same executable. This function checks the command-line and, if this is
  // a sub-process, executes the appropriate logic.
  exit_code = CefExecuteProcess(main_args, nullptr, sandbox_info);
  if (exit_code >= 0) {
    // The sub-process has completed so return here.
    return exit_code;
  }

Replace with:

cpp
// SimpleApp implements application-level callbacks. It will create the first
  // browser instance in OnContextInitialized() after CEF has initialized.
  CefRefPtr<SimpleApp> app(new SimpleApp);

  // CEF applications have multiple sub-processes (render, GPU, etc) that share
  // the same executable. This function checks the command-line and, if this is
  // a sub-process, executes the appropriate logic.
  exit_code = CefExecuteProcess(main_args, app, sandbox_info);
  if (exit_code >= 0) {
    // The sub-process has completed so return here.
    return exit_code;
  }

Then find the later SimpleApp creation:

cpp
// SimpleApp implements application-level callbacks for the browser process.
  // It will create the first browser instance in OnContextInitialized() after
  // CEF has initialized.
  CefRefPtr<SimpleApp> app(new SimpleApp);

Delete these lines (since we now create app earlier).

tests/cefsimple/process_helper_mac.cc

Add include at the top:

cpp
#include "tests/cefsimple/simple_app.h"

Update the main function to create SimpleApp before CefExecuteProcess:

Find:

cpp
return CefExecuteProcess(main_args, nullptr, nullptr);

Replace with:

cpp
// SimpleApp implements application-level callbacks for the sub-process.
// This allows SimpleApp::OnContextCreated to be called in the renderer process.
CefRefPtr<SimpleApp> app(new SimpleApp);

// Execute the sub-process.
return CefExecuteProcess(main_args, app, nullptr);

Note: Unlike the main executable, the macOS helper doesn't have a separate browser process initialization section, so there's no duplicate SimpleApp creation to remove.

tests/cefsimple/CMakeLists.txt (macOS only)

Update the helper sources to include simple_app:

Find:

cmake
# cefsimple helper sources.
set(CEFSIMPLE_HELPER_SRCS_MAC
  process_helper_mac.cc
  )

Replace with:

cmake
# cefsimple helper sources.
set(CEFSIMPLE_HELPER_SRCS_MAC
  process_helper_mac.cc
  simple_app.cc
  simple_app.h
  simple_handler.cc
  simple_handler.h
  )

This ensures the helper process can compile and link the SimpleApp code.

Note: We're including simple_handler.cc/h in the helper sources even though SimpleHandler is never used in renderer processes (only SimpleApp::OnContextCreated is called). For a production application, you could create a separate CefApp-derived class for the renderer process (e.g., SimpleRendererApp) to avoid linking browser-process-only dependencies like SimpleHandler into the helper executables.

Reconfigure CMake (macOS only) to pick up the CMakeLists.txt changes:

bash
cmake -B build -G Xcode

tests/cefsimple/simple_handler.h

Add process message handler:

text
/* Detailed source-code truncated for AI context efficiency. */

tests/cefsimple/simple_handler.cc

Add at the top (after the existing includes):

cpp
#include <sstream>

Update OnLoadEnd to inject a test button (replace the background color change with button injection):

cpp
// Frame load finished in the browser process
void SimpleHandler::OnLoadEnd(CefRefPtr<CefBrowser> browser,
                               CefRefPtr<CefFrame> frame,
                               int httpStatusCode) {
  CEF_REQUIRE_UI_THREAD();

  if (!frame->IsMain()) {
    return;
  }

  // Inject a test button to call our C++ function
  std::stringstream ss;
  ss << "var button = document.createElement('button');"
     << "button.textContent = 'Call C++ Function';"
     << "button.style.position = 'fixed';"
     << "button.style.top = '10px';"
     << "button.style.left = '10px';"
     << "button.style.zIndex = '10000';"
     << "button.style.backgroundColor = '#4CAF50';"
     << "button.style.color = 'white';"
     << "button.style.padding = '10px 15px';"
     << "button.style.border = 'none';"
     << "button.style.borderRadius = '4px';"
     << "button.style.cursor = 'pointer';"
     << "button.onclick = function() {"
     << "  var result = sayHello('Hello from JavaScript!');"
     << "  alert(result);"
     << "};"
     << "document.body.appendChild(button);";

  frame->ExecuteJavaScript(ss.str(), frame->GetURL(), 0);
}

Add OnProcessMessageReceived (after OnLoadEnd, before OnBeforeClose):

cpp
// Process message received in the browser process
bool SimpleHandler::OnProcessMessageReceived(
    CefRefPtr<CefBrowser> browser,
    CefRefPtr<CefFrame> frame,
    CefProcessId source_process,
    CefRefPtr<CefProcessMessage> message) {
  CEF_REQUIRE_UI_THREAD();

  // Handle message from MyHandler::Execute() in the renderer process
  if (message->GetName() == "greeting") {
    CefString msg = message->GetArgumentList()->GetString(0);

    // Show the message in a JavaScript alert
    std::stringstream ss;
    ss << "alert('C++ received: " << msg.ToString() << "');";
    frame->ExecuteJavaScript(ss.str(), frame->GetURL(), 0);

    return true;
  }

  return false;
}

Note: OnTitleChange and OnBeforeClose remain unchanged from previous steps.

Build and Run

bash
# Rebuild using the same commands as Step 0

# Windows
cmake --build build --config Release --target cefsimple

# Linux
cmake --build build --target cefsimple

# macOS
cmake --build build --config Release --target cefsimple

Run: See Step 0 for platform-specific run instructions.

Now you'll see a button in the top-left corner of every page. Click it to:

  1. Call JavaScript sayHello() function
  2. JavaScript sends message to C++ via process message
  3. C++ receives message in browser process
  4. C++ sends alert back to JavaScript

What's Happening

New concepts:

  1. Multi-process architecture:

    • Browser process (UI, main logic)
    • Renderer process (usually one per origin, runs web content)
    • Processes communicate via messages
  2. Renderer process (SimpleApp):

    • OnContextCreated() - Called when JavaScript context is created
    • CefV8Value::CreateFunction() - Register a JavaScript function
    • CefV8Handler::Execute() - Handle JavaScript function calls
  3. Process messages:

    • CefProcessMessage::Create() - Create a message
    • CefFrame::SendProcessMessage() - Send from renderer to browser
    • CefClient::OnProcessMessageReceived() - Receive in browser process

Flow:

  1. Renderer process creates → OnContextCreated() registers sayHello() function
  2. User clicks button → JavaScript calls sayHello('Hello from JavaScript!')
  3. MyV8Handler::Execute() receives call, creates process message
  4. Message sent to browser process
  5. SimpleHandler::OnProcessMessageReceived() handles message in browser process
  6. C++ executes JavaScript to show alert

Important notes:

  • JavaScript function runs in renderer process (sandboxed)
  • C++ handler runs in browser process (has full access)
  • Messages are the bridge between processes
  • Messages can contain simple data types (strings, ints, lists, dictionaries), binary data, and shared memory regions

Learn more:

  • JavaScriptIntegration.md - Complete guide to V8 JavaScript integration:

    • Window binding and extensions
    • V8 value creation and type conversion
    • Working with V8 contexts
    • JavaScript objects and arrays
    • Function handlers (CefV8Handler)
  • GeneralUsage.md - Inter-Process Communication section:

    • Process startup messages
    • Process runtime messages (CefProcessMessage)
    • Asynchronous JavaScript bindings
    • Generic message router for request/response patterns
    • Custom IPC implementations

Step 7: Multiple Browser Support

Goal: Track multiple browser instances and only quit the application when all browsers are closed.

What We're Adding

In Steps 1-6, the application only supported a single browser. When that browser closed, the app quit immediately. Now we'll add:

  • Browser instance tracking (map using browser ID as key)
  • Proper lifecycle management (add on create, remove on close)
  • Only quit when the last browser closes
  • Working CloseAllBrowsers() implementation
  • A second button to create popup windows

Why This Matters

The single-browser approach from earlier steps has limitations:

  • Quits too early: If user creates a popup, closing the main window quits immediately (popup also closes)
  • No browser tracking: Can't iterate over browsers to close them all
  • CloseAllBrowsers() is a stub: Doesn't actually do anything

Multi-browser tracking is essential for real applications that support popups, new windows, or multiple browser instances.

Modified Files

tests/cefsimple/simple_handler.h

Add browser tracking and lifecycle methods:

text
/* Detailed source-code truncated for AI context efficiency. */

Changes:

  • Added #include <map> for browser tracking container
  • Added OnAfterCreated() to track browser creation
  • Added browser_map_ member to store all browser instances (keyed by browser ID)
  • Added is_closing_ flag to prevent new browsers during shutdown
  • Changed IsClosing() to return the actual flag instead of hardcoded false

tests/cefsimple/simple_handler.cc

Add includes for callbacks and closure tasks (after the existing includes):

cpp
#include "include/base/cef_callback.h"
#include "include/wrapper/cef_closure_task.h"

Add OnAfterCreated (new method, add after OnTitleChange):

cpp
void SimpleHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
  CEF_REQUIRE_UI_THREAD();

  // Add browser to our tracking map
  browser_map_[browser->GetIdentifier()] = browser;
}

Update OnLoadEnd to add second button (replace the existing OnLoadEnd):

text
/* Detailed source-code truncated for AI context efficiency. */

Update OnBeforeClose (replace the existing OnBeforeClose):

cpp
void SimpleHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
  CEF_REQUIRE_UI_THREAD();

  // Remove browser from our tracking map
  browser_map_.erase(browser->GetIdentifier());

  // Only quit when all browsers are closed
  if (browser_map_.empty()) {
    CefQuitMessageLoop();
  }
}

Update CloseAllBrowsers (replace the existing stub implementation):

cpp
void SimpleHandler::CloseAllBrowsers(bool force_close) {
  // CloseAllBrowsers can be called from anywhere (e.g., macOS menu's Quit command),
  // but CEF browser operations must run on the UI thread. Check if we're already
  // on the UI thread; if not, post a task to run this method on the UI thread.
  if (!CefCurrentlyOn(TID_UI)) {
    CefPostTask(TID_UI, base::BindOnce(&SimpleHandler::CloseAllBrowsers, this,
                                        force_close));
    return;
  }

  is_closing_ = true;

  if (browser_map_.empty()) {
    return;
  }

  // Iterate over browsers and request close
  // Use a copy because browser_map_ will be modified during iteration (OnBeforeClose)
  BrowserMap browser_map_copy = browser_map_;
  for (auto& pair : browser_map_copy) {
    pair.second->GetHost()->CloseBrowser(force_close);
  }
}

Note: Constructor, OnTitleChange, OnProcessMessageReceived, and ShowMainWindow remain unchanged from previous steps.

Changes:

  • Added OnAfterCreated(): Adds browser to browser_map_ when created (keyed by browser ID)
  • Updated OnLoadEnd(): Added second button "Create Popup Window" (blue, positioned below first button)
  • Updated OnBeforeClose(): Removes browser from map by ID, only quits when map is empty
  • Implemented CloseAllBrowsers(): Sets is_closing_ flag, iterates over browser_map_ copy, closes each browser
  • Uses CefPostTask() to ensure execution on UI thread

Build and Run

bash
# Rebuild using the same commands as Step 0

# Windows
cmake --build build --config Release --target cefsimple

# Linux
cmake --build build --target cefsimple

# macOS
cmake --build build --config Release --target cefsimple

Run: See Step 0 for platform-specific run instructions.

What's Happening

New concepts:

  1. Browser lifecycle tracking with map:

    • Use std::map> keyed by browser ID
    • Browser ID obtained via browser->GetIdentifier() - unique integer for each browser
    • OnAfterCreated() - Called when browser is created, add to map
    • OnBeforeClose() - Called when browser is closing, remove from map by ID
    • Only quit when map is empty
    • Why map instead of set? CefRefPtr comparison doesn't work as expected for erase(), so we key by integer ID
  2. Thread-safe closing:

    • CloseAllBrowsers() uses CefPostTask() to execute on UI thread
    • CEF requires most operations on the UI thread
    • base::BindOnce() creates a callback that captures this and arguments
    • Requires two includes:
      • include/base/cef_callback.h - provides base::BindOnce
      • include/wrapper/cef_closure_task.h - converts callbacks to CefTask objects for CefPostTask
  3. Container iteration safety:

    • Create a copy of browser_map_ before iterating
    • OnBeforeClose() modifies the map during iteration (would cause crashes)
    • Copy ensures safe iteration even as browsers close
    • Iterate over pair.second to get browser from map entry

Testing:

  1. Launch the app - main window appears
  2. Click "Create Popup Window" - popup opens
  3. Close main window - app stays running (popup still open)
  4. Close popup - now app quits (all browsers closed)
  5. Try creating multiple popups and closing them in different orders

macOS note: The Quit menu now works! CloseAllBrowsers() is properly implemented, so the macOS application menu's Quit option (which calls CloseAllBrowsers()) will close all browsers and quit the app.

Learn more:

  • GeneralUsage.md - Posting Tasks section:
    • Complete guide to CefPostTask family of functions
    • base::BindOnce and base::Callback usage
    • Thread-safe task posting patterns
    • CefTaskRunner for accessing specific thread message loops
    • Bound methods vs bound functions

Step 8: Views Styling of Popup Browsers

Goal: Customize the appearance and behavior of popup browser windows using the Views framework.

What We're Adding

In Step 7, clicking "Create Popup Window" opened a new browser, but it used default styling (same size as main window). Now we'll add custom styling for popup windows:

  • OnPopupBrowserViewCreated() callback in SimpleBrowserViewDelegate
  • Custom window creation for popups (smaller size, different from main window)
  • Understanding of when to use OnBeforePopup vs OnPopupBrowserViewCreated

Why This Matters

CEF provides two callbacks for popup customization with different purposes:

OnBeforePopup (CefLifeSpanHandler) - Low-level popup control:

  • Called before the popup browser is created
  • Runs in the browser process
  • Can cancel popup creation (return true to cancel)
  • Can change the popup's CefWindowInfo, CefClient and CefBrowserSettings
  • Use for: Blocking popups, popup policy enforcement, native window parenting

OnPopupBrowserViewCreated (CefBrowserViewDelegate) - Views-specific window creation:

  • Called after the popup browser view is created
  • Only called when using the Views framework
  • Gives you control over window creation and styling
  • Create custom CefWindow with different size, position, title bar, etc.
  • Use for: Custom popup window appearance and behavior in Views-based apps

In this step, we use OnPopupBrowserViewCreated to create smaller popup windows (600x400) compared to the main window (800x600).

Modified Files

tests/cefsimple/simple_app.cc

Update SimpleBrowserViewDelegate to add OnPopupBrowserViewCreated (add after GetBrowserRuntimeStyle, before the private section):

cpp
// Browser view delegate
class SimpleBrowserViewDelegate : public CefBrowserViewDelegate {
 public:
  SimpleBrowserViewDelegate() = default;

  // NEW: Called when a popup browser view is created
  bool OnPopupBrowserViewCreated(
      CefRefPtr<CefBrowserView> browser_view,
      CefRefPtr<CefBrowserView> popup_browser_view,
      bool is_devtools) override {
    // Create a smaller window for popups (600x400 instead of 800x600)
    // We use a custom SimpleWindowDelegate that returns a different size
    CefWindow::CreateTopLevelWindow(
        new SimpleWindowDelegate(popup_browser_view));

    // Returning true indicates we created the window
    return true;
  }

  cef_runtime_style_t GetBrowserRuntimeStyle() override {
    return CEF_RUNTIME_STYLE_ALLOY;
  }

 private:
  IMPLEMENT_REFCOUNTING(SimpleBrowserViewDelegate);
  DISALLOW_COPY_AND_ASSIGN(SimpleBrowserViewDelegate);
};

Update SimpleWindowDelegate to support custom popup sizes (replace the GetPreferredSize method):

cpp
CefSize GetPreferredSize(CefRefPtr<CefView> view) override {
    // Check if this is a popup by seeing if we have a browser yet
    CefRefPtr<CefBrowser> browser = browser_view_->GetBrowser();
    if (browser && browser->IsPopup()) {
      // Popup windows are smaller
      return CefSize(600, 400);
    }
    // Main window is larger
    return CefSize(800, 600);
  }

Note: The SimpleWindowDelegate, MyV8Handler, and OnContextInitialized/OnContextCreated implementations remain unchanged from Step 7.

Build and Run

bash
# Rebuild using the same commands as Step 0

# Windows
cmake --build build --config Release --target cefsimple

# Linux
cmake --build build --target cefsimple

# macOS
cmake --build build --config Release --target cefsimple

Run: See Step 0 for platform-specific run instructions.

What's Happening

New concepts:

  1. OnPopupBrowserViewCreated callback:

    • Called by CEF after a popup browser view is created
    • Only invoked when using Views framework (not for native windows)
    • Parameters:
      • browser_view - The parent browser view that initiated the popup
      • popup_browser_view - The newly created popup browser view
      • is_devtools - True if this is a DevTools popup
    • Return value:
      • true - You created the window (CEF won't create a default window)
      • false - CEF will create a default window
  2. Custom popup window creation:

    • We create a new CefWindow for the popup using CefWindow::CreateTopLevelWindow()
    • Pass the popup_browser_view to SimpleWindowDelegate (same pattern as main window)
    • The delegate's GetPreferredSize() returns different sizes for popups vs main windows
    • browser->IsPopup() identifies popup browsers
  3. Popup detection:

    • CefBrowser::IsPopup() returns true for browsers created via window.open() or similar
    • Main browser (created in OnContextInitialized) returns false
    • We use this to return different window sizes
  4. Alternative approach - GetDelegateForPopupBrowserView:

    • Instead of using the same delegate for all browser views, you can override CefBrowserViewDelegate::GetDelegateForPopupBrowserView() to return a different delegate for popups
    • This allows you to completely separate popup behavior from main window behavior
    • Example: Create a PopupBrowserViewDelegate class with different implementations of OnPopupBrowserViewCreated(), etc.
    • The approach in this tutorial (same delegate, detect popups with IsPopup()) is simpler and works well for most cases
    • Use GetDelegateForPopupBrowserView when you need significantly different delegate behavior for popups

Testing:

  1. Launch the app - main window is 800x600
  2. Click "Create Popup Window" - popup opens at 600x400 (smaller)
  3. Click "Create Popup Window" from the popup - another 600x400 popup opens
  4. All popups use the custom smaller size
  5. Close popups - app continues running until all browsers closed

When to use each callback:

Scenario Use OnBeforePopup Use OnPopupBrowserViewCreated
Block all popups ✅ Return true to cancel ❌ Too late (browser already created)
Custom popup window size (Views) ❌ Limited control ✅ Full window control
Custom popup window styling (Views) ❌ Limited control ✅ Full window control
Popup policy enforcement ✅ Can examine URL, features ❌ Too late
Native window popups (non-Views) ✅ Can modify CefWindowInfo ❌ Not called for native windows

Important notes:

  • OnPopupBrowserViewCreated is only called for Views-based browsers (our tutorial uses Views)
  • If using native platform windows (non-Views), use OnBeforePopup and CefWindowInfo instead
  • Both callbacks can be used together (OnBeforePopup for policy, OnPopupBrowserViewCreated for styling)
  • DevTools windows also trigger these callbacks (check is_devtools parameter)

Next Steps

Congratulations! You've built a CEF application from scratch and learned:

  • ✅ CEF initialization and lifecycle
  • ✅ Views framework for cross-platform UI
  • ✅ Browser and window delegates
  • ✅ Command-line argument handling
  • ✅ Display and load handlers
  • ✅ JavaScript execution from C++
  • ✅ JavaScript-to-C++ communication via process messages
  • ✅ Multiple browser tracking and lifecycle management
  • ✅ Custom popup window styling with OnPopupBrowserViewCreated

Explore More Features

cefclient - A comprehensive sample application demonstrating:

  • Chrome style browsers (full Chrome UI)
  • Platform-specific implementations (WinAPI, GTK, Cocoa)
  • Off-screen rendering (OSR)
  • Custom scheme handlers (client:// URLs)
  • Request interception and modification
  • Cookie management
  • Dialog handlers
  • Context menus
  • Drag and drop
  • And much more...

cef-project - Focused example applications demonstrating specific features:

  • message_router - JavaScript bindings using CefMessageRouter for request/response patterns
  • resource_manager - Resource request handling using CefResourceManager
  • scheme_handler - Custom protocol handlers using CefSchemeHandlerFactory

Learn Advanced Topics

General Usage - Complete CEF documentation covering:

  • Multi-process architecture in depth
  • Threading model and task posting
  • Request handling and network interception
  • JavaScript integration patterns
  • Custom protocol handlers
  • Performance optimization
  • Security considerations

Using the C API

Using The CAPI - Complete guide to the CEF C API:

  • Manual reference counting patterns
  • Structure allocation and ownership
  • String handling
  • Thread safety and atomic operations
  • Complete working examples in cefsimple_capi

Using Claude Code for Implementation

Want help implementing additional features in your CEF application? The CEF project includes instructions for using Claude Code to assist with tasks like adding custom URL schemes, JavaScript-to-C++ communication, request interception, and more.

See the CEF Client Development Guide for step-by-step instructions.


Installer (`docs/installer.md`)

CEF Installer

Available starting with CEF version 151.1.

The CEF Installer Library is part of the standard bootstrap binary and provides
automatic CEF version management for Windows applications using the
bootstrap architecture. It downloads, installs, updates, and
uninstalls CEF from a shared installation directory so that multiple applications
can share a single managed copy of a CEF version (including libcef and all
associated DLLs and resources).

Installer behavior is optional. It activates only when a CEF_INSTALLER_CONFIG
resource is embedded in the client DLL or bootstrap executable. Without that
resource, the client DLL loads the libcef DLL in the usual way with no installer
involvement.

The authoritative reference is
libcef_dll/bootstrap/installer/README.md.
This document is a brief overview; follow the links below for full details.

What It Does

  • Automatic resolution: On startup, the bootstrap finds the best compatible
    CEF version already installed, or downloads and installs one from the CDN.
    Downloaded archives are verified against their hash and signed catalog before
    installation.
  • Shared installation: Multiple apps share versioned CEF installations
    under a common directory (typically %LocalAppData%\CEF\ for standard users
    or %ProgramFiles%\CEF\ for admin installs).
  • Updates: Apps can check for newer CEF versions in the background through
    the RunInstaller API, or manually via /cef-update (when enabled, see below).
  • Uninstall and cleanup: Removing an app's registration automatically
    prunes CEF versions that no other app needs.
  • Version safety: Revoked versions are excluded, and optional launch-health
    tracking rolls back versions that crash repeatedly.
  • Enterprise support: Group Policy controls can restrict download sources,
    enforce offline mirrors, or disable external downloads entirely.

Quick Start

1. Add an installer configuration resource

Embed a CEF_INSTALLER_CONFIG JSON resource in your client DLL (or bootstrap
.exe):

json
{
  "appid": "A3B9C4D5-E6F7-4A8B-9C0D-E1F2A3B4C5D6",
  "vmin": "151.1",
  "vmax": "",
  "abi_hash": "1234567890ABCDEF",
  "launch_health": "explicit"
}

Key fields:

Field Purpose
appid Unique UUID for your application (never changes).
vmin Minimum compatible CEF version.
vmax Maximum compatible CEF version (empty = no upper bound).
abi_hash Sandbox compatibility hash from cef_version.h.
launch_health "off", "explicit" (recommended), or "exit_code".

For the full field reference, see
Configuration Fields.

2. Build with the standard binary distribution (cefclient example)

The standard Windows binary distribution provides an opt-in CMake flag for
building cefclient with installer support:

bash
cmake -G "Visual Studio 17" -A x64 -DUSE_INSTALLER=On ..

This Release-only configuration:

  • Builds the bootstrap and sandbox libraries
  • Embeds the CEF_INSTALLER_CONFIG resource in the client DLL
  • Stages only the bootstrap .exe, client DLL, and chrome_elf.dll

Replace the sample application identity (appid) before production use. See
Build Integration
for resource embedding details.

3. Sign your binaries

The bootstrap verifies that chrome_elf.dll and the .exe share the same
code-signing certificate. Both must be signed before deployment. CEF
installations are signed and verified separately using their own catalog and
certificate.

See Code Signing for application signing commands and
signed catalog creation for CEF
distributions.

Command-Line Usage

Run the bootstrap executable with installer flags:

text
MyApp.exe /cef-update                    # Check for and install updates
MyApp.exe /cef-update /cef-background    # Silent background update
MyApp.exe /cef-uninstall                 # Unregister and prune unused versions

These commands require enable_explicit_modes: true in the bootstrap's
embedded configuration. See
Command-Line Options.

Background Updates from Code

Client DLLs can trigger updates programmatically via the RunInstaller export:

cpp
auto run_installer = reinterpret_cast<RunInstallerFunc>(
    GetProcAddress(GetModuleHandle(nullptr), "RunInstaller"));
const char* result = run_installer("update", config_json.c_str());

The result is a JSON string indicating success or failure. Available commands:
install, update, uninstall, query, and launch_success.

For the full API, result format, and extended configuration options, see
Background Update API.

Version Selection

The installer picks the best CEF version from multiple sources:

  1. Installed versions in the shared directory (or an app-specific
    install_path if configured)
  2. An optional bundled version shipped with the app (bundled_cef_path)
  3. CDN download when no local version qualifies

Among qualifying candidates, newer wins; ties go to the installed copy. Revoked
versions are excluded from normal selection, and crash-disqualified versions
(when launch-health tracking is enabled) are deprioritized.

For the complete decision tree, see
Version Selection.

Bundled CEF Modes

Applications can optionally ship CEF alongside their own binaries instead of
relying solely on the shared installation directory and CDN downloads.

  • bundled_cef_path: Points to a full CEF distribution directory (with
    cef_version.json, catalog.cat, and Release/libcef.dll). The bundled
    version participates in normal version selection — it is compared against
    installed versions and the newer one wins. It is used in-place and never
    copied to the shared directory. See
    Bundled Version Behavior.

  • unchecked_cef_path: Points directly to a directory containing
    libcef.dll. When set, this path is checked first and bypasses all version,
    ABI, signature, and revocation checks — the application is fully responsible
    for the integrity of that DLL. If the DLL is absent, the installer falls
    through to normal resolution. See
    Unchecked CEF Path.

Further Reading

Topic Link
Full installer reference README.md
Security model SECURITY.md
Enterprise policy ADMIN_POLICY.md
Testing TESTING.md
Bootstrap/client integration sandbox_setup.md

Javascript Integration (`docs/javascript_integration.md`)

This page explains how to use V8 JavaScript integration in client applications.

Contents


Introduction

Chromium and CEF use the V8 JavaScript Engine for their internal JavaScript (JS) implementation. Each frame in a browser has its own JS context that provides scope and security for JS code executing in that frame (see the "Working with Contexts" section for more information). CEF exposes a large number of JS features for integration in client applications.

With CEF3 Blink (WebKit) and JS execution run in a separate renderer process. The main thread in a renderer process is identified as TID_RENDERER and all V8 execution must take place on this thread. Callbacks related to JS execution are exposed via the CefRenderProcessHandler interface. This interface is retrieved via CefApp::GetRenderProcessHandler() when a new renderer process is initialized.

JS APIs that communicate between the browser and renderer processes should be designed using asynchronous callbacks. See the "Asynchronous JavaScript Bindings" section of the General Usage wiki page for more information.

ExecuteJavaScript

The simplest way to execute JS from a client application is using the CefFrame::ExecuteJavaScript() function. This function is available in both the browser process and the renderer process and can safely be used from outside of a JS context.

cpp
CefRefPtr<CefBrowser> browser = ...;
CefRefPtr<CefFrame> frame = browser->GetMainFrame();
frame->ExecuteJavaScript("alert('ExecuteJavaScript works!');",
    frame->GetURL(), 0);

The above example will cause alert('ExecuteJavaScript works!'); to be executed in the browser's main frame.

The ExecuteJavaScript() function can be used to interact with functions and variables in the frame's JS context. In order to return values from JS to the client application consider using Window Binding or Extensions.

Window Binding

Window binding allows the client application to attach values to a frame's window object. Window bindings are implemented using the CefRenderProcessHandler::OnContextCreated() method.

cpp
void MyRenderProcessHandler::OnContextCreated(
    CefRefPtr<CefBrowser> browser,
    CefRefPtr<CefFrame> frame,
    CefRefPtr<CefV8Context> context) {
  // Retrieve the context's window object.
  CefRefPtr<CefV8Value> object = context->GetGlobal();

  // Create a new V8 string value. See the "Basic JS Types" section below.
  CefRefPtr<CefV8Value> str = CefV8Value::CreateString("My Value!");

  // Add the string to the window object as "window.myval". See the "JS Objects" section below.
  object->SetValue("myval", str, V8_PROPERTY_ATTRIBUTE_NONE);
}

JavaScript in the frame can then interact with the window bindings.

html
<script language="JavaScript">
alert(window.myval); // Shows an alert box with "My Value!"
</script>

Window bindings are reloaded each time a frame is reloaded giving the client application an opportunity to change the bindings if necessary. For example, different frames may be given access to different features in the client application by modifying the values that are bound to the window object for that frame.

Extensions

Extension support is deprecated in M154.

Extensions are like window bindings except they are loaded into the context for every frame and cannot be modified once loaded. The DOM does not exist when an extension is loaded and attempts to access the DOM during extension loading will result in a crash. Extensions are registered using the CefRegisterExtension() function which should be called from the CefRenderProcessHandler::OnWebKitInitialized() method.

cpp
void MyRenderProcessHandler::OnWebKitInitialized() {
  // Define the extension contents.
  std::string extensionCode =
    "var test;"
    "if (!test)"
    "  test = {};"
    "(function() {"
    "  test.myval = 'My Value!';"
    "})();";

  // Register the extension.
  CefRegisterExtension("v8/test", extensionCode, NULL);
}

The string represented by extensionCode can be any valid JS code. JS in the frame can then interact with the extension code.

html
<script language="JavaScript">
alert(test.myval); // Shows an alert box with "My Value!"
</script>

Basic JS Types

CEF supports the creation of basic JS data types including undefined, null, bool, int, double, date and string. These types are created using the CefV8Value::Create*() static methods. For example, to create a new JS string value use the CreateString() method.

cpp
CefRefPtr<CefV8Value> str = CefV8Value::CreateString("My Value!");

Basic value types can be created at any time and are not initially associated with a particular context (see the "Working with Contexts" section for more information).

To test the value type use the Is*() methods.

cpp
CefRefPtr<CefV8Value> val = ...;
if (val.IsString()) {
  // The value is a string.
}

To retrieve the value use the Get*Value() methods.

text
CefString strVal = val.GetStringValue();

JS Arrays

Arrays are created using the CefV8Value::CreateArray() static method which accepts a length argument. Arrays can only be created and used from within a context (see the "Working with Contexts" section for more information).

cpp
// Create an array that can contain two values.
CefRefPtr<CefV8Value> arr = CefV8Value::CreateArray(2);

Values are assigned to an array using the SetValue() method variant that takes an index as the first argument.

cpp
// Add two values to the array.
arr->SetValue(0, CefV8Value::CreateString("My First String!"));
arr->SetValue(1, CefV8Value::CreateString("My Second String!"));

To test if a CefV8Value is an array use the IsArray() method. To get the le