tests: Add cefsimple_capi example project

This is a more-or-less direct translation of the cefsimple (C++)
project to use C API based on the updated UsingTheCAPI.md docs.
Translation was performed using Claude Code.
This commit is contained in:
Marshall Greenblatt committed 2025-11-13 19:59:23 +00:00
1 parent 69a352bf67
commit 2305ffe1d6
51 files changed
+4183 -17

No files matched your search

+257 -3
View File
@@ -306,6 +306,12 @@ includes_mac = gypi_paths2.includes_mac + gypi_paths2.includes_mac_capi
includes_linux = gypi_paths2.includes_linux + gypi_paths2.includes_linux_capi
includes_win = gypi_paths2.includes_win + gypi_paths2.includes_win_capi
# CAPI-only includes for pure C implementations.
includes_common_capi = gypi_paths2.includes_common_capi + gypi_paths2.includes_capi
includes_mac_capi = gypi_paths2.includes_mac_capi
includes_linux_capi = gypi_paths2.includes_linux_capi
includes_win_capi = gypi_paths2.includes_win_capi
#
# Targets that will be built when depending on "//cef".
#
@@ -314,6 +320,7 @@ group("cef") {
testonly = true
deps = [
":cefsimple",
":cefsimple_capi",
":ceftests",
":libcef_static_unittests",
]
@@ -324,6 +331,7 @@ group("cef") {
":bootstrapc",
":cefclient_dll",
":cefsimple_dll",
":cefsimple_capi_dll",
":ceftests_dll",
]
}
@@ -1123,6 +1131,49 @@ source_set("libcef_static") {
# libcef_dll_wrapper static targets.
#
if (is_mac) {
# Minimal target for loading the CEF framework dynamically on macOS.
# This provides just the necessary C API functions without the full
# C++ wrapper.
static_library("cef_capi_library_loader") {
# Necessary because the generated file includes test API headers.
testonly = true
sources = [
"include/wrapper/cef_library_loader.h",
"libcef_dll/wrapper/libcef_dll_dylib.cc",
"libcef_dll/wrapper/cef_scoped_library_loader_mac.mm",
]
defines = [ "WRAPPING_CEF_SHARED" ]
configs += [ ":libcef_includes_config" ]
public_configs = [ ":libcef_includes_config" ]
deps = [ ":cef_make_headers" ]
}
# Minimal target for loading the CEF sandbox dynamically on macOS.
# This provides just the necessary C API functions without the full
# C++ wrapper.
static_library("cef_capi_sandbox_loader") {
# Necessary because the generated file includes test API headers.
testonly = true
sources = [
"include/cef_sandbox_mac.h",
"libcef_dll/wrapper/cef_scoped_sandbox_context_mac.mm",
]
defines = [ "WRAPPING_CEF_SHARED" ]
configs += [ ":libcef_includes_config" ]
public_configs = [ ":libcef_includes_config" ]
deps = [ ":cef_make_headers" ]
}
}
# Configuration that will be applied to all targets that build autogen files.
config("libcef_autogen_config") {
if (is_clang) {
@@ -1886,7 +1937,6 @@ if (is_mac) {
deps = [
":cef_make_headers",
":libcef_dll_wrapper",
]
if (defined(invoker.helper_deps)) {
deps += invoker.helper_deps
@@ -1909,6 +1959,10 @@ if (is_mac) {
defines = invoker.helper_defines
}
if (defined(invoker.helper_cflags_c)) {
cflags_c = invoker.helper_cflags_c
}
# Disable clang modules for this target (see issue #3611).
use_libcxx_modules = false
}
@@ -1942,6 +1996,9 @@ if (is_mac) {
if (defined(invoker.helper_defines)) {
helper_defines = invoker.helper_defines
}
if (defined(invoker.helper_cflags_c)) {
helper_cflags_c = invoker.helper_cflags_c
}
helper_name_suffix = _helper_suffix
helper_bundle_id_suffix = _helper_bundle_id
@@ -1986,10 +2043,8 @@ if (is_mac) {
sources = invoker.sources
public_deps = [ ":libcef_dll_wrapper" ]
deps = [
":cef_make_headers",
":libcef_dll_wrapper",
":${app_name}_framework_bundle_data",
]
if (defined(invoker.deps)) {
@@ -2004,6 +2059,10 @@ if (is_mac) {
defines = invoker.defines
}
if (defined(invoker.cflags_c)) {
cflags_c = invoker.cflags_c
}
info_plist_target = ":${app_name}_plist"
# Disable clang modules for this target (see issue #3611).
@@ -2059,6 +2118,9 @@ if (is_mac) {
gypi_paths2.shared_sources_mac_helper +
gypi_paths2.cefclient_sources_common +
gypi_paths2.cefclient_sources_renderer
helper_deps = [
":libcef_dll_wrapper",
]
helper_defines = [
"CEF_USE_SANDBOX",
]
@@ -2075,6 +2137,7 @@ if (is_mac) {
gypi_paths2.cefclient_sources_common +
gypi_paths2.cefclient_sources_mac
deps = [
":libcef_dll_wrapper",
":cefclient_resources_bundle_data",
":cefclient_resources_bundle_data_english",
":cefclient_xibs",
@@ -2131,6 +2194,9 @@ if (is_mac) {
gypi_paths2.includes_wrapper +
gypi_paths2.includes_wrapper_mac +
gypi_paths2.cefsimple_sources_mac_helper
helper_deps = [
":libcef_dll_wrapper",
]
helper_defines = [
"CEF_USE_SANDBOX",
]
@@ -2143,6 +2209,7 @@ if (is_mac) {
gypi_paths2.cefsimple_sources_common +
gypi_paths2.cefsimple_sources_mac
deps = [
":libcef_dll_wrapper",
":cefsimple_resources_bundle_data",
":cefsimple_resources_bundle_data_english",
":cefsimple_xibs",
@@ -2156,6 +2223,75 @@ if (is_mac) {
}
#
# cefsimple_capi app targets (Pure C API implementation).
#
bundle_data("cefsimple_capi_resources_bundle_data") {
sources = [
"tests/cefsimple_capi/mac/cefsimple_capi.icns",
]
outputs = [
"{{bundle_resources_dir}}/{{source_file_part}}",
]
}
bundle_data("cefsimple_capi_resources_bundle_data_english") {
sources = [
"tests/cefsimple_capi/mac/English.lproj/InfoPlist.strings",
]
outputs = [
"{{bundle_resources_dir}}/English.lproj/{{source_file_part}}",
]
}
mac_xib_bundle_data("cefsimple_capi_xibs") {
sources = [
"tests/cefsimple_capi/mac/English.lproj/MainMenu.xib",
]
output_path = "{{bundle_resources_dir}}/English.lproj"
}
cef_app("cefsimple_capi") {
# Necessary because the cef_framework target is testonly.
testonly = true
helper_info_plist = "tests/cefsimple_capi/mac/helper-Info.plist.in"
helper_sources = includes_common_capi +
includes_mac_capi +
gypi_paths2.cefsimple_capi_sources_mac_helper
helper_deps = [
":cef_capi_library_loader",
":cef_capi_sandbox_loader",
]
helper_defines = [
"CEF_USE_SANDBOX",
]
helper_cflags_c = [ "-std=c11" ]
info_plist = "tests/cefsimple_capi/mac/Info.plist.in"
sources = includes_common_capi +
includes_mac_capi +
gypi_paths2.cefsimple_capi_sources_common +
gypi_paths2.cefsimple_capi_sources_mac
deps = [
":cef_capi_library_loader",
":cefsimple_capi_resources_bundle_data",
":cefsimple_capi_resources_bundle_data_english",
":cefsimple_capi_xibs",
]
frameworks = [
"AppKit.framework",
]
defines = [
"CEF_USE_SANDBOX",
]
cflags_c = [ "-std=c11" ]
}
#
# ceftests app targets.
#
@@ -2197,6 +2333,7 @@ if (is_mac) {
gypi_paths2.ceftests_sources_mac_helper +
gypi_paths2.ceftests_sources_mac_helper_shared
helper_deps = [
":libcef_dll_wrapper",
":gtest_teamcity",
"//testing/gtest",
]
@@ -2219,6 +2356,7 @@ if (is_mac) {
gypi_paths2.ceftests_sources_common +
gypi_paths2.ceftests_sources_mac
deps = [
":libcef_dll_wrapper",
":ceftests_resources_bundle_data",
":ceftests_resources_bundle_data_english",
":ceftests_xibs",
@@ -2557,6 +2695,122 @@ if (is_mac) {
}
#
# cefsimple_capi targets (Pure C API implementation).
#
executable("cefsimple_capi") {
# Necessary because the libcef target is testonly.
testonly = true
sources = includes_common_capi +
gypi_paths2.cefsimple_capi_sources_common
deps = [
":libcef",
]
configs += [ ":libcef_includes_config" ]
defines = [
"CEF_USE_SANDBOX",
]
if (is_win) {
sources += includes_win_capi +
gypi_paths2.cefsimple_capi_sources_win +
gypi_paths2.cefsimple_capi_sources_resources_win_rc
# Set /SUBSYSTEM:WINDOWS.
configs -= [ "//build/config/win:console" ]
configs += [ "//build/config/win:windowed" ]
# Delay-load as many DLLs as possible for sandbox and startup perf
# improvements.
configs += [
"//build/config/win:delayloads",
"//build/config/win:delayloads_not_for_child_dll",
]
deps += [
":cef_sandbox",
"//build/win:default_exe_manifest",
]
libs = [
"comctl32.lib",
"shlwapi.lib",
"rpcrt4.lib",
]
# Use C11 standard for C files.
cflags_c = [ "/std:c11" ]
}
if (is_linux) {
sources += includes_linux_capi +
gypi_paths2.cefsimple_capi_sources_linux
if (ozone_platform_x11) {
libs = [
"X11",
]
}
if (!is_component_build) {
# Set rpath to find our own libfreetype even in a non-component build.
configs += [ "//build/config/gcc:rpath_for_built_shared_libraries" ]
}
# Use C11 standard for C files.
cflags_c = [ "-std=c11" ]
}
}
if (is_win) {
# Like the "cefsimple_capi" executable target, but building a DLL to be loaded by
# bootstrap.exe.
shared_library("cefsimple_capi_dll") {
# Necessary because the libcef target is testonly.
testonly = true
output_name = "cefsimple_capi"
sources = includes_common_capi +
includes_win_capi +
gypi_paths2.cefsimple_capi_sources_common +
gypi_paths2.cefsimple_capi_sources_win +
gypi_paths2.cefsimple_capi_sources_resources_win_rc
deps = [
":bootstrap",
":libcef",
]
defines = [
"CEF_USE_BOOTSTRAP",
]
# Delay-load as many DLLs as possible for sandbox and startup perf
# improvements.
configs += [
":libcef_includes_config",
"//build/config/win:delayloads",
"//build/config/win:delayloads_not_for_child_dll",
]
libs = [
"comctl32.lib",
"shlwapi.lib",
"rpcrt4.lib",
]
# Use C11 standard for C files.
cflags_c = [ "/std:c11" ]
}
}
#
# ceftests targets.
#
+1
View File
@@ -223,6 +223,7 @@ add_subdirectory(${CEF_LIBCEF_DLL_WRAPPER_PATH} libcef_dll_wrapper)
# TODO: Change these lines to match your project target when you copy this file.
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tests")
add_subdirectory(tests/cefsimple)
add_subdirectory(tests/cefsimple_capi)
add_subdirectory(tests/gtest)
add_subdirectory(tests/ceftests)
endif()
+17 -2
View File
@@ -62,12 +62,27 @@ def declare_cc_library(copts=[], local_defines=[], **kwargs):
**kwargs
)
def declare_objc_library(copts=[], **kwargs):
def declare_objc_library(copts=[], enable_cxx17=True, **kwargs):
"""
objc_library wrapper that applies common copts.
Args:
copts: Additional compiler options to add.
enable_cxx17: If True (default), adds -std=c++17 for Objective-C++ (.mm) files.
Set to False for pure Objective-C (.m) files that should not use C++ standard.
**kwargs: Additional arguments passed to objc_library.
"""
# NOTE: objc_library does not support local_defines on MacOS, so on
# that platform we put the defines in copts instead.
# Add C++17 standard for Objective-C++ files on macOS (default behavior)
# This allows .mm files to properly parse C++ headers while allowing
# pure .m files to opt out when needed.
cxx_standard = select({
"@platforms//os:macos": ["-std=c++17"] if enable_cxx17 else [],
"//conditions:default": [],
})
objc_library(
copts = select({
"@platforms//os:windows": WIN_COMMON_COPTS,
@@ -85,6 +100,6 @@ def declare_objc_library(copts=[], **kwargs):
"@cef//:macos_dbg": MAC_COMMON_COPTS_DEBUG,
"@cef//:macos_fastbuild": MAC_COMMON_COPTS_RELEASE,
"//conditions:default": None,
}) + copts,
}) + cxx_standard + copts,
**kwargs
)
+45
View File
@@ -494,6 +494,51 @@
'tests/cefsimple/cefsimple_linux.cc',
'tests/cefsimple/simple_handler_linux.cc',
],
'cefsimple_capi_sources_common': [
'tests/cefsimple_capi/ref_counted.h',
'tests/cefsimple_capi/simple_app.c',
'tests/cefsimple_capi/simple_app.h',
'tests/cefsimple_capi/simple_browser_list.c',
'tests/cefsimple_capi/simple_browser_list.h',
'tests/cefsimple_capi/simple_display_handler.c',
'tests/cefsimple_capi/simple_handler.c',
'tests/cefsimple_capi/simple_handler.h',
'tests/cefsimple_capi/simple_life_span_handler.c',
'tests/cefsimple_capi/simple_load_handler.c',
'tests/cefsimple_capi/simple_utils.h',
'tests/cefsimple_capi/simple_views.c',
'tests/cefsimple_capi/simple_views.h',
],
'cefsimple_capi_sources_win': [
'tests/cefsimple_capi/cefsimple_win.c',
'tests/cefsimple_capi/resource.h',
'tests/cefsimple_capi/simple_handler_win.c',
],
'cefsimple_capi_sources_resources_win': [
'tests/cefsimple_capi/win/cefsimple.exe.manifest',
'tests/cefsimple_capi/win/cefsimple.ico',
'tests/cefsimple_capi/win/small.ico',
],
'cefsimple_capi_sources_resources_win_rc': [
'tests/cefsimple_capi/win/cefsimple.rc',
],
'cefsimple_capi_sources_mac': [
'tests/cefsimple_capi/cefsimple_mac.m',
'tests/cefsimple_capi/simple_handler_mac.m',
],
'cefsimple_capi_sources_mac_helper': [
'tests/cefsimple_capi/process_helper_mac.c',
],
'cefsimple_capi_bundle_resources_mac': [
'tests/cefsimple_capi/mac/cefsimple_capi.icns',
'tests/cefsimple_capi/mac/English.lproj/InfoPlist.strings',
'tests/cefsimple_capi/mac/English.lproj/MainMenu.xib',
'tests/cefsimple_capi/mac/Info.plist.in',
],
'cefsimple_capi_sources_linux': [
'tests/cefsimple_capi/cefsimple_linux.c',
'tests/cefsimple_capi/simple_handler_linux.c',
],
'ceftests_data_resources': [
'tests/ceftests/resources/net/data/ssl/certificates/expired_cert.pem',
'tests/ceftests/resources/net/data/ssl/certificates/localhost_cert.pem',
+4 -2
View File
@@ -338,8 +338,10 @@ macro(SET_COMMON_TARGET_PROPERTIES target)
# Compile flags.
target_compile_options(${target} PRIVATE $<$<COMPILE_LANGUAGE:C>:${CEF_COMPILER_FLAGS} ${CEF_C_COMPILER_FLAGS}>)
target_compile_options(${target} PRIVATE $<$<COMPILE_LANGUAGE:CXX>:${CEF_COMPILER_FLAGS} ${CEF_CXX_COMPILER_FLAGS}>)
target_compile_options(${target} PRIVATE $<$<CONFIG:Debug>:${CEF_COMPILER_FLAGS_DEBUG} ${CEF_CXX_COMPILER_FLAGS_DEBUG}>)
target_compile_options(${target} PRIVATE $<$<CONFIG:Release>:${CEF_COMPILER_FLAGS_RELEASE} ${CEF_CXX_COMPILER_FLAGS_RELEASE}>)
target_compile_options(${target} PRIVATE $<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:Debug>>:${CEF_COMPILER_FLAGS_DEBUG} ${CEF_C_COMPILER_FLAGS_DEBUG}>)
target_compile_options(${target} PRIVATE $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:Debug>>:${CEF_COMPILER_FLAGS_DEBUG} ${CEF_CXX_COMPILER_FLAGS_DEBUG}>)
target_compile_options(${target} PRIVATE $<$<AND:$<COMPILE_LANGUAGE:C>,$<CONFIG:Release>>:${CEF_COMPILER_FLAGS_RELEASE} ${CEF_C_COMPILER_FLAGS_RELEASE}>)
target_compile_options(${target} PRIVATE $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:Release>>:${CEF_COMPILER_FLAGS_RELEASE} ${CEF_CXX_COMPILER_FLAGS_RELEASE}>)
# Compile definitions.
target_compile_definitions(${target} PRIVATE ${CEF_COMPILER_DEFINES})
+16 -1
View File
@@ -60,10 +60,25 @@ extern "C" {
CEF_EXPORT void* cef_sandbox_initialize(int argc, char** argv);
///
/// Destroy the specified sandbox context handle.
/// Destroy the specified sandbox context handle created by
/// cef_sandbox_initialize().
///
CEF_EXPORT void cef_sandbox_destroy(void* sandbox_context);
///
/// Initialize the scoped sandbox for this process. Returns an opaque handle
/// on success or NULL on failure. The returned handle should be passed to
/// cef_scoped_sandbox_destroy() immediately before process termination.
/// This is a wrapper around CefScopedSandboxContext for C API users.
///
void* cef_scoped_sandbox_initialize(int argc, char** argv);
///
/// Destroy the specified scoped sandbox handle created by
/// cef_scoped_sandbox_initialize().
///
void cef_scoped_sandbox_destroy(void* sandbox_context);
#ifdef __cplusplus
}
+16
View File
@@ -51,6 +51,22 @@ int cef_load_library(const char* path);
///
int cef_unload_library(void);
///
/// Load the CEF library from the expected app bundle location relative to the
/// executable. Pass |helper| as 1 for helper processes or 0 for the main
/// process. Returns an opaque handle on success or NULL on failure. The
/// returned handle should be passed to cef_scoped_library_loader_free()
/// immediately before process termination. This is a wrapper around
/// CefScopedLibraryLoader for C API users.
///
void* cef_scoped_library_loader_create(int helper);
///
/// Destroy the specified library loader handle created by
/// cef_scoped_library_loader_create().
///
void cef_scoped_library_loader_free(void* loader);
#ifdef __cplusplus
}
#endif
@@ -75,3 +75,20 @@ CefScopedLibraryLoader::~CefScopedLibraryLoader() {
cef_unload_library();
}
}
// C API wrapper functions for use with pure C code.
void* cef_scoped_library_loader_create(int helper) {
CefScopedLibraryLoader* loader = new CefScopedLibraryLoader();
bool success = helper ? loader->LoadInHelper() : loader->LoadInMain();
if (!success) {
delete loader;
return nullptr;
}
return loader;
}
void cef_scoped_library_loader_free(void* loader) {
if (loader) {
delete static_cast<CefScopedLibraryLoader*>(loader);
}
}
@@ -111,3 +111,19 @@ bool CefScopedSandboxContext::Initialize(int argc, char** argv) {
sandbox_context_ = SandboxInitialize(library_handle_, argc, argv);
return !!sandbox_context_;
}
// C API wrapper functions for use with pure C code.
void* cef_scoped_sandbox_initialize(int argc, char** argv) {
CefScopedSandboxContext* context = new CefScopedSandboxContext();
if (!context->Initialize(argc, argv)) {
delete context;
return nullptr;
}
return context;
}
void cef_scoped_sandbox_destroy(void* sandbox_context) {
if (sandbox_context) {
delete static_cast<CefScopedSandboxContext*>(sandbox_context);
}
}
+113
View File
@@ -0,0 +1,113 @@
# 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/](../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](https://bitbucket.org/chromiumembedded/cef/wiki/Tutorial.md), 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 [GeneralUsage](https://bitbucket.org/chromiumembedded/cef/wiki/GeneralUsage.md).
## 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
## Building
From the Chromium `src` directory:
```bash
# Debug build (x64)
autoninja -C out/Debug_GN_x64 cefsimple
# Debug build (ARM64 on macOS)
autoninja -C out/Debug_GN_arm64 cefsimple
# Release build
autoninja -C out/Release_GN_x64 cefsimple
```
## Running
```bash
# macOS
out/Debug_GN_arm64/cefsimple.app/Contents/MacOS/cefsimple
# Linux
out/Debug_GN_x64/cefsimple
# Windows
out/Debug_GN_x64/cefsimple.exe
```
Expected behavior: Opens a window displaying https://www.google.com
## 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
- [CEF Tutorial](https://bitbucket.org/chromiumembedded/cef/wiki/Tutorial.md) - Detailed walkthrough of this example
- [CEF General Usage](https://bitbucket.org/chromiumembedded/cef/wiki/GeneralUsage.md) - Complete API documentation
- [CEF C++ API Documentation](https://bitbucket.org/chromiumembedded/cef/wiki/)
- [C API Version](../cefsimple_capi/) - Pure C implementation of this example
+239
View File
@@ -0,0 +1,239 @@
# CLAUDE.md - CEF Simple C API Example
This file provides guidance to Claude Code when working with the cefsimple_capi example application.
## Project Overview
**cefsimple_capi** is a minimal CEF browser application written in **pure C** (C11 standard) using the CEF C API. It demonstrates proper usage of CEF's C API including manual reference counting, handler callbacks, and cross-platform implementation.
This is a reference implementation showing how to use CEF from C without any C++ code.
## Essential Documentation
**ALWAYS read these documents when working on this project:**
1. **[cef_wiki/UsingTheCAPI.md](../../cef_wiki/UsingTheCAPI.md)** - Complete CEF C API guide
- Base structure types (ref-counted vs scoped)
- Reference counting rules and patterns
- String handling
- Thread safety and atomic operations
- Complete working examples
2. **[cef/tools/claude/CLAUDE_BUILD_INSTRUCTIONS.md](../../tools/claude/CLAUDE_BUILD_INSTRUCTIONS.md)** - Build workflow and error fixing
- Build system usage (GN and Ninja)
- Fixing build errors during Chromium updates
- Common error patterns and solutions
3. **[README.md](README.md)** - Project-specific documentation
- Architecture and file organization
- Key differences from C++ version
- Critical C API rules specific to this implementation
**Note:** If the `cef_wiki` directory doesn't exist, create it by running from the `chromium/src` directory:
```bash
git clone https://bitbucket.org/chromiumembedded/cef.git/wiki cef_wiki
```
## Building
From the Chromium `src` directory:
```bash
# Debug build (x64)
autoninja -C out/Debug_GN_x64 cefsimple_capi
# Debug build (ARM64 on macOS)
autoninja -C out/Debug_GN_arm64 cefsimple_capi
# Release build
autoninja -C out/Release_GN_x64 cefsimple_capi
# Rebuild after fixing errors in a specific file
autoninja -C out/Debug_GN_x64 obj/cef/tests/cefsimple_capi/cefsimple_capi/simple_app.o
```
## Architecture and API Layers
Understanding the layering is critical when fixing build errors:
```
┌─────────────────────────────────────────┐
│ cefsimple_capi (this project) │ ← We work here
│ - Pure C code (C11) │
│ - Uses CEF C API │
└─────────────────────────────────────────┘
↓ calls
┌─────────────────────────────────────────┐
│ CEF C API (cef/include/capi/*.h) │ ← Auto-generated headers
│ - C wrappers around CEF C++ API │ ← We reference these
│ - Generated by CEF translator tool │
└─────────────────────────────────────────┘
↓ wraps
┌─────────────────────────────────────────┐
│ CEF C++ Implementation (cef/libcef/) │ ← We don't modify
│ - C++ code wrapping Chromium │
│ - Updated when Chromium updates │
└─────────────────────────────────────────┘
↓ uses
┌─────────────────────────────────────────┐
│ Chromium (chrome/, content/, base/) │ ← We don't modify
│ - Core browser engine │
│ - Updates cause cascading changes │
└─────────────────────────────────────────┘
```
**Key points:**
- **We only modify** code in `cef/tests/cefsimple_capi/`
- **We consult** CEF C API headers in `cef/include/capi/` to understand the API
- When Chromium updates → CEF C++ updates → CEF C API updates → we update our C code
## File Organization
See [README.md](README.md) for complete architecture details. Key files:
### Utility Modules
- **ref_counted.h** - Reference counting macros (`IMPLEMENT_REFCOUNTING_SIMPLE`, `IMPLEMENT_REFCOUNTING_MANUAL`, `INIT_CEF_BASE_REFCOUNTED`)
- **simple_utils.h** - Common utilities (`CHECK` macro)
- **simple_browser_list.h/c** - Browser list with automatic reference counting
### Core Implementation
- **simple_app.h/c** - Application handler (`cef_app_t`)
- **simple_handler.h** - Client handler interface and structure definitions
- **simple_handler.c** - Main client handler, public API, browser close operations
- **simple_display_handler.c** - Display handler (title change)
- **simple_life_span_handler.c** - Life span handler (browser lifecycle)
- **simple_load_handler.c** - Load handler (error pages)
- **simple_views.h/c** - Browser view and window delegates
### Platform Entry Points
- **cefsimple_win.c** - Windows (wWinMain)
- **cefsimple_linux.c** - Linux (main)
- **cefsimple_mac.m** - macOS (NSApplicationMain integration)
### Platform Handlers
- **simple_handler_win.c** - Windows-specific (window title)
- **simple_handler_linux.c** - Linux-specific (X11 window title)
- **simple_handler_mac.m** - macOS-specific (NSWindow title)
- **process_helper_mac.c** - macOS helper process entry point
## Fixing Build Errors
**Important context:** This application uses the **CEF C API**, not Chromium APIs directly. When Chromium updates, CEF updates its C API wrappers to reflect changes in underlying Chromium functionality. This can cause build errors in our C code that need to be fixed.
#### What We're Working With
- **We modify:** Only files in `cef/tests/cefsimple_capi/` directory
- **We use:** CEF C API headers from `cef/include/capi/*.h`
- **We don't modify:** Chromium code, CEF implementation code, or patch files
#### When CEF C APIs Change
Similar to Chromium API updates, CEF C API changes follow patterns:
**Common CEF C API change patterns:**
1. **Function pointer signature changes** (new/removed parameters):
```c
// Old CEF API
void CEF_CALLBACK on_load_end(
cef_load_handler_t* self,
cef_browser_t* browser,
cef_frame_t* frame,
int httpStatusCode) { ... }
// New CEF API (added is_main_frame parameter)
void CEF_CALLBACK on_load_end(
cef_load_handler_t* self,
cef_browser_t* browser,
cef_frame_t* frame,
int httpStatusCode,
int is_main_frame) { ... }
```
2. **Structure field changes** (renamed/removed members):
```c
// Old
window_info.window = parent_window;
// New (if renamed)
window_info.parent_window = parent_window;
```
3. **New required function pointers** in base structures:
```c
// CEF added new function pointer to cef_base_ref_counted_t
app->app.base.get_size = app_get_size; // Must implement
```
4. **Handler callback changes** (different return types or parameters):
```c
// Old - returned int
int CEF_CALLBACK on_before_browse(...);
// New - returns cef_return_value_t enum
cef_return_value_t CEF_CALLBACK on_before_browse(...);
```
#### Fixing Process
1. **Read the error carefully** - Compiler tells you exactly what's wrong
2. **Check CEF C API headers** - Look at the current definition in `cef/include/capi/*.h`
3. **Update our code** - Modify files in `cef/tests/cefsimple_capi/` to match
4. **Rebuild incrementally** - Build individual .o files for fast feedback:
```bash
autoninja -C out/Debug_GN_x64 obj/cef/tests/cefsimple_capi/cefsimple_capi/simple_handler.o
```
5. **Consult UsingTheCAPI.md** - Reference patterns for correct implementation
#### Example Fix Workflow
If you see this error:
```
simple_handler.c:45:3: error: too few arguments to function call
```
1. Read the full error to identify the function
2. Open the relevant CEF header (`cef/include/capi/cef_life_span_handler_capi.h`)
3. Compare the signature in the header with our code
4. Update our callback to match the new signature
5. Rebuild just that file to verify
6. If successful, continue to next error
See [CLAUDE_BUILD_INSTRUCTIONS.md](../../tools/claude/CLAUDE_BUILD_INSTRUCTIONS.md) for detailed workflow and common patterns. The same principles apply, but remember we're working with CEF C API changes, not Chromium API changes directly.
## Key Reminders
1. **We work with CEF C API** - Not Chromium APIs directly; consult `cef/include/capi/*.h` headers
2. **Only modify cefsimple_capi files** - Never change Chromium code, CEF implementation, or patches
3. **Always check documentation first** - UsingTheCAPI.md has complete reference implementations
4. **Reference counting is critical** - Use atomics, release parameters, add_ref returns
5. **API version must be initialized** - Call `cef_api_hash()` before any CEF functions
6. **Structure ordering matters** - CEF base structure MUST be first member
7. **Size field is CEF size** - Use `sizeof(cef_*_t)`, not `sizeof(your_wrapper_t)`
8. **Thread safety required** - Reference counting functions called from any thread
9. **When APIs change** - Check CEF C API headers first, then update our C code to match
## Testing
Run the application after building:
```bash
# macOS
out/Debug_GN_arm64/cefsimple_capi.app/Contents/MacOS/cefsimple_capi
# Linux
out/Debug_GN_x64/cefsimple_capi
# Windows
out/Debug_GN_x64/cefsimple_capi.exe
```
Expected behavior: Opens a window displaying https://www.google.com
+256
View File
@@ -0,0 +1,256 @@
# Copyright (c) 2025 The Chromium Embedded Framework Authors. All rights
# reserved. Use of this source code is governed by a BSD-style license that
# can be found in the LICENSE file.
#
# Source files.
#
# cefsimple_capi sources.
{{
'prefix': 'cefsimple_capi',
'set': 'CEFSIMPLE_CAPI_SRCS',
'includes': [
'cefsimple_capi_sources_common',
'cefsimple_capi_sources_win:WINDOWS',
'cefsimple_capi_sources_resources_win_rc:WINDOWS',
'cefsimple_capi_sources_mac:MAC',
'cefsimple_capi_sources_linux:LINUX',
],
}}
# cefsimple_capi helper sources.
{{
'prefix': 'cefsimple_capi_helper',
'includes': [
'cefsimple_capi_sources_mac_helper:MAC',
],
}}
# cefsimple_capi resources.
{{
'prefix': 'cefsimple_capi_resources',
'set': 'CEFSIMPLE_CAPI_RESOURCES_SRCS',
'includes': [
'cefsimple_capi_bundle_resources_mac:MAC',
],
}}
#
# Shared configuration.
#
# Target executable names.
set(CEF_TARGET "cefsimple_capi")
if(OS_MAC)
set(CEF_HELPER_TARGET "cefsimple_capi_Helper")
set(CEF_HELPER_OUTPUT_NAME "cefsimple_capi Helper")
else()
# Logical target used to link the libcef library.
ADD_LOGICAL_TARGET("libcef_lib" "${CEF_LIB_DEBUG}" "${CEF_LIB_RELEASE}")
endif()
# Determine the target output directory.
SET_CEF_TARGET_OUT_DIR()
# cefsimple_capi requires C11 for stdatomic.h and other C11 features.
# Override the default C99 standard set by CEF.
if(OS_LINUX OR OS_MAC)
# Replace -std=c99 with -std=c11 for C files.
list(REMOVE_ITEM CEF_C_COMPILER_FLAGS "-std=c99")
list(APPEND CEF_C_COMPILER_FLAGS "-std=c11")
endif()
if(OS_WINDOWS)
# Add /std:c11 for C files.
list(APPEND CEF_C_COMPILER_FLAGS "/std:c11")
# Remove C++ standard flag since this is a C-only target.
# The Visual Studio generator sets both <LanguageStandard> and <LanguageStandard_C>
# properties, and <LanguageStandard> takes precedence when compiling C files.
list(REMOVE_ITEM CEF_CXX_COMPILER_FLAGS "/std:c++17")
endif()
#
# Linux configuration.
#
if(OS_LINUX)
# Executable target.
add_executable(${CEF_TARGET} ${CEFSIMPLE_CAPI_SRCS})
SET_EXECUTABLE_TARGET_PROPERTIES(${CEF_TARGET})
add_dependencies(${CEF_TARGET} libcef_dll_wrapper)
target_link_libraries(${CEF_TARGET} libcef_lib libcef_dll_wrapper ${CEF_STANDARD_LIBS})
# Set rpath so that libraries can be placed next to the executable.
set_target_properties(${CEF_TARGET} PROPERTIES
INSTALL_RPATH "$ORIGIN"
BUILD_WITH_INSTALL_RPATH TRUE
RUNTIME_OUTPUT_DIRECTORY ${CEF_TARGET_OUT_DIR}
C_STANDARD 11
C_STANDARD_REQUIRED ON
)
# Copy binary and resource files to the target output directory.
COPY_FILES("${CEF_TARGET}" "${CEF_BINARY_FILES}" "${CEF_BINARY_DIR}" "${CEF_TARGET_OUT_DIR}")
COPY_FILES("${CEF_TARGET}" "${CEF_RESOURCE_FILES}" "${CEF_RESOURCE_DIR}" "${CEF_TARGET_OUT_DIR}")
if (EXISTS "${CEF_BINARY_DIR}/libminigbm.so")
COPY_FILES("${CEF_TARGET}" "libminigbm.so" "${CEF_BINARY_DIR}" "${CEF_TARGET_OUT_DIR}")
endif()
# Set SUID permissions on the chrome-sandbox target.
SET_LINUX_SUID_PERMISSIONS("${CEF_TARGET}" "${CEF_TARGET_OUT_DIR}/chrome-sandbox")
endif()
#
# Mac OS X configuration.
#
if(OS_MAC)
option(OPTION_USE_ARC "Build with ARC (automatic Reference Counting) on macOS." ON)
if(OPTION_USE_ARC)
list(APPEND CEF_COMPILER_FLAGS
-fobjc-arc
)
set_target_properties(${target} PROPERTIES
CLANG_ENABLE_OBJC_ARC "YES"
)
endif()
# Output path for the main app bundle.
set(CEF_APP "${CEF_TARGET_OUT_DIR}/${CEF_TARGET}.app")
# Variables referenced from the main Info.plist file.
set(EXECUTABLE_NAME "${CEF_TARGET}")
set(PRODUCT_NAME "${CEF_TARGET}")
# Main app bundle target.
add_executable(${CEF_TARGET} MACOSX_BUNDLE ${CEFSIMPLE_CAPI_RESOURCES_SRCS} ${CEFSIMPLE_CAPI_SRCS})
SET_EXECUTABLE_TARGET_PROPERTIES(${CEF_TARGET})
add_dependencies(${CEF_TARGET} libcef_dll_wrapper)
target_link_libraries(${CEF_TARGET} libcef_dll_wrapper ${CEF_STANDARD_LIBS})
set_target_properties(${CEF_TARGET} PROPERTIES
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/mac/Info.plist.in
C_STANDARD 11
C_STANDARD_REQUIRED ON
XCODE_ATTRIBUTE_GCC_C_LANGUAGE_STANDARD "c11"
)
# Copy the CEF framework into the Frameworks directory and create the necessary symlinks.
COPY_MAC_FRAMEWORK("${CEF_TARGET}" "${CEF_BINARY_DIR}" "${CEF_APP}")
# Create the multiple Helper app bundle targets.
foreach(_suffix_list ${CEF_HELPER_APP_SUFFIXES})
# Convert to a list and extract the suffix values.
string(REPLACE ":" ";" _suffix_list ${_suffix_list})
list(GET _suffix_list 0 _name_suffix)
list(GET _suffix_list 1 _target_suffix)
list(GET _suffix_list 2 _plist_suffix)
# Define Helper target and output names.
set(_helper_target "${CEF_HELPER_TARGET}${_target_suffix}")
set(_helper_output_name "${CEF_HELPER_OUTPUT_NAME}${_name_suffix}")
# Create Helper-specific variants of the helper-Info.plist file. Do this
# manually because the configure_file command (which is executed as part of
# MACOSX_BUNDLE_INFO_PLIST) uses global env variables and would insert the
# wrong values with multiple targets.
set(_helper_info_plist "${CMAKE_CURRENT_BINARY_DIR}/helper-Info${_target_suffix}.plist")
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/mac/helper-Info.plist.in" _plist_contents)
string(REPLACE "\${EXECUTABLE_NAME}" "${_helper_output_name}" _plist_contents ${_plist_contents})
string(REPLACE "\${PRODUCT_NAME}" "${_helper_output_name}" _plist_contents ${_plist_contents})
string(REPLACE "\${BUNDLE_ID_SUFFIX}" "${_plist_suffix}" _plist_contents ${_plist_contents})
file(WRITE ${_helper_info_plist} ${_plist_contents})
# Create Helper executable target.
add_executable(${_helper_target} MACOSX_BUNDLE ${CEFSIMPLE_CAPI_HELPER_SRCS})
SET_EXECUTABLE_TARGET_PROPERTIES(${_helper_target})
add_dependencies(${_helper_target} libcef_dll_wrapper)
target_link_libraries(${_helper_target} libcef_dll_wrapper ${CEF_STANDARD_LIBS})
set_target_properties(${_helper_target} PROPERTIES
MACOSX_BUNDLE_INFO_PLIST ${_helper_info_plist}
OUTPUT_NAME ${_helper_output_name}
C_STANDARD 11
C_STANDARD_REQUIRED ON
XCODE_ATTRIBUTE_GCC_C_LANGUAGE_STANDARD "c11"
)
# Add the Helper as a dependency of the main executable target.
add_dependencies(${CEF_TARGET} "${_helper_target}")
# Copy the Helper app bundle into the Frameworks directory.
add_custom_command(
TARGET ${CEF_TARGET}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${CEF_TARGET_OUT_DIR}/${_helper_output_name}.app"
"${CEF_APP}/Contents/Frameworks/${_helper_output_name}.app"
VERBATIM
)
endforeach()
# Manually process and copy over resource files.
# The Xcode generator can support this via the set_target_properties RESOURCE
# directive but that doesn't properly handle nested resource directories.
# Remove these prefixes from input file paths.
set(PREFIXES "mac/")
COPY_MAC_RESOURCES("${CEFSIMPLE_CAPI_RESOURCES_SRCS}" "${PREFIXES}" "${CEF_TARGET}" "${CMAKE_CURRENT_SOURCE_DIR}" "${CEF_APP}")
endif()
#
# Windows configuration.
#
if(OS_WINDOWS)
if(USE_SANDBOX)
# Shared library (DLL) target.
add_library(${CEF_TARGET} SHARED ${CEFSIMPLE_CAPI_SRCS})
SET_LIBRARY_TARGET_PROPERTIES(${CEF_TARGET})
set_target_properties(${CEF_TARGET} PROPERTIES
C_STANDARD 11
C_STANDARD_REQUIRED ON
)
# Copy and rename the bootstrap binary.
COPY_SINGLE_FILE(${CEF_TARGET} ${CEF_BINARY_DIR}/bootstrap.exe ${CEF_TARGET_OUT_DIR}/${CEF_TARGET}.exe)
if (CMAKE_GENERATOR MATCHES "Visual Studio")
# Make the bootstrap binary the default command for the Visual Studio debugger.
set_target_properties(${CEF_TARGET} PROPERTIES
VS_DEBUGGER_COMMAND "$<TARGET_FILE_DIR:${CEF_TARGET}>/${CEF_TARGET}.exe"
)
endif()
else()
# Executable target.
add_executable(${CEF_TARGET} WIN32 ${CEFSIMPLE_CAPI_SRCS})
SET_EXECUTABLE_TARGET_PROPERTIES(${CEF_TARGET})
set_target_properties(${CEF_TARGET} PROPERTIES
C_STANDARD 11
C_STANDARD_REQUIRED ON
)
# Add the custom manifest files to the executable.
ADD_WINDOWS_MANIFEST("${CMAKE_CURRENT_SOURCE_DIR}/win" "${CEF_TARGET}" "exe")
endif()
add_dependencies(${CEF_TARGET} libcef_dll_wrapper)
target_link_libraries(${CEF_TARGET} libcef_lib libcef_dll_wrapper ${CEF_STANDARD_LIBS})
# Enable C11 atomics support (required for stdatomic.h in MSVC).
# Must use set_property instead of CEF_C_COMPILER_FLAGS because the Visual Studio
# generator doesn't properly handle this flag when passed through generator expressions.
if(CMAKE_GENERATOR MATCHES "Visual Studio")
set_property(TARGET ${CEF_TARGET} APPEND PROPERTY COMPILE_OPTIONS "/experimental:c11atomics")
endif()
# Copy binary and resource files to the target output directory.
COPY_FILES("${CEF_TARGET}" "${CEF_BINARY_FILES}" "${CEF_BINARY_DIR}" "${CEF_TARGET_OUT_DIR}")
COPY_FILES("${CEF_TARGET}" "${CEF_RESOURCE_FILES}" "${CEF_RESOURCE_DIR}" "${CEF_TARGET_OUT_DIR}")
if(USE_SANDBOX)
# Set LPAC ACLs required for Windows sandbox support.
SET_LPAC_ACLS("${CEF_TARGET}")
endif()
endif()
+114
View File
@@ -0,0 +1,114 @@
# 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:**
```
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](https://bitbucket.org/chromiumembedded/cef/wiki/Tutorial.md)** - Start here to understand CEF architecture and concepts using the C++ cefsimple example
2. **[Using the C API](https://bitbucket.org/chromiumembedded/cef/wiki/UsingTheCAPI.md)** - 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 [UsingTheCAPI.md](https://bitbucket.org/chromiumembedded/cef/wiki/UsingTheCAPI.md) 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 [UsingTheCAPI.md](https://bitbucket.org/chromiumembedded/cef/wiki/UsingTheCAPI.md) for detailed explanations, complete code examples, and reference counting patterns.**
## References
- [CEF Tutorial](https://bitbucket.org/chromiumembedded/cef/wiki/Tutorial.md) - Architecture overview (uses C++ examples)
- [CEF C API Documentation](https://bitbucket.org/chromiumembedded/cef/wiki/UsingTheCAPI.md) - C-specific patterns
- [Original C++ cefsimple](../cefsimple/)
+95
View File
@@ -0,0 +1,95 @@
// Copyright (c) 2025 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#include "tests/cefsimple_capi/simple_app.h"
#if defined(CEF_X11)
#include <X11/Xlib.h>
#endif
#include "include/capi/cef_app_capi.h"
#include "include/cef_api_hash.h"
#include "tests/cefsimple_capi/simple_utils.h"
#if defined(CEF_X11)
static int XErrorHandlerImpl(Display* display, XErrorEvent* event) {
// X error handler - just return to avoid terminating the application.
return 0;
}
static int XIOErrorHandlerImpl(Display* display) {
return 0;
}
#endif // defined(CEF_X11)
// Entry point function for all processes.
int main(int argc, char* argv[]) {
// Configure the CEF API version. This must be called before any other CEF
// API functions.
cef_api_hash(CEF_API_VERSION, 0);
// Provide CEF with command-line arguments.
cef_main_args_t main_args = {};
main_args.argc = argc;
main_args.argv = argv;
// Create the application instance.
simple_app_t* app = simple_app_create();
CHECK(app);
// Add reference before cef_execute_process. Both cef_execute_process and
// cef_initialize will take ownership of a reference, so we need 2 total.
app->app.base.add_ref(&app->app.base);
// 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 = cef_execute_process(&main_args, &app->app, NULL);
if (exit_code >= 0) {
// The sub-process has completed so return here.
// cef_execute_process took ownership of one reference.
// Release only the additional reference we added.
app->app.base.release(&app->app.base);
return exit_code;
}
#if defined(CEF_X11)
// Install xlib error handlers so that the application won't be terminated
// on non-fatal errors.
XSetErrorHandler(XErrorHandlerImpl);
XSetIOErrorHandler(XIOErrorHandlerImpl);
#endif
// Specify CEF global settings here.
cef_settings_t settings = {};
settings.size = sizeof(cef_settings_t);
// When generating projects with CMake the CEF_USE_SANDBOX value will be defined
// automatically. Pass -DUSE_SANDBOX=OFF to the CMake command-line to disable
// use of the sandbox.
#if !defined(CEF_USE_SANDBOX)
settings.no_sandbox = 1;
#endif
// Initialize the CEF browser process. May return false if initialization
// fails or if early exit is desired (for example, due to process singleton
// relaunch behavior).
if (!cef_initialize(&main_args, &settings, &app->app, NULL)) {
app->app.base.release(&app->app.base);
return cef_get_exit_code();
}
// Run the CEF message loop. This will block until cef_quit_message_loop() is
// called.
cef_run_message_loop();
// Shut down CEF.
cef_shutdown();
// Note: We DON'T release the app here.
// We transferred our creation reference to CEF via cef_initialize.
// CEF will release it during cef_shutdown().
return 0;
}
+179
View File
@@ -0,0 +1,179 @@
// Copyright (c) 2025 The Chromium Embedded Framework Authors.
// Portions copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import <Cocoa/Cocoa.h>
#include "include/capi/cef_app_capi.h"
#include "include/cef_api_hash.h"
#include "include/cef_application_mac.h"
#include "include/wrapper/cef_library_loader.h"
#include "tests/cefsimple_capi/simple_app.h"
#include "tests/cefsimple_capi/simple_handler.h"
#include "tests/cefsimple_capi/simple_utils.h"
// Receives notifications from the application.
@interface SimpleAppDelegate : NSObject <NSApplicationDelegate>
- (void)createApplication:(id)object;
- (void)tryToTerminateApplication:(NSApplication*)app;
@end
// Provide the CefAppProtocol implementation required by CEF.
@interface SimpleApplication : NSApplication <CefAppProtocol> {
@private
BOOL handlingSendEvent_;
}
@end
@implementation SimpleApplication
- (BOOL)isHandlingSendEvent {
return handlingSendEvent_;
}
- (void)setHandlingSendEvent:(BOOL)handlingSendEvent {
handlingSendEvent_ = handlingSendEvent;
}
- (void)sendEvent:(NSEvent*)event {
BOOL wasHandlingSendEvent = [self isHandlingSendEvent];
[self setHandlingSendEvent:YES];
[super sendEvent:event];
[self setHandlingSendEvent:wasHandlingSendEvent];
}
- (void)terminate:(id)sender {
SimpleAppDelegate* delegate = (SimpleAppDelegate*)[NSApp delegate];
[delegate tryToTerminateApplication:self];
// Return, don't exit. The application is responsible for exiting on its own.
}
@end
@implementation SimpleAppDelegate
// Create the application on the UI thread.
- (void)createApplication:(id)object {
[[NSBundle mainBundle] loadNibNamed:@"MainMenu"
owner:NSApp
topLevelObjects:nil];
// Set the delegate for application events.
[[NSApplication sharedApplication] setDelegate:self];
}
- (void)tryToTerminateApplication:(NSApplication*)app {
simple_handler_t* handler = simple_handler_get_instance();
if (handler && !handler->is_closing) {
simple_handler_close_all_browsers(handler, 0);
}
}
- (NSApplicationTerminateReply)applicationShouldTerminate:
(NSApplication*)sender {
return NSTerminateNow;
}
// Called when the user clicks the app dock icon while the application is
// already running.
- (BOOL)applicationShouldHandleReopen:(NSApplication*)theApplication
hasVisibleWindows:(BOOL)flag {
simple_handler_t* handler = simple_handler_get_instance();
if (handler && !handler->is_closing) {
simple_handler_show_main_window(handler);
}
return NO;
}
// Requests that any state restoration archive be created with secure encoding
// (macOS 12+ only). See https://crrev.com/c737387656 for details. This also
// fixes an issue with macOS default behavior incorrectly restoring windows
// after hard reset (holding down the power button).
- (BOOL)applicationSupportsSecureRestorableState:(NSApplication*)app {
return YES;
}
@end
// Entry point function for the browser process.
int main(int argc, char* argv[]) {
// Load the CEF framework library at runtime instead of linking directly
// as required by the macOS sandbox implementation.
void* library_loader = cef_scoped_library_loader_create(/*helper=*/0);
if (!library_loader) {
return 1;
}
// Configure the CEF API version. This must be called before any other CEF
// API functions.
cef_api_hash(CEF_API_VERSION, 0);
// Provide CEF with command-line arguments.
cef_main_args_t main_args = {};
main_args.argc = argc;
main_args.argv = argv;
@autoreleasepool {
// Initialize the SimpleApplication instance.
[SimpleApplication sharedApplication];
// If there was an invocation to NSApp prior to this method, then the NSApp
// will not be a SimpleApplication, but will instead be an NSApplication.
// This is undesirable and we must enforce that this doesn't happen.
if (![NSApp isKindOfClass:[SimpleApplication class]]) {
return 1;
}
// Create the application instance.
simple_app_t* app = simple_app_create();
CHECK(app);
// Specify CEF global settings here.
cef_settings_t settings = {};
settings.size = sizeof(cef_settings_t);
// When generating projects with CMake the CEF_USE_SANDBOX value will be
// defined automatically. Pass -DUSE_SANDBOX=OFF to the CMake command-line
// to disable use of the sandbox.
#if !defined(CEF_USE_SANDBOX)
settings.no_sandbox = 1;
#endif
// Initialize the CEF browser process. May return false if initialization
// fails or if early exit is desired (for example, due to process singleton
// relaunch behavior).
if (!cef_initialize(&main_args, &settings, &app->app, NULL)) {
app->app.base.release(&app->app.base);
return cef_get_exit_code();
}
// Create the application delegate.
SimpleAppDelegate* delegate = [[SimpleAppDelegate alloc] init];
// Set as the delegate for application events.
NSApp.delegate = delegate;
[delegate performSelectorOnMainThread:@selector(createApplication:)
withObject:nil
waitUntilDone:NO];
// Run the CEF message loop. This will block until cef_quit_message_loop()
// is called.
cef_run_message_loop();
// Shut down CEF.
cef_shutdown();
// Release the delegate.
#if !__has_feature(objc_arc)
[delegate release];
#endif // !__has_feature(objc_arc)
delegate = nil;
// Note: We DON'T release the app here.
// We transferred our creation reference to CEF via cef_initialize.
// CEF will release it during cef_shutdown().
} // @autoreleasepool
// Unload the CEF framework library.
cef_scoped_library_loader_free(library_loader);
return 0;
}
+134
View File
@@ -0,0 +1,134 @@
// Copyright (c) 2025 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#include <windows.h>
#include "include/capi/cef_app_capi.h"
#include "include/cef_api_hash.h"
#include "include/cef_sandbox_win.h"
#include "tests/cefsimple_capi/simple_app.h"
#include "tests/cefsimple_capi/simple_utils.h"
static int RunMain(HINSTANCE hInstance,
LPTSTR lpCmdLine,
int nCmdShow,
void* sandbox_info) {
int exit_code;
// Configure the CEF API version. This must be called before any other CEF
// API functions.
cef_api_hash(CEF_API_VERSION, 0);
// Provide CEF with command-line arguments.
cef_main_args_t main_args = {};
main_args.instance = hInstance;
// Create the application instance.
simple_app_t* app = simple_app_create();
CHECK(app);
// Add reference before cef_execute_process. Both cef_execute_process and
// cef_initialize will take ownership of a reference, so we need 2 total.
app->app.base.add_ref(&app->app.base);
// 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 = cef_execute_process(&main_args, &app->app, sandbox_info);
if (exit_code >= 0) {
// The sub-process has completed so return here.
// cef_execute_process took ownership of one reference.
// Release only the additional reference we added.
app->app.base.release(&app->app.base);
return exit_code;
}
// Specify CEF global settings here.
cef_settings_t settings = {};
settings.size = sizeof(cef_settings_t);
if (!sandbox_info) {
settings.no_sandbox = 1;
}
// Initialize the CEF browser process. May return false if initialization
// fails or if early exit is desired (for example, due to process singleton
// relaunch behavior).
if (!cef_initialize(&main_args, &settings, &app->app, sandbox_info)) {
app->app.base.release(&app->app.base);
return cef_get_exit_code();
}
// Run the CEF message loop. This will block until cef_quit_message_loop() is
// called.
cef_run_message_loop();
// Shut down CEF.
cef_shutdown();
// Note: We DON'T release the app here.
// We transferred our creation reference to CEF via cef_initialize.
// CEF will release it during cef_shutdown().
return 0;
}
#if defined(CEF_USE_BOOTSTRAP)
// Entry point called by bootstrap.exe when built as a DLL.
CEF_BOOTSTRAP_EXPORT int RunWinMain(HINSTANCE hInstance,
LPTSTR lpCmdLine,
int nCmdShow,
void* sandbox_info,
cef_version_info_t* version_info) {
(void)version_info; // Unused parameter
return RunMain(hInstance, lpCmdLine, nCmdShow, sandbox_info);
}
#else // !defined(CEF_USE_BOOTSTRAP)
// Entry point function for all processes.
int APIENTRY wWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow) {
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
#if defined(ARCH_CPU_32_BITS)
// Run the main thread on 32-bit Windows using a fiber with the preferred 4MiB
// stack size. This function must be called at the top of the executable entry
// point function (`main()` or `wWinMain()`). It is used in combination with
// the initial stack size of 0.5MiB configured via the `/STACK:0x80000` linker
// flag on executable targets. This saves significant memory on threads (like
// those in the Windows thread pool, and others) whose stack size can only be
// controlled via the linker flag.
int exit_code = cef_run_winmain_with_preferred_stack_size(
wWinMain, hInstance, lpCmdLine, nCmdShow);
if (exit_code >= 0) {
// The fiber has completed so return here.
return exit_code;
}
#endif
void* sandbox_info = NULL;
#if defined(CEF_USE_SANDBOX)
// Manage the life span of the sandbox information object. This is necessary
// for sandbox support on Windows. See cef_sandbox_win.h for complete details.
sandbox_info = cef_sandbox_info_create();
#endif
int result = RunMain(hInstance, lpCmdLine, nCmdShow, sandbox_info);
#if defined(CEF_USE_SANDBOX)
if (sandbox_info) {
cef_sandbox_info_destroy(sandbox_info);
}
#endif
return result;
}
#endif // !defined(CEF_USE_BOOTSTRAP)
@@ -0,0 +1,3 @@
/* Localized versions of Info.plist keys */
NSHumanReadableCopyright = "© Chromium Embedded Framework Authors, 2013";
@@ -0,0 +1,330 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10117" systemVersion="16B2657" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
<dependencies>
<deployment version="1090" identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10117"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication"/>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application"/>
<menu title="AMainMenu" systemMenu="main" id="29" userLabel="MainMenu">
<items>
<menuItem title="cefsimple" id="56">
<menu key="submenu" title="TestShell" systemMenu="apple" id="57">
<items>
<menuItem title="About cefsimple" id="58">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontStandardAboutPanel:" target="-2" id="142"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="236">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Preferences…" keyEquivalent="," id="129" userLabel="121"/>
<menuItem isSeparatorItem="YES" id="143">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Services" id="131">
<menu key="submenu" title="Services" systemMenu="services" id="130"/>
</menuItem>
<menuItem isSeparatorItem="YES" id="144">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Hide cefsimple" keyEquivalent="h" id="134">
<connections>
<action selector="hide:" target="-1" id="367"/>
</connections>
</menuItem>
<menuItem title="Hide Others" keyEquivalent="h" id="145">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="hideOtherApplications:" target="-1" id="368"/>
</connections>
</menuItem>
<menuItem title="Show All" id="150">
<connections>
<action selector="unhideAllApplications:" target="-1" id="370"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="149">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Quit cefsimple" keyEquivalent="q" id="136" userLabel="1111">
<connections>
<action selector="terminate:" target="-1" id="369"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="File" id="83">
<menu key="submenu" title="File" id="81">
<items>
<menuItem title="New" keyEquivalent="n" id="82" userLabel="9">
<connections>
<action selector="newDocument:" target="-1" id="373"/>
</connections>
</menuItem>
<menuItem title="Open…" keyEquivalent="o" id="72">
<connections>
<action selector="openDocument:" target="-1" id="374"/>
</connections>
</menuItem>
<menuItem title="Open Recent" id="124">
<menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="125">
<items>
<menuItem title="Clear Menu" id="126">
<connections>
<action selector="clearRecentDocuments:" target="-1" id="127"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="79" userLabel="7">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Close" keyEquivalent="w" id="73" userLabel="1">
<connections>
<action selector="performClose:" target="-1" id="193"/>
</connections>
</menuItem>
<menuItem title="Save" keyEquivalent="s" id="75" userLabel="3">
<connections>
<action selector="saveDocument:" target="-1" id="362"/>
</connections>
</menuItem>
<menuItem title="Save As…" keyEquivalent="S" id="80" userLabel="8">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="saveDocumentAs:" target="-1" id="363"/>
</connections>
</menuItem>
<menuItem title="Revert to Saved" id="112" userLabel="10">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="revertDocumentToSaved:" target="-1" id="364"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="74" userLabel="2">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Page Setup..." keyEquivalent="P" id="77" userLabel="5">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="runPageLayout:" target="-1" id="87"/>
</connections>
</menuItem>
<menuItem title="Print…" keyEquivalent="p" id="78" userLabel="6">
<connections>
<action selector="print:" target="-1" id="86"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Edit" id="217">
<menu key="submenu" title="Edit" id="205">
<items>
<menuItem title="Undo" keyEquivalent="z" id="207">
<connections>
<action selector="undo:" target="-1" id="223"/>
</connections>
</menuItem>
<menuItem title="Redo" keyEquivalent="Z" id="215">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="redo:" target="-1" id="231"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="206">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Cut" keyEquivalent="x" id="199">
<connections>
<action selector="cut:" target="-1" id="228"/>
</connections>
</menuItem>
<menuItem title="Copy" keyEquivalent="c" id="197">
<connections>
<action selector="copy:" target="-1" id="224"/>
</connections>
</menuItem>
<menuItem title="Paste" keyEquivalent="v" id="203">
<connections>
<action selector="paste:" target="-1" id="226"/>
</connections>
</menuItem>
<menuItem title="Delete" id="202">
<connections>
<action selector="delete:" target="-1" id="235"/>
</connections>
</menuItem>
<menuItem title="Select All" keyEquivalent="a" id="198">
<connections>
<action selector="selectAll:" target="-1" id="232"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="214">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Find" id="218">
<menu key="submenu" title="Find" id="220">
<items>
<menuItem title="Find…" tag="1" keyEquivalent="f" id="209">
<connections>
<action selector="performFindPanelAction:" target="-1" id="241"/>
</connections>
</menuItem>
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="208"/>
<menuItem title="Find Previous" tag="3" keyEquivalent="G" id="213">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
</menuItem>
<menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="221"/>
<menuItem title="Jump to Selection" keyEquivalent="j" id="210">
<connections>
<action selector="centerSelectionInVisibleArea:" target="-1" id="245"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Spelling and Grammar" id="216">
<menu key="submenu" title="Spelling and Grammar" id="200">
<items>
<menuItem title="Show Spelling…" keyEquivalent=":" id="204">
<connections>
<action selector="showGuessPanel:" target="-1" id="230"/>
</connections>
</menuItem>
<menuItem title="Check Spelling" keyEquivalent=";" id="201">
<connections>
<action selector="checkSpelling:" target="-1" id="225"/>
</connections>
</menuItem>
<menuItem title="Check Spelling While Typing" id="219">
<connections>
<action selector="toggleContinuousSpellChecking:" target="-1" id="222"/>
</connections>
</menuItem>
<menuItem title="Check Grammar With Spelling" id="346">
<connections>
<action selector="toggleGrammarChecking:" target="-1" id="347"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Substitutions" id="348">
<menu key="submenu" title="Substitutions" id="349">
<items>
<menuItem title="Smart Copy/Paste" tag="1" keyEquivalent="f" id="350">
<connections>
<action selector="toggleSmartInsertDelete:" target="-1" id="355"/>
</connections>
</menuItem>
<menuItem title="Smart Quotes" tag="2" keyEquivalent="g" id="351">
<connections>
<action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="356"/>
</connections>
</menuItem>
<menuItem title="Smart Links" tag="3" keyEquivalent="G" id="354">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="toggleAutomaticLinkDetection:" target="-1" id="357"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Speech" id="211">
<menu key="submenu" title="Speech" id="212">
<items>
<menuItem title="Start Speaking" id="196">
<connections>
<action selector="startSpeaking:" target="-1" id="233"/>
</connections>
</menuItem>
<menuItem title="Stop Speaking" id="195">
<connections>
<action selector="stopSpeaking:" target="-1" id="227"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Format" id="299">
<menu key="submenu" title="Format" id="300">
<items>
<menuItem title="Show Fonts" keyEquivalent="t" id="344"/>
<menuItem title="Show Colors" keyEquivalent="C" id="345">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="orderFrontColorPanel:" target="-1" id="361"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="View" id="295">
<menu key="submenu" title="View" id="296">
<items>
<menuItem title="Show Toolbar" keyEquivalent="t" id="297">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="toggleToolbarShown:" target="-1" id="366"/>
</connections>
</menuItem>
<menuItem title="Customize Toolbar…" id="298">
<connections>
<action selector="runToolbarCustomizationPalette:" target="-1" id="365"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Window" id="19">
<menu key="submenu" title="Window" systemMenu="window" id="24">
<items>
<menuItem title="Minimize" keyEquivalent="m" id="23">
<connections>
<action selector="performMiniaturize:" target="-1" id="37"/>
</connections>
</menuItem>
<menuItem title="Zoom" id="239">
<connections>
<action selector="performZoom:" target="-1" id="240"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="92">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Bring All to Front" id="5">
<connections>
<action selector="arrangeInFront:" target="-1" id="39"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Help" id="103" userLabel="1">
<menu key="submenu" title="Help" id="106" userLabel="2">
<items>
<menuItem title="cefsimple Help" keyEquivalent="?" id="111">
<connections>
<action selector="showHelp:" target="-1" id="360"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
<userDefaultsController representsSharedInstance="YES" id="389"/>
</objects>
</document>
+39
View File
@@ -0,0 +1,39 @@
<?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>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string>cefsimple_capi.icns</string>
<key>CFBundleIdentifier</key>
<string>org.cef.cefsimple_capi</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${VERSION_SHORT}</string>
<key>CFBundleShortVersionString</key>
<string>${VERSION_SHORT}</string>
<key>LSEnvironment</key>
<dict>
<key>MallocNanoZone</key>
<string>0</string>
</dict>
<key>LSMinimumSystemVersion</key>
<string>12.0</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSSupportsAutomaticGraphicsSwitching</key>
<true/>
</dict>
</plist>
Binary file not shown.
@@ -0,0 +1,39 @@
<?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>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>org.cef.cefsimple_capi.helper${BUNDLE_ID_SUFFIX}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${VERSION_SHORT}</string>
<key>CFBundleShortVersionString</key>
<string>${VERSION_SHORT}</string>
<key>LSEnvironment</key>
<dict>
<key>MallocNanoZone</key>
<string>0</string>
</dict>
<key>LSFileQuarantineEnabled</key>
<true/>
<key>LSMinimumSystemVersion</key>
<string>12.0</string>
<key>LSUIElement</key>
<string>1</string>
<key>NSSupportsAutomaticGraphicsSwitching</key>
<true/>
</dict>
</plist>
+57
View File
@@ -0,0 +1,57 @@
// Copyright (c) 2025 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that can
// be found in the LICENSE file.
#include "include/capi/cef_app_capi.h"
#include "include/cef_api_hash.h"
#include "include/wrapper/cef_library_loader.h"
// When generating projects with CMake the CEF_USE_SANDBOX value will be defined
// automatically. Pass -DUSE_SANDBOX=OFF to the CMake command-line to disable
// use of the sandbox.
#if defined(CEF_USE_SANDBOX)
#include "include/cef_sandbox_mac.h"
#endif
// Entry point function for sub-processes.
int main(int argc, char* argv[]) {
#if defined(CEF_USE_SANDBOX)
// Initialize the macOS sandbox for this helper process.
void* sandbox_context = cef_scoped_sandbox_initialize(argc, argv);
if (!sandbox_context) {
return 1;
}
#endif
// Load the CEF framework library at runtime instead of linking directly
// as required by the macOS sandbox implementation.
void* library_loader = cef_scoped_library_loader_create(/*helper=*/1);
if (!library_loader) {
#if defined(CEF_USE_SANDBOX)
cef_scoped_sandbox_destroy(sandbox_context);
#endif
return 1;
}
// Configure the CEF API version. This must be called before any other CEF
// API functions.
cef_api_hash(CEF_API_VERSION, 0);
// Provide CEF with command-line arguments.
cef_main_args_t main_args = {};
main_args.argc = argc;
main_args.argv = argv;
// Execute the sub-process.
int result = cef_execute_process(&main_args, NULL, NULL);
// Unload the CEF framework library.
cef_unload_library();
#if defined(CEF_USE_SANDBOX)
// Destroy the sandbox context.
cef_scoped_sandbox_destroy(sandbox_context);
#endif
return result;
}
+85
View File
@@ -0,0 +1,85 @@
// Copyright (c) 2025 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#ifndef CEF_TESTS_CEFSIMPLE_CAPI_REF_COUNTED_H_
#define CEF_TESTS_CEFSIMPLE_CAPI_REF_COUNTED_H_
#include <stdatomic.h>
#include <stdlib.h>
// Generic macros for implementing reference counting on CEF C API structures.
// These macros help reduce boilerplate when implementing add_ref, release,
// has_one_ref, and has_at_least_one_ref functions.
// Macro to implement add_ref for a structure type.
// Usage: IMPLEMENT_ADDREF(my_struct_t, my_struct, ref_count)
#define IMPLEMENT_ADDREF(struct_type, struct_name, ref_field) \
void CEF_CALLBACK struct_name##_add_ref(cef_base_ref_counted_t* self) { \
struct_type* obj = (struct_type*)self; \
atomic_fetch_add(&obj->ref_field, 1); \
}
// Macro to implement has_one_ref for a structure type.
#define IMPLEMENT_HAS_ONE_REF(struct_type, struct_name, ref_field) \
int CEF_CALLBACK struct_name##_has_one_ref(cef_base_ref_counted_t* self) { \
struct_type* obj = (struct_type*)self; \
return atomic_load(&obj->ref_field) == 1; \
}
// Macro to implement has_at_least_one_ref for a structure type.
#define IMPLEMENT_HAS_AT_LEAST_ONE_REF(struct_type, struct_name, ref_field) \
int CEF_CALLBACK struct_name##_has_at_least_one_ref( \
cef_base_ref_counted_t* self) { \
struct_type* obj = (struct_type*)self; \
return atomic_load(&obj->ref_field) >= 1; \
}
// Macro to implement a simple release function that only frees the object.
// Use this for structures that don't own other ref-counted objects.
// For complex cleanup, implement release manually.
// Usage: IMPLEMENT_RELEASE_SIMPLE(my_struct_t, my_struct, ref_count)
#define IMPLEMENT_RELEASE_SIMPLE(struct_type, struct_name, ref_field) \
int CEF_CALLBACK struct_name##_release(cef_base_ref_counted_t* self) { \
struct_type* obj = (struct_type*)self; \
int count = atomic_fetch_sub(&obj->ref_field, 1) - 1; \
if (count == 0) { \
free(obj); \
return 1; \
} \
return 0; \
}
// Implement all ref-counting for structures with no cleanup needed.
// Use when structure only needs free(), no owned objects to release.
// Usage: IMPLEMENT_REFCOUNTING_SIMPLE(my_struct_t, my_struct, ref_count)
#define IMPLEMENT_REFCOUNTING_SIMPLE(struct_type, struct_name, ref_field) \
IMPLEMENT_ADDREF(struct_type, struct_name, ref_field) \
IMPLEMENT_RELEASE_SIMPLE(struct_type, struct_name, ref_field) \
IMPLEMENT_HAS_ONE_REF(struct_type, struct_name, ref_field) \
IMPLEMENT_HAS_AT_LEAST_ONE_REF(struct_type, struct_name, ref_field)
// Implement ref-counting WITHOUT release for structures with custom cleanup.
// Use when you need to manually implement release() with custom logic.
// You must manually implement struct_name##_release().
// Usage: IMPLEMENT_REFCOUNTING_MANUAL(my_struct_t, my_struct, ref_count)
#define IMPLEMENT_REFCOUNTING_MANUAL(struct_type, struct_name, ref_field) \
IMPLEMENT_ADDREF(struct_type, struct_name, ref_field) \
IMPLEMENT_HAS_ONE_REF(struct_type, struct_name, ref_field) \
IMPLEMENT_HAS_AT_LEAST_ONE_REF(struct_type, struct_name, ref_field)
// Macro to initialize CEF base ref-counted structure.
// Must be called after allocating the structure.
// Usage: INIT_CEF_BASE_REFCOUNTED(ptr, cef_type_t, struct_name)
// Example: INIT_CEF_BASE_REFCOUNTED(&handler->handler, cef_display_handler_t,
// display_handler)
#define INIT_CEF_BASE_REFCOUNTED(ptr, cef_type, struct_name) \
do { \
(ptr)->size = sizeof(cef_type); \
(ptr)->add_ref = struct_name##_add_ref; \
(ptr)->release = struct_name##_release; \
(ptr)->has_one_ref = struct_name##_has_one_ref; \
(ptr)->has_at_least_one_ref = struct_name##_has_at_least_one_ref; \
} while (0)
#endif // CEF_TESTS_CEFSIMPLE_CAPI_REF_COUNTED_H_
+26
View File
@@ -0,0 +1,26 @@
// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by cefsimple.rc
//
#define IDI_CEFSIMPLE 120
#define IDI_SMALL 121
// Avoid files associated with MacOS
#define _X86_
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NO_MFC 1
#define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NEXT_COMMAND_VALUE 32700
#define _APS_NEXT_CONTROL_VALUE 1000
#define _APS_NEXT_SYMED_VALUE 102
#endif
#endif
+290
View File
@@ -0,0 +1,290 @@
// Copyright (c) 2025 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#include "tests/cefsimple_capi/simple_app.h"
#include <stdatomic.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include "include/capi/cef_browser_capi.h"
#include "include/capi/cef_command_line_capi.h"
#include "include/capi/views/cef_browser_view_capi.h"
#include "include/capi/views/cef_window_capi.h"
#include "tests/cefsimple_capi/ref_counted.h"
#include "tests/cefsimple_capi/simple_handler.h"
#include "tests/cefsimple_capi/simple_utils.h"
#include "tests/cefsimple_capi/simple_views.h"
// Implement reference counting functions for simple_app_t.
IMPLEMENT_REFCOUNTING_MANUAL(simple_app_t, simple_app, ref_count)
// Release function for simple_app_t with custom cleanup logic.
int CEF_CALLBACK simple_app_release(cef_base_ref_counted_t* self) {
simple_app_t* app = (simple_app_t*)self;
int count = atomic_fetch_sub(&app->ref_count, 1) - 1;
if (count == 0) {
// Release the browser process handler if we own one.
if (app->browser_process_handler) {
app->browser_process_handler->handler.base.release(
&app->browser_process_handler->handler.base);
}
free(app);
return 1;
}
return 0;
}
// Returns the browser process handler.
// Adds a reference before returning (CEF will release it when done).
cef_browser_process_handler_t* CEF_CALLBACK
simple_app_get_browser_process_handler(cef_app_t* self) {
simple_app_t* app = (simple_app_t*)self;
if (app->browser_process_handler) {
// Add reference for CEF (it will release when done).
app->browser_process_handler->handler.base.add_ref(
&app->browser_process_handler->handler.base);
return &app->browser_process_handler->handler;
}
return NULL;
}
// Forward declarations for browser process handler functions.
void CEF_CALLBACK browser_process_handler_on_context_initialized(
cef_browser_process_handler_t* self);
cef_client_t* CEF_CALLBACK
browser_process_handler_get_default_client(cef_browser_process_handler_t* self);
// Implement reference counting functions for browser process handler.
IMPLEMENT_REFCOUNTING_SIMPLE(simple_browser_process_handler_t,
browser_process_handler,
ref_count)
// Called after CEF initialization to create the browser.
void CEF_CALLBACK browser_process_handler_on_context_initialized(
cef_browser_process_handler_t* self) {
// Get the global command line.
cef_command_line_t* command_line = cef_command_line_get_global();
CHECK(command_line);
// Check if Alloy style will be used.
cef_string_t alloy_switch = {};
cef_string_from_ascii("use-alloy-style", 15, &alloy_switch);
int use_alloy_style = command_line->has_switch(command_line, &alloy_switch);
cef_string_clear(&alloy_switch);
// Create the client handler.
simple_handler_t* client_handler = simple_handler_create(use_alloy_style);
CHECK(client_handler);
// The client_handler is stored globally via simple_handler_get_instance().
// GetDefaultClient() will retrieve it from there when needed.
// Specify CEF browser settings.
cef_browser_settings_t browser_settings = {};
browser_settings.size = sizeof(cef_browser_settings_t);
// Get the URL from command line or use default.
cef_string_t url_switch = {};
cef_string_from_ascii("url", 3, &url_switch);
cef_string_userfree_t url_value =
command_line->get_switch_value(command_line, &url_switch);
cef_string_clear(&url_switch);
cef_string_t url = {};
if (url_value && url_value->length > 0) {
cef_string_copy(url_value->str, url_value->length, &url);
} else {
cef_string_from_ascii("https://www.google.com", 22, &url);
}
if (url_value) {
cef_string_userfree_free(url_value);
}
// Check if Views framework should be used.
// Views is enabled by default (add `--use-native` to disable).
cef_string_t native_switch = {};
cef_string_from_ascii("use-native", 10, &native_switch);
int use_native = command_line->has_switch(command_line, &native_switch);
cef_string_clear(&native_switch);
int use_views = !use_native;
// Determine runtime style.
cef_runtime_style_t runtime_style = CEF_RUNTIME_STYLE_DEFAULT;
if (use_alloy_style) {
runtime_style = CEF_RUNTIME_STYLE_ALLOY;
}
if (use_views) {
// Create the BrowserView using Views framework.
simple_browser_view_delegate_t* browser_view_delegate =
browser_view_delegate_create(runtime_style);
CHECK(browser_view_delegate);
// Create the browser view.
// We transfer our client_handler and browser_view_delegate references to
// CEF. CEF will release them when the browser view is destroyed.
cef_browser_view_t* browser_view = cef_browser_view_create(
&client_handler->client, &url, &browser_settings, NULL, NULL,
&browser_view_delegate->delegate);
// Note: We DON'T release browser_view_delegate here - we transferred
// ownership to CEF.
if (browser_view) {
// Optionally configure the initial show state.
cef_show_state_t initial_show_state = CEF_SHOW_STATE_NORMAL;
cef_string_t show_state_switch = {};
cef_string_from_ascii("initial-show-state", 18, &show_state_switch);
cef_string_userfree_t show_state_value =
command_line->get_switch_value(command_line, &show_state_switch);
cef_string_clear(&show_state_switch);
if (show_state_value && show_state_value->length > 0) {
// Check for "minimized"
cef_string_t minimized = {};
cef_string_from_ascii("minimized", 9, &minimized);
if (cef_string_utf16_cmp(show_state_value, &minimized) == 0) {
initial_show_state = CEF_SHOW_STATE_MINIMIZED;
}
cef_string_clear(&minimized);
// Check for "maximized"
cef_string_t maximized = {};
cef_string_from_ascii("maximized", 9, &maximized);
if (cef_string_utf16_cmp(show_state_value, &maximized) == 0) {
initial_show_state = CEF_SHOW_STATE_MAXIMIZED;
}
cef_string_clear(&maximized);
#if defined(OS_MAC)
// Hidden show state is only supported on macOS.
cef_string_t hidden = {};
cef_string_from_ascii("hidden", 6, &hidden);
if (cef_string_utf16_cmp(show_state_value, &hidden) == 0) {
initial_show_state = CEF_SHOW_STATE_HIDDEN;
}
cef_string_clear(&hidden);
#endif
}
if (show_state_value) {
cef_string_userfree_free(show_state_value);
}
// Create the Window. It will show itself after creation.
// We transfer our browser_view reference to the window delegate.
simple_window_delegate_t* window_delegate = window_delegate_create(
browser_view, runtime_style, initial_show_state);
CHECK(window_delegate);
// Create the window.
// We transfer our window_delegate reference to CEF.
cef_window_create_top_level(&window_delegate->delegate);
}
} else {
// Information used when creating the native window.
cef_window_info_t window_info = {};
window_info.size = sizeof(cef_window_info_t);
#if defined(OS_WIN)
// On Windows we need to specify certain flags that will be passed to
// CreateWindowEx().
cef_string_t window_name = {};
cef_string_from_ascii("cefsimple_capi", 14, &window_name);
window_info.style =
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE;
window_info.bounds.x = CW_USEDEFAULT;
window_info.bounds.y = CW_USEDEFAULT;
window_info.bounds.width = CW_USEDEFAULT;
window_info.bounds.height = CW_USEDEFAULT;
window_info.window_name = window_name;
#elif defined(OS_LINUX)
window_info.bounds.width = 800;
window_info.bounds.height = 600;
#endif
// Use the runtime style determined earlier.
window_info.runtime_style = runtime_style;
// Create the browser window.
// We pass our creation reference to CEF - don't release it!
// CEF takes ownership of this reference and will release it when the
// browser closes.
cef_browser_host_create_browser(&window_info, &client_handler->client, &url,
&browser_settings, NULL, NULL);
#if defined(OS_WIN)
cef_string_clear(&window_name);
#endif
}
cef_string_clear(&url);
// Note: We DON'T release the client_handler here.
// We transferred our creation reference to CEF via
// cef_browser_host_create_browser. CEF will release it when the browser
// closes.
}
// Returns the default client handler for Chrome style UI.
cef_client_t* CEF_CALLBACK browser_process_handler_get_default_client(
cef_browser_process_handler_t* self) {
// Return the global instance (matches C++ SimpleApp::GetDefaultClient).
simple_handler_t* instance = simple_handler_get_instance();
if (instance) {
// Add reference before returning (CEF will release it).
instance->client.base.add_ref(&instance->client.base);
return &instance->client;
}
return NULL;
}
// Creates a browser process handler instance.
simple_browser_process_handler_t* browser_process_handler_create(void) {
simple_browser_process_handler_t* handler =
(simple_browser_process_handler_t*)calloc(
1, sizeof(simple_browser_process_handler_t));
CHECK(handler);
// Initialize base structure.
INIT_CEF_BASE_REFCOUNTED(&handler->handler.base,
cef_browser_process_handler_t,
browser_process_handler);
// Set callbacks.
handler->handler.on_context_initialized =
browser_process_handler_on_context_initialized;
handler->handler.get_default_client =
browser_process_handler_get_default_client;
// Initialize with ref count of 1.
atomic_store(&handler->ref_count, 1);
return handler;
}
// Creates the application instance.
simple_app_t* simple_app_create(void) {
simple_app_t* app = (simple_app_t*)calloc(1, sizeof(simple_app_t));
CHECK(app);
// Initialize base structure.
INIT_CEF_BASE_REFCOUNTED(&app->app.base, cef_app_t, simple_app);
// Set callbacks.
app->app.get_browser_process_handler = simple_app_get_browser_process_handler;
// Create the browser process handler.
app->browser_process_handler = browser_process_handler_create();
CHECK(app->browser_process_handler);
// Initialize with ref count of 1.
atomic_store(&app->ref_count, 1);
return app;
}
+50
View File
@@ -0,0 +1,50 @@
// Copyright (c) 2025 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#ifndef CEF_TESTS_CEFSIMPLE_CAPI_SIMPLE_APP_H_
#define CEF_TESTS_CEFSIMPLE_CAPI_SIMPLE_APP_H_
#include <stdatomic.h>
#include "include/capi/cef_app_capi.h"
#include "include/capi/cef_browser_process_handler_capi.h"
// Forward declaration.
typedef struct _simple_browser_process_handler_t
simple_browser_process_handler_t;
typedef struct _simple_handler_t simple_handler_t;
// Application handler structure.
// Implements cef_app_t interface.
typedef struct _simple_app_t {
// MUST be first member - CEF base structure.
cef_app_t app;
// Reference count for this object.
atomic_int ref_count;
// Browser process handler (owned by this structure).
simple_browser_process_handler_t* browser_process_handler;
} simple_app_t;
// Browser process handler structure.
// Implements cef_browser_process_handler_t interface.
typedef struct _simple_browser_process_handler_t {
// MUST be first member - CEF base structure.
cef_browser_process_handler_t handler;
// Reference count for this object.
atomic_int ref_count;
// Note: We don't store a client reference here.
// GetDefaultClient() uses simple_handler_get_instance() to get the global
// instance.
} simple_browser_process_handler_t;
// Create a new application handler instance.
// Returns a pointer with ref count of 1.
// Caller is responsible for releasing the reference when done.
simple_app_t* simple_app_create(void);
#endif // CEF_TESTS_CEFSIMPLE_CAPI_SIMPLE_APP_H_
@@ -0,0 +1,96 @@
// Copyright (c) 2025 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#include "tests/cefsimple_capi/simple_browser_list.h"
#include <stdlib.h>
#include <string.h>
#include "tests/cefsimple_capi/simple_utils.h"
void browser_list_init(browser_list_t* list) {
CHECK(list);
list->browsers = NULL;
list->count = 0;
list->capacity = 0;
}
int browser_list_add(browser_list_t* list, cef_browser_t* browser) {
CHECK(list);
CHECK(browser);
// Ensure capacity.
if (list->count >= list->capacity) {
size_t new_capacity = list->capacity == 0 ? 4 : list->capacity * 2;
cef_browser_t** new_list = (cef_browser_t**)realloc(
list->browsers, new_capacity * sizeof(cef_browser_t*));
if (!new_list) {
return 0; // Allocation failed.
}
list->browsers = new_list;
list->capacity = new_capacity;
}
// Add a reference for the list.
// The list now owns one reference to this browser.
browser->base.add_ref(&browser->base);
// Store the browser pointer.
list->browsers[list->count++] = browser;
return 1;
}
void browser_list_remove(browser_list_t* list, cef_browser_t* browser) {
CHECK(list);
CHECK(browser);
// Find and remove the browser.
for (size_t i = 0; i < list->count; ++i) {
// Add a reference before calling is_same, since CEF functions take
// ownership of parameters passed to them.
browser->base.add_ref(&browser->base);
if (list->browsers[i]->is_same(list->browsers[i], browser)) {
// Release the list's reference to this browser.
list->browsers[i]->base.release(&list->browsers[i]->base);
// Shift remaining elements.
for (size_t j = i; j < list->count - 1; ++j) {
list->browsers[j] = list->browsers[j + 1];
}
list->count--;
break;
}
}
}
void browser_list_destroy(browser_list_t* list) {
CHECK(list);
// Release all browsers in the list.
for (size_t i = 0; i < list->count; ++i) {
list->browsers[i]->base.release(&list->browsers[i]->base);
}
// Free the array.
free(list->browsers);
// Reset to empty state.
list->browsers = NULL;
list->count = 0;
list->capacity = 0;
}
size_t browser_list_count(const browser_list_t* list) {
CHECK(list);
return list->count;
}
cef_browser_t* browser_list_get(const browser_list_t* list, size_t index) {
CHECK(list);
if (index >= list->count) {
return NULL;
}
return list->browsers[index];
}
@@ -0,0 +1,62 @@
// Copyright (c) 2025 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#ifndef CEF_TESTS_CEFSIMPLE_CAPI_SIMPLE_BROWSER_LIST_H_
#define CEF_TESTS_CEFSIMPLE_CAPI_SIMPLE_BROWSER_LIST_H_
#include <stddef.h>
#include "include/capi/cef_browser_capi.h"
// Dynamic list of browser instances with automatic reference counting.
//
// THREAD SAFETY:
// - NOT thread-safe. All methods must be called on the CEF UI thread.
//
// REF-COUNTING RULES:
//
// browser_list_add():
// - Takes a browser pointer and ADDS a reference (list takes ownership)
// - Ref count change: +1 (one new reference owned by the list)
// - Caller must still release their own reference if they have one
//
// browser_list_remove():
// - Finds the browser in the list and RELEASES the list's reference
// - Ref count change: -1 (list gives up ownership)
// - Does NOT modify the browser parameter's reference (caller still owns it)
//
// browser_list_destroy():
// - Releases ALL browser references owned by the list
// - Frees the internal array
//
typedef struct {
cef_browser_t** browsers;
size_t count;
size_t capacity;
} browser_list_t;
// Initialize an empty browser list.
void browser_list_init(browser_list_t* list);
// Add a browser to the list.
// Adds a reference - the list takes ownership of one reference.
int browser_list_add(browser_list_t* list, cef_browser_t* browser);
// Remove a browser from the list.
// Releases the list's reference - does not affect the caller's reference.
void browser_list_remove(browser_list_t* list, cef_browser_t* browser);
// Destroy the browser list.
// Releases all browser references and frees internal memory.
void browser_list_destroy(browser_list_t* list);
// Get the number of browsers in the list.
size_t browser_list_count(const browser_list_t* list);
// Get a browser by index.
// Does NOT add a reference.
// Returns NULL if index is out of bounds.
cef_browser_t* browser_list_get(const browser_list_t* list, size_t index);
#endif // CEF_TESTS_CEFSIMPLE_CAPI_SIMPLE_BROWSER_LIST_H_
@@ -0,0 +1,53 @@
// Copyright (c) 2025 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#include <stdatomic.h>
#include <stdlib.h>
#include "tests/cefsimple_capi/ref_counted.h"
#include "tests/cefsimple_capi/simple_handler.h"
#include "tests/cefsimple_capi/simple_utils.h"
//
// Display handler implementation.
//
IMPLEMENT_REFCOUNTING_SIMPLE(simple_display_handler_t,
display_handler,
ref_count)
void CEF_CALLBACK display_handler_on_title_change(cef_display_handler_t* self,
cef_browser_t* browser,
const cef_string_t* title) {
simple_display_handler_t* handler = (simple_display_handler_t*)self;
// Call platform-specific implementation.
if (handler->parent->is_alloy_style) {
simple_handler_platform_title_change(handler->parent, browser, title);
}
// Release the browser reference (CEF gave us one for this callback).
browser->base.release(&browser->base);
}
simple_display_handler_t* display_handler_create(simple_handler_t* parent) {
simple_display_handler_t* handler =
(simple_display_handler_t*)calloc(1, sizeof(simple_display_handler_t));
CHECK(handler);
// Initialize base structure.
INIT_CEF_BASE_REFCOUNTED(&handler->handler.base, cef_display_handler_t,
display_handler);
// Set callbacks.
handler->handler.on_title_change = display_handler_on_title_change;
// Store parent reference (no ref count - parent owns us).
handler->parent = parent;
// Initialize with ref count of 1.
atomic_store(&handler->ref_count, 1);
return handler;
}
+241
View File
@@ -0,0 +1,241 @@
// Copyright (c) 2025 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#include "tests/cefsimple_capi/simple_handler.h"
#include <stdatomic.h>
#include <stdlib.h>
#include "include/capi/cef_task_capi.h"
#include "tests/cefsimple_capi/ref_counted.h"
#include "tests/cefsimple_capi/simple_browser_list.h"
#include "tests/cefsimple_capi/simple_utils.h"
// Global instance pointer.
static simple_handler_t* g_instance = NULL;
// Forward declarations for handler create functions.
simple_display_handler_t* display_handler_create(simple_handler_t* parent);
simple_life_span_handler_t* life_span_handler_create(simple_handler_t* parent);
simple_load_handler_t* load_handler_create(simple_handler_t* parent);
//
// Client handler implementation.
//
IMPLEMENT_REFCOUNTING_MANUAL(simple_handler_t, simple_handler, ref_count)
int CEF_CALLBACK simple_handler_release(cef_base_ref_counted_t* self) {
simple_handler_t* handler = (simple_handler_t*)self;
int count = atomic_fetch_sub(&handler->ref_count, 1) - 1;
if (count == 0) {
// Release all handlers.
if (handler->display_handler) {
handler->display_handler->handler.base.release(
&handler->display_handler->handler.base);
}
if (handler->life_span_handler) {
handler->life_span_handler->handler.base.release(
&handler->life_span_handler->handler.base);
}
if (handler->load_handler) {
handler->load_handler->handler.base.release(
&handler->load_handler->handler.base);
}
// Destroy the browser list.
browser_list_destroy(&handler->browser_list);
// Clear global instance if this is it.
if (g_instance == handler) {
g_instance = NULL;
}
free(handler);
return 1;
}
return 0;
}
//
// Client handler getter implementations.
//
cef_display_handler_t* CEF_CALLBACK
simple_handler_get_display_handler(cef_client_t* self) {
simple_handler_t* handler = (simple_handler_t*)self;
if (handler->display_handler) {
// Add reference before returning.
handler->display_handler->handler.base.add_ref(
&handler->display_handler->handler.base);
return &handler->display_handler->handler;
}
return NULL;
}
cef_life_span_handler_t* CEF_CALLBACK
simple_handler_get_life_span_handler(cef_client_t* self) {
simple_handler_t* handler = (simple_handler_t*)self;
if (handler->life_span_handler) {
// Add reference before returning.
handler->life_span_handler->handler.base.add_ref(
&handler->life_span_handler->handler.base);
return &handler->life_span_handler->handler;
}
return NULL;
}
cef_load_handler_t* CEF_CALLBACK
simple_handler_get_load_handler(cef_client_t* self) {
simple_handler_t* handler = (simple_handler_t*)self;
if (handler->load_handler) {
// Add reference before returning.
handler->load_handler->handler.base.add_ref(
&handler->load_handler->handler.base);
return &handler->load_handler->handler;
}
return NULL;
}
//
// Public API implementation.
//
simple_handler_t* simple_handler_create(int is_alloy_style) {
simple_handler_t* handler =
(simple_handler_t*)calloc(1, sizeof(simple_handler_t));
CHECK(handler);
// Initialize base structure.
INIT_CEF_BASE_REFCOUNTED(&handler->client.base, cef_client_t, simple_handler);
// Set callbacks.
handler->client.get_display_handler = simple_handler_get_display_handler;
handler->client.get_life_span_handler = simple_handler_get_life_span_handler;
handler->client.get_load_handler = simple_handler_get_load_handler;
// Create sub-handlers.
handler->display_handler = display_handler_create(handler);
CHECK(handler->display_handler);
handler->life_span_handler = life_span_handler_create(handler);
CHECK(handler->life_span_handler);
handler->load_handler = load_handler_create(handler);
CHECK(handler->load_handler);
// Initialize other fields.
handler->is_alloy_style = is_alloy_style;
browser_list_init(&handler->browser_list);
handler->is_closing = 0;
// Initialize with ref count of 1.
atomic_store(&handler->ref_count, 1);
// Set global instance.
if (!g_instance) {
g_instance = handler;
}
return handler;
}
simple_handler_t* simple_handler_get_instance(void) {
return g_instance;
}
// Task for closing browsers on the UI thread.
typedef struct _close_browsers_task_t {
cef_task_t task;
atomic_int ref_count;
simple_handler_t* handler;
int force_close;
} close_browsers_task_t;
IMPLEMENT_REFCOUNTING_MANUAL(close_browsers_task_t,
close_browsers_task,
ref_count)
int CEF_CALLBACK close_browsers_task_release(cef_base_ref_counted_t* self) {
close_browsers_task_t* task = (close_browsers_task_t*)self;
int count = atomic_fetch_sub(&task->ref_count, 1) - 1;
if (count == 0) {
// Don't release handler reference - we don't own it.
free(task);
return 1;
}
return 0;
}
void CEF_CALLBACK close_browsers_task_execute(cef_task_t* self) {
close_browsers_task_t* task = (close_browsers_task_t*)self;
size_t count = browser_list_count(&task->handler->browser_list);
if (count == 0) {
return;
}
// Close all browsers.
for (size_t i = 0; i < count; ++i) {
cef_browser_t* browser = browser_list_get(&task->handler->browser_list, i);
cef_browser_host_t* host = browser->get_host(browser);
if (host) {
host->close_browser(host, task->force_close);
host->base.release(&host->base);
}
}
}
void simple_handler_close_all_browsers(simple_handler_t* handler,
int force_close) {
CHECK(handler);
if (!cef_currently_on(TID_UI)) {
// Execute on the UI thread.
close_browsers_task_t* task =
(close_browsers_task_t*)calloc(1, sizeof(close_browsers_task_t));
CHECK(task);
INIT_CEF_BASE_REFCOUNTED(&task->task.base, cef_task_t, close_browsers_task);
task->task.execute = close_browsers_task_execute;
task->handler = handler;
task->force_close = force_close;
atomic_store(&task->ref_count, 1);
cef_post_task(TID_UI, &task->task);
return;
}
// Already on UI thread, execute directly.
size_t count = browser_list_count(&handler->browser_list);
if (count == 0) {
return;
}
for (size_t i = 0; i < count; ++i) {
cef_browser_t* browser = browser_list_get(&handler->browser_list, i);
cef_browser_host_t* host = browser->get_host(browser);
if (host) {
host->close_browser(host, force_close);
host->base.release(&host->base);
}
}
}
void simple_handler_show_main_window(simple_handler_t* handler) {
CHECK(handler);
if (browser_list_count(&handler->browser_list) == 0) {
return;
}
cef_browser_t* main_browser = browser_list_get(&handler->browser_list, 0);
simple_handler_platform_show_window(handler, main_browser);
}
// Default platform implementations (can be overridden in platform-specific
// files).
#if !defined(OS_MAC)
void simple_handler_platform_show_window(simple_handler_t* handler,
cef_browser_t* browser) {
// Not implemented on this platform.
}
#endif
+112
View File
@@ -0,0 +1,112 @@
// Copyright (c) 2025 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#ifndef CEF_TESTS_CEFSIMPLE_CAPI_SIMPLE_HANDLER_H_
#define CEF_TESTS_CEFSIMPLE_CAPI_SIMPLE_HANDLER_H_
#include <stdatomic.h>
#include "include/capi/cef_browser_capi.h"
#include "include/capi/cef_client_capi.h"
#include "tests/cefsimple_capi/simple_browser_list.h"
// Forward declarations.
typedef struct _simple_display_handler_t simple_display_handler_t;
typedef struct _simple_life_span_handler_t simple_life_span_handler_t;
typedef struct _simple_load_handler_t simple_load_handler_t;
// Client handler structure.
// Implements cef_client_t interface.
typedef struct _simple_handler_t {
// MUST be first member - CEF base structure.
cef_client_t client;
// Reference count for this object.
atomic_int ref_count;
// Handler implementations (owned by this structure).
simple_display_handler_t* display_handler;
simple_life_span_handler_t* life_span_handler;
simple_load_handler_t* load_handler;
// True if this client is Alloy style, otherwise Chrome style.
int is_alloy_style;
// List of existing browser windows.
browser_list_t browser_list;
// Set to true when browsers are closing.
int is_closing;
} simple_handler_t;
// Display handler structure.
// Implements cef_display_handler_t interface.
typedef struct _simple_display_handler_t {
// MUST be first member - CEF base structure.
cef_display_handler_t handler;
// Reference count for this object.
atomic_int ref_count;
// Back reference to parent handler.
simple_handler_t* parent;
} simple_display_handler_t;
// Life span handler structure.
// Implements cef_life_span_handler_t interface.
typedef struct _simple_life_span_handler_t {
// MUST be first member - CEF base structure.
cef_life_span_handler_t handler;
// Reference count for this object.
atomic_int ref_count;
// Back reference to parent handler.
simple_handler_t* parent;
} simple_life_span_handler_t;
// Load handler structure.
// Implements cef_load_handler_t interface.
typedef struct _simple_load_handler_t {
// MUST be first member - CEF base structure.
cef_load_handler_t handler;
// Reference count for this object.
atomic_int ref_count;
// Back reference to parent handler.
simple_handler_t* parent;
} simple_load_handler_t;
// Create a new client handler instance.
// Parameters:
// is_alloy_style - 1 for Alloy style, 0 for Chrome style
// Returns a pointer with ref count of 1.
// Caller is responsible for releasing the reference when done.
simple_handler_t* simple_handler_create(int is_alloy_style);
// Get the global singleton instance (if created).
// Does NOT add a reference.
// Returns NULL if no instance has been created yet.
simple_handler_t* simple_handler_get_instance(void);
// Request that all existing browser windows close.
void simple_handler_close_all_browsers(simple_handler_t* handler,
int force_close);
// Show the main window (for macOS dock icon activation).
void simple_handler_show_main_window(simple_handler_t* handler);
// Platform-specific title change implementation.
// Declared here but implemented in platform-specific .c files.
void simple_handler_platform_title_change(simple_handler_t* handler,
cef_browser_t* browser,
const cef_string_t* title);
// Platform-specific show window implementation (macOS only).
// Declared here but implemented in platform-specific .c files.
void simple_handler_platform_show_window(simple_handler_t* handler,
cef_browser_t* browser);
#endif // CEF_TESTS_CEFSIMPLE_CAPI_SIMPLE_HANDLER_H_
@@ -0,0 +1,60 @@
// Copyright (c) 2025 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#include "tests/cefsimple_capi/simple_handler.h"
#if defined(CEF_X11)
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <string.h>
#endif
#include "include/capi/cef_browser_capi.h"
void simple_handler_platform_title_change(simple_handler_t* handler,
cef_browser_t* browser,
const cef_string_t* title) {
#if defined(CEF_X11)
// Convert the title to UTF-8.
cef_string_utf8_t title_utf8 = {};
cef_string_to_utf8(title->str, title->length, &title_utf8);
// Retrieve the X11 display shared with Chromium.
Display* display = cef_get_xdisplay();
if (!display) {
cef_string_utf8_clear(&title_utf8);
return;
}
// Retrieve the X11 window handle for the browser.
cef_browser_host_t* host = browser->get_host(browser);
if (!host) {
cef_string_utf8_clear(&title_utf8);
return;
}
cef_window_handle_t window = host->get_window_handle(host);
if (window == 0) {
host->base.release(&host->base);
cef_string_utf8_clear(&title_utf8);
return;
}
// Retrieve the atoms required by the below XChangeProperty call.
const char* kAtoms[] = {"_NET_WM_NAME", "UTF8_STRING"};
Atom atoms[2];
int result = XInternAtoms(display, (char**)kAtoms, 2, False, atoms);
if (result) {
// Set the window title.
XChangeProperty(display, window, atoms[0], atoms[1], 8, PropModeReplace,
(const unsigned char*)title_utf8.str, title_utf8.length);
// Also set via XStoreName as fallback.
XStoreName(display, window, title_utf8.str);
}
host->base.release(&host->base);
cef_string_utf8_clear(&title_utf8);
#endif // defined(CEF_X11)
}
+55
View File
@@ -0,0 +1,55 @@
// Copyright (c) 2025 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#include "tests/cefsimple_capi/simple_handler.h"
#import <Cocoa/Cocoa.h>
#include "include/capi/cef_browser_capi.h"
#include "include/internal/cef_types_mac.h"
static NSWindow* GetNSWindowForBrowser(cef_browser_t* browser) {
cef_browser_host_t* host = browser->get_host(browser);
if (!host) {
return nil;
}
cef_window_handle_t handle = host->get_window_handle(host);
host->base.release(&host->base);
if (!handle) {
return nil;
}
NSView* view = CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(handle);
return [view window];
}
void simple_handler_platform_title_change(simple_handler_t* handler,
cef_browser_t* browser,
const cef_string_t* title) {
NSWindow* window = GetNSWindowForBrowser(browser);
if (!window) {
return;
}
// Convert CEF string to UTF-8.
cef_string_utf8_t title_utf8 = {};
cef_string_to_utf8(title->str, title->length, &title_utf8);
NSString* str = [NSString stringWithUTF8String:title_utf8.str];
[window setTitle:str];
cef_string_utf8_clear(&title_utf8);
}
void simple_handler_platform_show_window(simple_handler_t* handler,
cef_browser_t* browser) {
NSWindow* window = GetNSWindowForBrowser(browser);
if (!window) {
return;
}
[window makeKeyAndOrderFront:window];
}
+26
View File
@@ -0,0 +1,26 @@
// Copyright (c) 2025 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#include "tests/cefsimple_capi/simple_handler.h"
#include <windows.h>
#include "include/capi/cef_browser_capi.h"
void simple_handler_platform_title_change(simple_handler_t* handler,
cef_browser_t* browser,
const cef_string_t* title) {
cef_browser_host_t* host = browser->get_host(browser);
if (!host) {
return;
}
cef_window_handle_t hwnd = host->get_window_handle(host);
if (hwnd) {
// CEF uses UTF-16 strings, which Windows SetWindowTextW expects.
SetWindowTextW(hwnd, title->str);
}
host->base.release(&host->base);
}
@@ -0,0 +1,92 @@
// Copyright (c) 2025 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#include <stdatomic.h>
#include <stdlib.h>
#include "include/capi/cef_app_capi.h"
#include "tests/cefsimple_capi/ref_counted.h"
#include "tests/cefsimple_capi/simple_browser_list.h"
#include "tests/cefsimple_capi/simple_handler.h"
#include "tests/cefsimple_capi/simple_utils.h"
//
// Life span handler implementation.
//
IMPLEMENT_REFCOUNTING_SIMPLE(simple_life_span_handler_t,
life_span_handler,
ref_count)
void CEF_CALLBACK
life_span_handler_on_after_created(cef_life_span_handler_t* self,
cef_browser_t* browser) {
simple_life_span_handler_t* handler = (simple_life_span_handler_t*)self;
// Add to the list of existing browsers.
// browser_list_add adds its own reference, so we can release the parameter.
browser_list_add(&handler->parent->browser_list, browser);
// Release the browser callback parameter.
// The list has its own reference now.
browser->base.release(&browser->base);
}
int CEF_CALLBACK life_span_handler_do_close(cef_life_span_handler_t* self,
cef_browser_t* browser) {
simple_life_span_handler_t* handler = (simple_life_span_handler_t*)self;
// Closing the main window requires special handling.
if (browser_list_count(&handler->parent->browser_list) == 1) {
// Set a flag to indicate that the window close should be allowed.
handler->parent->is_closing = 1;
}
// Release the browser callback parameter before returning.
browser->base.release(&browser->base);
// Allow the close. Return false to proceed with closing.
return 0;
}
void CEF_CALLBACK
life_span_handler_on_before_close(cef_life_span_handler_t* self,
cef_browser_t* browser) {
simple_life_span_handler_t* handler = (simple_life_span_handler_t*)self;
// Remove from the list of existing browsers.
// This releases the list's reference to the browser.
browser_list_remove(&handler->parent->browser_list, browser);
if (browser_list_count(&handler->parent->browser_list) == 0) {
// All browser windows have closed. Quit the application message loop.
cef_quit_message_loop();
}
// Release the browser callback parameter before returning.
browser->base.release(&browser->base);
}
simple_life_span_handler_t* life_span_handler_create(simple_handler_t* parent) {
simple_life_span_handler_t* handler = (simple_life_span_handler_t*)calloc(
1, sizeof(simple_life_span_handler_t));
CHECK(handler);
// Initialize base structure.
INIT_CEF_BASE_REFCOUNTED(&handler->handler.base, cef_life_span_handler_t,
life_span_handler);
// Set callbacks.
handler->handler.on_after_created = life_span_handler_on_after_created;
handler->handler.do_close = life_span_handler_do_close;
handler->handler.on_before_close = life_span_handler_on_before_close;
// Store parent reference (no ref count - parent owns us).
handler->parent = parent;
// Initialize with ref count of 1.
atomic_store(&handler->ref_count, 1);
return handler;
}
@@ -0,0 +1,97 @@
// Copyright (c) 2025 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#include <stdatomic.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "include/capi/cef_parser_capi.h"
#include "tests/cefsimple_capi/ref_counted.h"
#include "tests/cefsimple_capi/simple_handler.h"
#include "tests/cefsimple_capi/simple_utils.h"
//
// Load handler implementation.
//
IMPLEMENT_REFCOUNTING_SIMPLE(simple_load_handler_t, load_handler, ref_count)
void CEF_CALLBACK load_handler_on_load_error(cef_load_handler_t* self,
cef_browser_t* browser,
cef_frame_t* frame,
cef_errorcode_t errorCode,
const cef_string_t* errorText,
const cef_string_t* failedUrl) {
simple_load_handler_t* handler = (simple_load_handler_t*)self;
// Only display error page in alloy style and not for aborted downloads.
// Allow Chrome to show the error page in other cases.
if (handler->parent->is_alloy_style && errorCode != ERR_ABORTED) {
// Display a load error message using a data: URI.
char error_html[1024];
snprintf(error_html, sizeof(error_html),
"<html><body bgcolor=\"white\">"
"<h2>Failed to load URL with error %d.</h2></body></html>",
errorCode);
// Convert to cef_string_t.
cef_string_t error_str = {};
cef_string_from_ascii(error_html, strlen(error_html), &error_str);
// Create data URI.
cef_string_t mime_type = {};
cef_string_from_ascii("text/html", 9, &mime_type);
// Base64 encode the error HTML.
cef_string_userfree_t encoded =
cef_base64_encode(error_str.str, error_str.length);
if (encoded) {
// Create the data URI.
char data_uri[2048];
cef_string_utf8_t encoded_utf8 = {};
cef_string_to_utf8(encoded->str, encoded->length, &encoded_utf8);
snprintf(data_uri, sizeof(data_uri), "data:text/html;base64,%s",
encoded_utf8.str);
cef_string_utf8_clear(&encoded_utf8);
// Load the data URI.
cef_string_t data_uri_str = {};
cef_string_from_ascii(data_uri, strlen(data_uri), &data_uri_str);
frame->load_url(frame, &data_uri_str);
cef_string_clear(&data_uri_str);
cef_string_userfree_free(encoded);
}
cef_string_clear(&error_str);
cef_string_clear(&mime_type);
}
// Release all object parameters.
browser->base.release(&browser->base);
frame->base.release(&frame->base);
}
simple_load_handler_t* load_handler_create(simple_handler_t* parent) {
simple_load_handler_t* handler =
(simple_load_handler_t*)calloc(1, sizeof(simple_load_handler_t));
CHECK(handler);
// Initialize base structure.
INIT_CEF_BASE_REFCOUNTED(&handler->handler.base, cef_load_handler_t,
load_handler);
// Set callbacks.
handler->handler.on_load_error = load_handler_on_load_error;
// Store parent reference (no ref count - parent owns us).
handler->parent = parent;
// Initialize with ref count of 1.
atomic_store(&handler->ref_count, 1);
return handler;
}
+29
View File
@@ -0,0 +1,29 @@
// Copyright (c) 2025 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#ifndef CEF_TESTS_CEFSIMPLE_CAPI_SIMPLE_UTILS_H_
#define CEF_TESTS_CEFSIMPLE_CAPI_SIMPLE_UTILS_H_
#include <stdio.h>
#include <stdlib.h>
//
// Assertion Helpers
//
// For invariants that should never fail, crash immediately with a clear
// message.
//
// Check that a condition is true. If false, crash with error message.
// Example: CHECK(handler != NULL);
#define CHECK(cond) \
do { \
if (!(cond)) { \
fprintf(stderr, "[cefsimple_capi FATAL] %s:%d: CHECK failed: %s\n", \
__FILE__, __LINE__, #cond); \
abort(); \
} \
} while (0)
#endif // CEF_TESTS_CEFSIMPLE_CAPI_SIMPLE_UTILS_H_
+330
View File
@@ -0,0 +1,330 @@
// Copyright (c) 2025 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#include "tests/cefsimple_capi/simple_views.h"
#include <stdlib.h>
#include "tests/cefsimple_capi/ref_counted.h"
#include "tests/cefsimple_capi/simple_utils.h"
//
// Browser View Delegate Implementation
//
// Implement reference counting functions for browser view delegate.
IMPLEMENT_REFCOUNTING_SIMPLE(simple_browser_view_delegate_t,
browser_view_delegate,
ref_count)
// Returns the delegate for a popup BrowserView.
// Called before on_popup_browser_view_created.
cef_browser_view_delegate_t* CEF_CALLBACK
browser_view_delegate_get_delegate_for_popup_browser_view(
cef_browser_view_delegate_t* self,
cef_browser_view_t* browser_view,
const cef_browser_settings_t* settings,
cef_client_t* client,
int is_devtools) {
simple_browser_view_delegate_t* delegate =
(simple_browser_view_delegate_t*)self;
// Release parameters before returning.
browser_view->base.base.release(&browser_view->base.base);
// settings is a const pointer, not ref-counted
client->base.release(&client->base);
// Return this same delegate for the popup (matches C++ default).
// Add a reference since we're returning to CEF.
delegate->delegate.base.base.add_ref(&delegate->delegate.base.base);
return &delegate->delegate;
}
// Called when a popup BrowserView is created.
// Return true if we handle creating the window for the popup.
int CEF_CALLBACK browser_view_delegate_on_popup_browser_view_created(
cef_browser_view_delegate_t* self,
cef_browser_view_t* browser_view,
cef_browser_view_t* popup_browser_view,
int is_devtools) {
simple_browser_view_delegate_t* delegate =
(simple_browser_view_delegate_t*)self;
// Create a new top-level Window for the popup.
// We transfer our reference to the window delegate.
simple_window_delegate_t* window_delegate = window_delegate_create(
popup_browser_view, delegate->runtime_style, CEF_SHOW_STATE_NORMAL);
if (window_delegate) {
// Create the window. The window will show itself after creation.
// We transfer our window_delegate reference to CEF.
cef_window_create_top_level(&window_delegate->delegate);
}
// Release parameters before returning.
browser_view->base.base.release(&browser_view->base.base);
// Don't release popup_browser_view - we transferred it to window_delegate.
// We created the Window.
return 1;
}
// Returns the runtime style for the browser.
cef_runtime_style_t CEF_CALLBACK
browser_view_delegate_get_browser_runtime_style(
cef_browser_view_delegate_t* self) {
simple_browser_view_delegate_t* delegate =
(simple_browser_view_delegate_t*)self;
return delegate->runtime_style;
}
// Creates a browser view delegate instance.
simple_browser_view_delegate_t* browser_view_delegate_create(
cef_runtime_style_t runtime_style) {
simple_browser_view_delegate_t* delegate =
(simple_browser_view_delegate_t*)calloc(
1, sizeof(simple_browser_view_delegate_t));
CHECK(delegate);
// Initialize base structure.
INIT_CEF_BASE_REFCOUNTED(&delegate->delegate.base.base,
cef_browser_view_delegate_t, browser_view_delegate);
// Set callbacks.
delegate->delegate.get_delegate_for_popup_browser_view =
browser_view_delegate_get_delegate_for_popup_browser_view;
delegate->delegate.on_popup_browser_view_created =
browser_view_delegate_on_popup_browser_view_created;
delegate->delegate.get_browser_runtime_style =
browser_view_delegate_get_browser_runtime_style;
// Store runtime style.
delegate->runtime_style = runtime_style;
// Initialize with ref count of 1.
atomic_store(&delegate->ref_count, 1);
return delegate;
}
//
// Window Delegate Implementation
//
// Implement reference counting functions for window delegate.
IMPLEMENT_REFCOUNTING_MANUAL(simple_window_delegate_t,
window_delegate,
ref_count)
// Release function for window delegate with custom cleanup.
int CEF_CALLBACK window_delegate_release(cef_base_ref_counted_t* self) {
simple_window_delegate_t* delegate = (simple_window_delegate_t*)self;
int count = atomic_fetch_sub(&delegate->ref_count, 1) - 1;
if (count == 0) {
// Release the browser view reference if we still have it.
if (delegate->browser_view) {
delegate->browser_view->base.base.release(
&delegate->browser_view->base.base);
}
free(delegate);
return 1;
}
return 0;
}
// Called when the window is created.
void CEF_CALLBACK window_delegate_on_window_created(cef_window_delegate_t* self,
cef_window_t* window) {
simple_window_delegate_t* delegate = (simple_window_delegate_t*)self;
// Add a reference for ourselves since add_child_view transfers ownership.
// This gives us an independent reference to use in can_close.
delegate->browser_view->base.base.add_ref(&delegate->browser_view->base.base);
// Add the browser view to the window (transfers the original reference).
window->base.add_child_view(&window->base, &delegate->browser_view->base);
// Show the window if not hidden.
if (delegate->initial_show_state != CEF_SHOW_STATE_HIDDEN) {
window->show(window);
}
// Release the window parameter before returning.
window->base.base.base.release(&window->base.base.base);
}
// Called when the window is destroyed.
void CEF_CALLBACK
window_delegate_on_window_destroyed(cef_window_delegate_t* self,
cef_window_t* window) {
simple_window_delegate_t* delegate = (simple_window_delegate_t*)self;
// Release our browser_view reference (added in on_window_created).
// We won't need browser_view anymore since the window is being destroyed.
// Note: Releasing the browser_view may also release the contained browser,
// so on_before_close will be called before this callback returns.
if (delegate->browser_view) {
delegate->browser_view->base.base.release(
&delegate->browser_view->base.base);
delegate->browser_view = NULL;
}
// Release the window parameter before returning.
window->base.base.base.release(&window->base.base.base);
}
// Called to check if the window can be resized.
// C++ defaults this to true in the header. We don't get those defaults with
// the C API.
int CEF_CALLBACK window_delegate_can_resize(cef_window_delegate_t* self,
cef_window_t* window) {
// Release the window parameter before returning.
window->base.base.base.release(&window->base.base.base);
// Default: allow resize.
return 1;
}
// Called to check if the window can be maximized.
// C++ defaults this to true in the header. We don't get those defaults with
// the C API.
int CEF_CALLBACK window_delegate_can_maximize(cef_window_delegate_t* self,
cef_window_t* window) {
// Release the window parameter before returning.
window->base.base.base.release(&window->base.base.base);
// Default: allow maximize.
return 1;
}
// Called to check if the window can be minimized.
// C++ defaults this to true in the header. We don't get those defaults with
// the C API.
int CEF_CALLBACK window_delegate_can_minimize(cef_window_delegate_t* self,
cef_window_t* window) {
// Release the window parameter before returning.
window->base.base.base.release(&window->base.base.base);
// Default: allow minimize.
return 1;
}
// Called to check if the window can close.
int CEF_CALLBACK window_delegate_can_close(cef_window_delegate_t* self,
cef_window_t* window) {
simple_window_delegate_t* delegate = (simple_window_delegate_t*)self;
int can_close = 1; // Default to allowing close.
// Ask the browser if it's OK to close.
if (delegate->browser_view) {
cef_browser_t* browser =
delegate->browser_view->get_browser(delegate->browser_view);
if (browser) {
cef_browser_host_t* host = browser->get_host(browser);
if (host) {
can_close = host->try_close_browser(host);
// Release host (get_host returns a new reference).
host->base.release(&host->base);
}
// Release browser (get_browser returns a new reference).
browser->base.release(&browser->base);
}
}
// Release the window parameter before returning.
window->base.base.base.release(&window->base.base.base);
return can_close;
}
// Called to check if the window should have standard window buttons (macOS).
// C++ defaults this to true in the header. We don't get those defaults with
// the C API.
int CEF_CALLBACK
window_delegate_with_standard_window_buttons(cef_window_delegate_t* self,
cef_window_t* window) {
// Release the window parameter before returning.
window->base.base.base.release(&window->base.base.base);
// Default: show standard window buttons.
return 1;
}
// Returns the preferred size for the view.
cef_size_t CEF_CALLBACK
window_delegate_get_preferred_size(cef_view_delegate_t* self,
cef_view_t* view) {
cef_size_t size = {800, 600};
// Release the view parameter before returning.
view->base.release(&view->base);
return size;
}
// Returns the initial show state for the window.
cef_show_state_t CEF_CALLBACK
window_delegate_get_initial_show_state(cef_window_delegate_t* self,
cef_window_t* window) {
simple_window_delegate_t* delegate = (simple_window_delegate_t*)self;
cef_show_state_t show_state = delegate->initial_show_state;
// Release the window parameter before returning.
window->base.base.base.release(&window->base.base.base);
return show_state;
}
// Returns the runtime style for the window.
cef_runtime_style_t CEF_CALLBACK
window_delegate_get_window_runtime_style(cef_window_delegate_t* self) {
simple_window_delegate_t* delegate = (simple_window_delegate_t*)self;
return delegate->runtime_style;
}
// Creates a window delegate instance.
simple_window_delegate_t* window_delegate_create(
cef_browser_view_t* browser_view,
cef_runtime_style_t runtime_style,
cef_show_state_t initial_show_state) {
simple_window_delegate_t* delegate =
(simple_window_delegate_t*)calloc(1, sizeof(simple_window_delegate_t));
CHECK(delegate);
// Initialize base structure.
INIT_CEF_BASE_REFCOUNTED(&delegate->delegate.base.base.base,
cef_window_delegate_t, window_delegate);
// Set callbacks for cef_window_delegate_t.
delegate->delegate.on_window_created = window_delegate_on_window_created;
delegate->delegate.on_window_destroyed = window_delegate_on_window_destroyed;
delegate->delegate.can_resize = window_delegate_can_resize;
delegate->delegate.can_maximize = window_delegate_can_maximize;
delegate->delegate.can_minimize = window_delegate_can_minimize;
delegate->delegate.can_close = window_delegate_can_close;
delegate->delegate.with_standard_window_buttons =
window_delegate_with_standard_window_buttons;
delegate->delegate.get_initial_show_state =
window_delegate_get_initial_show_state;
delegate->delegate.get_window_runtime_style =
window_delegate_get_window_runtime_style;
// Set callbacks for cef_view_delegate_t (parent structure).
delegate->delegate.base.base.get_preferred_size =
window_delegate_get_preferred_size;
// Store the browser view reference (we take ownership).
delegate->browser_view = browser_view;
// Store runtime style and initial show state.
delegate->runtime_style = runtime_style;
delegate->initial_show_state = initial_show_state;
// Initialize with ref count of 1.
atomic_store(&delegate->ref_count, 1);
return delegate;
}
+68
View File
@@ -0,0 +1,68 @@
// Copyright (c) 2025 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#ifndef CEF_TESTS_CEFSIMPLE_CAPI_SIMPLE_VIEWS_H_
#define CEF_TESTS_CEFSIMPLE_CAPI_SIMPLE_VIEWS_H_
#include <stdatomic.h>
#include "include/capi/views/cef_browser_view_capi.h"
#include "include/capi/views/cef_window_capi.h"
// Forward declaration.
typedef struct _simple_window_delegate_t simple_window_delegate_t;
//
// Browser view delegate structure.
// Implements cef_browser_view_delegate_t callbacks.
//
typedef struct _simple_browser_view_delegate_t {
cef_browser_view_delegate_t delegate;
// Reference count for this object.
atomic_int ref_count;
// Runtime style for the browser.
cef_runtime_style_t runtime_style;
} simple_browser_view_delegate_t;
//
// Window delegate structure.
// Implements cef_window_delegate_t callbacks.
//
typedef struct _simple_window_delegate_t {
cef_window_delegate_t delegate;
// Reference count for this object.
atomic_int ref_count;
// Browser view to add to the window (owned by this structure).
// We keep CEF's reference from the creation call.
cef_browser_view_t* browser_view;
// Runtime style for the window.
cef_runtime_style_t runtime_style;
// Initial show state for the window.
cef_show_state_t initial_show_state;
} simple_window_delegate_t;
//
// Creates a browser view delegate.
// Caller is responsible for releasing the reference when done.
//
simple_browser_view_delegate_t* browser_view_delegate_create(
cef_runtime_style_t runtime_style);
//
// Creates a window delegate.
// Takes ownership of the browser_view reference.
// Caller is responsible for releasing the reference when done.
//
simple_window_delegate_t* window_delegate_create(
cef_browser_view_t* browser_view,
cef_runtime_style_t runtime_style,
cef_show_state_t initial_show_state);
#endif // CEF_TESTS_CEFSIMPLE_CAPI_SIMPLE_VIEWS_H_
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<!--The compatibility section will be merged from build/win/compatibility.manifest -->
<dependency>
<dependentAssembly>
<assemblyIdentity type="Win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"></assemblyIdentity>
</dependentAssembly>
</dependency>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" />
</requestedPrivileges>
</security>
</trustInfo>
</assembly>
Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

+79
View File
@@ -0,0 +1,79 @@
// Microsoft Visual C++ generated resource script.
//
#include "tests/cefsimple/resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#define APSTUDIO_HIDDEN_SYMBOLS
#include "windows.h"
#undef APSTUDIO_HIDDEN_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (U.S.) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif //_WIN32
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_CEFSIMPLE ICON "cefsimple.ico"
IDI_SMALL ICON "small.ico"
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#define APSTUDIO_HIDDEN_SYMBOLS\r\n"
"#include ""windows.h""\r\n"
"#undef APSTUDIO_HIDDEN_SYMBOLS\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
#endif // English (U.S.) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED
Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

+1 -7
View File
@@ -13,17 +13,11 @@ build --enable_platform_specific_config
#
# Build with C++17.
# See also the enable_cxx17 argument to declare_objc_library()
build:linux --cxxopt='-std=c++17'
build:macos --cxxopt='-std=c++17'
build:macos --copt='-std=c++17'
build:windows --cxxopt='/std:c++17'
#
# MacOS configuration.
#
build:macos --copt='-ObjC++'
#
# Windows configuration.
#
@@ -0,0 +1,142 @@
# Copyright (c) 2025 The Chromium Embedded Framework Authors. All rights
# reserved. Use of this source code is governed by a BSD-style license that
# can be found in the LICENSE file.
PRODUCT_NAME = "cefsimple_capi"
PKG_NAME = "//tests/{}".format(PRODUCT_NAME)
# Allow access from subpackages only.
package(default_visibility = [
":__subpackages__",
])
load("//bazel:library_helpers.bzl", "declare_cc_library", "declare_objc_library")
load("@rules_cc//cc:defs.bzl", "cc_library")
#
# Source file lists.
#
srcs_browser = [
${cefsimple_capi_sources_common}
]
srcs_browser_linux = [
${cefsimple_capi_sources_linux}
]
srcs_browser_mac = [
${cefsimple_capi_sources_mac}
]
srcs_browser_win = [
${cefsimple_capi_sources_win}
]
srcs_renderer_mac = [
${cefsimple_capi_sources_mac_helper}
]
#
# MacOS targets.
#
# Pure C sources
declare_cc_library(
name = "BrowserLibMac_C",
srcs = srcs_browser, # Pure .c files
target_compatible_with = [
"@platforms//os:macos",
],
copts = [
"-std=c11", # Use C11 standard
],
deps = [
"//:cef_wrapper",
],
)
# Objective-C sources
declare_objc_library(
name = "BrowserLibMac_ObjC",
srcs = srcs_browser_mac, # .m files
enable_cxx17 = False, # Pure Objective-C, not Objective-C++
target_compatible_with = [
"@platforms//os:macos",
],
deps = [
"//:cef_wrapper",
":BrowserLibMac_C",
],
)
# Combined library
alias(
name = "BrowserLibMac",
actual = ":BrowserLibMac_ObjC",
)
# Pure C helper process sources
declare_cc_library(
name = "RendererLibMac",
srcs = srcs_renderer_mac,
target_compatible_with = [
"@platforms//os:macos",
],
copts = [
"-std=c11", # Use C11 standard
],
deps = [
"//:cef_wrapper",
],
)
#
# Windows targets.
#
# Allow access to resource.h from the declare_exe target.
cc_library(
name = "ResourceH",
hdrs = [
"resource.h"
]
)
# Include files directly in the declare_exe target. This simplifies the build
# configuration and avoids issues with Windows discarding symbols (like WinMain)
# when linking libraries.
filegroup(
name = "SrcsWin",
srcs = srcs_browser + srcs_browser_win,
target_compatible_with = [
"@platforms//os:windows",
],
)
#
# Linux targets.
#
# Include files directly in the declare_exe target. This simplifies the build
# configuration.
filegroup(
name = "SrcsLinux",
srcs = srcs_browser + srcs_browser_linux,
target_compatible_with = [
"@platforms//os:linux",
],
)
#
# Alias to platform-specific build targets.
#
alias(
name = PRODUCT_NAME,
actual = select({
"@platforms//os:linux": "{}/linux:{}".format(PKG_NAME, PRODUCT_NAME),
"@platforms//os:macos": "{}/mac:{}".format(PKG_NAME, PRODUCT_NAME),
"@platforms//os:windows": "{}/win:{}".format(PKG_NAME, PRODUCT_NAME),
}),
)
@@ -0,0 +1,27 @@
# Copyright (c) 2025 The Chromium Embedded Framework Authors. All rights
# reserved. Use of this source code is governed by a BSD-style license that
# can be found in the LICENSE file.
PRODUCT_NAME = "cefsimple_capi"
PKG_NAME = "//tests/{}".format(PRODUCT_NAME)
# Allow access from the parent package only.
package(default_visibility = [
"{}:__pkg__".format(PKG_NAME),
])
load("//bazel/linux:exe_helpers.bzl", "declare_exe")
#
# Linux executable target.
#
declare_exe(
name = PRODUCT_NAME,
srcs = [
"{}:SrcsLinux".format(PKG_NAME),
],
copts = [
"-std=c11", # cefsimple_capi requires C11 for stdatomic.h
],
)
@@ -0,0 +1,48 @@
# Copyright (c) 2025 The Chromium Embedded Framework Authors. All rights
# reserved. Use of this source code is governed by a BSD-style license that
# can be found in the LICENSE file.
PRODUCT_NAME = "cefsimple_capi"
PKG_NAME = "//tests/{}".format(PRODUCT_NAME)
# Allow access from the parent package only.
package(default_visibility = [
"{}:__pkg__".format(PKG_NAME),
])
load("//bazel/mac:app_helpers.bzl", "declare_all_helper_apps", "declare_main_app")
#
# MacOS app bundle target.
#
filegroup(
name = "ResourcesMac",
srcs = [
"English.lproj/InfoPlist.strings",
"English.lproj/MainMenu.xib",
"cefsimple_capi.icns",
],
)
# Helper app bundles.
declare_all_helper_apps(
name = PRODUCT_NAME,
info_plist = "helper-Info.plist.in",
deps = [
"{}:RendererLibMac".format(PKG_NAME),
],
)
# Main app bundle.
declare_main_app(
name = PRODUCT_NAME,
info_plist = "Info.plist.in",
deps = [
"{}:BrowserLibMac".format(PKG_NAME),
],
linkopts = [],
resources = [
":ResourcesMac",
]
)
@@ -0,0 +1,68 @@
# Copyright (c) 2025 The Chromium Embedded Framework Authors. All rights
# reserved. Use of this source code is governed by a BSD-style license that
# can be found in the LICENSE file.
PRODUCT_NAME = "cefsimple_capi"
PKG_NAME = "//tests/{}".format(PRODUCT_NAME)
# Allow access from the parent package only.
package(default_visibility = [
"{}:__pkg__".format(PKG_NAME),
])
#
# Windows DLL target (sandbox enabled).
#
load("//bazel/win:exe_helpers.bzl", "declare_dll")
declare_dll(
name = PRODUCT_NAME,
srcs = [
"{}:SrcsWin".format(PKG_NAME),
],
rc_file = "cefsimple.rc",
resources_srcs = [
"cefsimple.ico",
"small.ico",
],
resources_deps = [
"{}:ResourceH".format(PKG_NAME),
],
copts = [
"/std:c11", # cefsimple_capi requires C11 for stdatomic.h
"/experimental:c11atomics", # Enable C11 atomics support in MSVC
],
local_defines = [
# Enable use of the bootstrap executable.
"CEF_USE_BOOTSTRAP",
],
)
#
# Windows executable target (sandbox disabled).
#
# load("//bazel/win:exe_helpers.bzl", "declare_exe")
#
# declare_exe(
# name = PRODUCT_NAME,
# srcs = [
# "{}:SrcsWin".format(PKG_NAME),
# ],
# rc_file = "cefsimple.rc",
# manifest_srcs = [
# "compatibility.manifest",
# "cefsimple.exe.manifest",
# ],
# resources_srcs = [
# "cefsimple.ico",
# "small.ico",
# ],
# resources_deps = [
# "{}:ResourceH".format(PKG_NAME),
# ],
# linkopts = [
# "/SUBSYSTEM:WINDOWS",
# ],
# )
+1 -1
View File
@@ -12,7 +12,7 @@ from git_util import get_changed_files
from yapf_util import yapf_format
# File extensions that can be formatted.
DEFAULT_LINT_WHITELIST_REGEX = r"(.*\.cpp|.*\.cc|.*\.h|.*\.java|.*\.mm|.*\.py)$"
DEFAULT_LINT_WHITELIST_REGEX = r"(.*\.cpp|.*\.cc|.*\.c|.*\.h|.*\.java|.*\.mm|.*\.m|.*\.py)$"
DEFAULT_LINT_BLACKLIST_REGEX = r"$^"
# Directories containing these path components will be ignored.
+32
View File
@@ -966,6 +966,10 @@ if mode == 'standard':
cefsimple_dir = os.path.join(tests_dir, 'cefsimple')
make_dir(cefsimple_dir, options.quiet)
# create the tests/cefsimple_capi directory
cefsimple_capi_dir = os.path.join(tests_dir, 'cefsimple_capi')
make_dir(cefsimple_capi_dir, options.quiet)
# create the tests/ceftests directory
ceftests_dir = os.path.join(tests_dir, 'ceftests')
make_dir(ceftests_dir, options.quiet)
@@ -995,6 +999,10 @@ if mode == 'standard':
transfer_gypi_files(cef_dir, cef_paths2['cefsimple_sources_common'], \
'tests/cefsimple/', cefsimple_dir, options.quiet)
# transfer common cefsimple_capi files
transfer_gypi_files(cef_dir, cef_paths2['cefsimple_capi_sources_common'], \
'tests/cefsimple_capi/', cefsimple_capi_dir, options.quiet)
# transfer common ceftests files
transfer_gypi_files(cef_dir, cef_paths2['ceftests_sources_common'], \
'tests/ceftests/', ceftests_dir, options.quiet)
@@ -1010,6 +1018,9 @@ if mode == 'standard':
process_cmake_template(os.path.join(cef_dir, 'tests', 'cefsimple', 'CMakeLists.txt.in'), \
os.path.join(cefsimple_dir, 'CMakeLists.txt'), \
variables, options.quiet)
process_cmake_template(os.path.join(cef_dir, 'tests', 'cefsimple_capi', 'CMakeLists.txt.in'), \
os.path.join(cefsimple_capi_dir, 'CMakeLists.txt'), \
variables, options.quiet)
process_cmake_template(os.path.join(cef_dir, 'tests', 'gtest', 'CMakeLists.txt.in'), \
os.path.join(tests_dir, 'gtest', 'CMakeLists.txt'), \
variables, options.quiet)
@@ -1196,6 +1207,12 @@ elif platform == 'windows':
cef_paths2['cefsimple_sources_resources_win_rc'],
'tests/cefsimple/', cefsimple_dir, options.quiet)
# transfer cefsimple_capi files
transfer_gypi_files(cef_dir, cef_paths2['cefsimple_capi_sources_win'] +
cef_paths2['cefsimple_capi_sources_resources_win'] +
cef_paths2['cefsimple_capi_sources_resources_win_rc'],
'tests/cefsimple_capi/', cefsimple_capi_dir, options.quiet)
# transfer ceftests files
transfer_gypi_files(cef_dir, cef_paths2['ceftests_sources_win'] +
cef_paths2['ceftests_sources_resources_win'] +
@@ -1350,6 +1367,17 @@ elif platform == 'mac':
os.path.join(cefsimple_dir, 'mac'), \
options.quiet)
# transfer cefsimple_capi files
transfer_gypi_files(cef_dir, cef_paths2['cefsimple_capi_sources_mac'], \
'tests/cefsimple_capi/', cefsimple_capi_dir, options.quiet)
transfer_gypi_files(cef_dir, cef_paths2['cefsimple_capi_sources_mac_helper'], \
'tests/cefsimple_capi/', cefsimple_capi_dir, options.quiet)
# transfer cefsimple_capi/mac files
copy_dir(os.path.join(cef_dir, 'tests/cefsimple_capi/mac'), \
os.path.join(cefsimple_capi_dir, 'mac'), \
options.quiet)
# transfer ceftests files
transfer_gypi_files(cef_dir, cef_paths2['ceftests_sources_mac'], \
'tests/ceftests/', ceftests_dir, options.quiet)
@@ -1448,6 +1476,10 @@ elif platform == 'linux':
transfer_gypi_files(cef_dir, cef_paths2['cefsimple_sources_linux'], \
'tests/cefsimple/', cefsimple_dir, options.quiet)
# transfer cefsimple_capi files
transfer_gypi_files(cef_dir, cef_paths2['cefsimple_capi_sources_linux'], \
'tests/cefsimple_capi/', cefsimple_capi_dir, options.quiet)
# transfer ceftests files
transfer_gypi_files(cef_dir, cef_paths2['ceftests_sources_linux'], \
'tests/ceftests/', ceftests_dir, options.quiet)
+6 -1
View File
@@ -158,9 +158,14 @@ int cef_load_library(const char* path) {
if (g_libcef_handle)
return 0;
if (!path) {
fprintf(stderr, "cef_load_library: Invalid path argument");
return 0;
}
g_libcef_handle = dlopen(path, RTLD_LAZY | RTLD_LOCAL | RTLD_FIRST);
if (!g_libcef_handle) {
fprintf(stderr, "dlopen %s: %s\\n", path, dlerror());
fprintf(stderr, "cef_load_library: dlopen %s: %s\\n", path, dlerror());
return 0;
}