win: Add bootstrap[c].exe for sandbox integration (see #3824)

Replace cef_sandbox.lib usage with bootstrap executables.
See the SandboxSetup Wiki page for details.
This commit is contained in:
Marshall Greenblatt
2025-05-08 18:33:07 -04:00
parent 7581264dbb
commit adcac2c37c
28 changed files with 1116 additions and 447 deletions

215
BUILD.gn
View File

@ -319,6 +319,16 @@ group("cef") {
":libcef_static_unittests",
]
if (is_win) {
deps += [
":bootstrap",
":bootstrapc",
":cefclient_dll",
":cefsimple_dll",
":ceftests_dll",
]
}
if (!is_linux || ozone_platform_x11) {
deps += [ ":cefclient" ]
}
@ -992,6 +1002,9 @@ source_set("libcef_static") {
"libcef/browser/native/browser_platform_delegate_native_win.h",
"libcef/browser/osr/browser_platform_delegate_osr_win.cc",
"libcef/browser/osr/browser_platform_delegate_osr_win.h",
"libcef/browser/preferred_stack_size_win.inc",
"libcef_dll/bootstrap/bootstrap_util_win.cc",
"libcef_dll/bootstrap/bootstrap_util_win.h",
]
deps += [
@ -1228,6 +1241,73 @@ if (is_mac) {
}
}
#
# bootstrap target.
#
if (is_win) {
bootstrap_sources = includes_common +
includes_win + [
"libcef_dll/bootstrap/bootstrap_util_win.cc",
"libcef_dll/bootstrap/bootstrap_util_win.h",
"libcef_dll/bootstrap/bootstrap_win.cc",
"libcef_dll/bootstrap/win/bootstrap.rc",
"libcef_dll/bootstrap/win/resource.h",
"libcef/browser/preferred_stack_size_win.inc",
]
bootstrap_deps = [
":cef_sandbox",
":make_version_header",
"//base",
"//build/win:default_exe_manifest",
]
# Windows application that initializes the sandbox and then passes
# execution to a client-provided DLL.
executable("bootstrap") {
# Necessary because the libcef target is testonly.
testonly = true
sources = bootstrap_sources
deps = bootstrap_deps
configs += [ ":libcef_includes_config" ]
# 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" ]
defines = [
"CEF_BUILD_BOOTSTRAP",
]
}
# Like "bootstrap", but as a console application.
executable("bootstrapc") {
# Necessary because the libcef target is testonly.
testonly = true
sources = bootstrap_sources
deps = bootstrap_deps
configs += [ ":libcef_includes_config" ]
# Delay-load as many DLLs as possible for sandbox and startup perf
# improvements.
configs += [ "//build/config/win:delayloads" ]
defines = [
"CEF_BUILD_BOOTSTRAP",
"CEF_BUILD_BOOTSTRAP_CONSOLE",
]
}
}
#
# Resource grit/pack targets.
#
@ -2224,6 +2304,66 @@ if (is_mac) {
}
}
if (is_win) {
# Like the "cefclient" executable target, but building a DLL to be loaded by
# bootstrap.exe.
shared_library("cefclient_dll") {
# Necessary because the libcef target is testonly.
testonly = true
output_name = "cefclient"
sources = includes_common +
includes_win +
gypi_paths2.includes_wrapper +
gypi_paths2.shared_sources_browser +
gypi_paths2.shared_sources_common +
gypi_paths2.shared_sources_renderer +
gypi_paths2.shared_sources_win +
gypi_paths2.cefclient_sources_browser +
gypi_paths2.cefclient_sources_common +
gypi_paths2.cefclient_sources_renderer +
gypi_paths2.cefclient_sources_win +
gypi_paths2.cefclient_sources_resources_win_rc
deps = [
":bootstrap",
":libcef",
":libcef_dll_wrapper",
]
defines = [
"CEF_USE_ATL",
"CEF_USE_BOOTSTRAP",
]
# Delay-load as many DLLs as possible for sandbox and startup perf
# improvements.
configs += [ "//build/config/win:delayloads" ]
libs = [
"comctl32.lib",
"d3d11.lib",
"imm32.lib",
"oleacc.lib",
"rpcrt4.lib",
"shlwapi.lib",
]
if (target_cpu != "arm64") {
libs += [
"glu32.lib",
"opengl32.lib",
]
ldflags = [
"/DELAYLOAD:glu32.dll",
"/DELAYLOAD:oleaut32.dll",
"/DELAYLOAD:opengl32.dll",
]
}
}
}
#
# cefsimple targets.
@ -2288,6 +2428,44 @@ if (is_mac) {
}
}
if (is_win) {
# Like the "cefsimple" executable target, but building a DLL to be loaded by
# bootstrap.exe.
shared_library("cefsimple_dll") {
# Necessary because the libcef target is testonly.
testonly = true
output_name = "cefsimple"
sources = includes_common +
includes_win +
gypi_paths2.includes_wrapper +
gypi_paths2.cefsimple_sources_common +
gypi_paths2.cefsimple_sources_win +
gypi_paths2.cefsimple_sources_resources_win_rc
deps = [
":bootstrap",
":libcef",
":libcef_dll_wrapper",
]
defines = [
"CEF_USE_BOOTSTRAP",
]
# Delay-load as many DLLs as possible for sandbox and startup perf
# improvements.
configs += [ "//build/config/win:delayloads" ]
libs = [
"comctl32.lib",
"shlwapi.lib",
"rpcrt4.lib",
]
}
}
#
# ceftests targets.
@ -2361,4 +2539,41 @@ if (is_mac) {
configs += [ "//build/config/gcc:rpath_for_built_shared_libraries" ]
}
}
if (is_win) {
# Like the "ceftests" executable target, but building a DLL to be loaded by
# bootstrapc.exe.
shared_library("ceftests_dll") {
testonly = true
output_name = "ceftests"
sources = includes_common +
gypi_paths2.includes_wrapper +
gypi_paths2.shared_sources_browser +
gypi_paths2.shared_sources_common +
gypi_paths2.shared_sources_renderer +
gypi_paths2.shared_sources_win +
gypi_paths2.ceftests_sources_common +
gypi_paths2.ceftests_sources_win +
gypi_paths2.ceftests_sources_resources_win_rc
deps = [
":bootstrapc",
":libcef",
":libcef_dll_wrapper",
":gtest_teamcity",
"//testing/gtest",
]
defines = [
"CEF_USE_BOOTSTRAP",
"CEF_TESTS_IN_SRC_DIRECTORY",
]
# Delay-load as many DLLs as possible for sandbox and startup perf
# improvements.
configs += [ "//build/config/win:delayloads" ]
}
}
}

View File

@ -37,6 +37,10 @@
#if defined(OS_WIN)
#if !defined(GENERATING_CEF_API_HASH)
#include <windows.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
@ -49,13 +53,34 @@ extern "C" {
/// http://www.chromium.org/developers/design-documents/sandbox for complete
/// details.
///
/// To enable the sandbox on Windows the following requirements must be met:
/// 1. Use the same executable for the browser process and all sub-processes.
/// 2. Link the executable with the cef_sandbox static library.
/// 3. Call the cef_sandbox_info_create() function from within the executable
/// (not from a separate DLL) and pass the resulting pointer into both the
/// CefExecuteProcess() and CefInitialize() functions via the
/// |windows_sandbox_info| parameter.
/// To enable the sandbox on Windows the same executable must be used for all
/// processes (browser process and sub-processes). This executable must link the
/// cef_sandbox static library and initialize the sandbox by calling
/// cef_sandbox_info_create. The resulting |sandbox_info| value must then be
/// passed to CefExecuteProcess and CefInitialize.
///
/// Beginning with M138 the cef_sandbox static library can only be linked with
/// applications built as part of the CEF/Chromium build. This is due to
/// unavoidable dependencies on Chromium's bundled Clang/LLVM/libc++ toolchain.
/// Client applications therefore have 3 options for sandbox integration:
///
/// 1. Build the client application (or a custom bootstrap executable) as part
/// of the CEF/Chromium build using Chromium's bundled Clang/LLVM/libc++
/// toolchain. For details of this option see
/// https://bitbucket.org/chromiumembedded/cef/wiki/SandboxSetup.md
/// 2. Build the client application as a DLL using any toolchain and run using
/// the provided bootstrap.exe or bootstrapc.exe. The DLL implements
/// RunWinMain or RunConsoleMain respectively and gets passed the
/// |sandbox_info| parameter which it then forwards to CefExecuteProcess
/// and CefInitialize. The provided bootstrap executables can optionally be
/// renamed or modified [1] to meet client branding needs.
/// 3. Build the client application as an executable using any toolchain with
/// the sandbox disabled. Pass nullptr as the |sandbox_info| parameter to
/// CefExecuteProcess and CefInitialize.
///
/// [1] Embedded executable resources such as icons and file properties can be
/// modified using Visual Studio or Resource Hacker tools. Be sure to code
/// sign all binaries after modification and before distribution to users.
///
///
@ -88,6 +113,37 @@ class CefScopedSandboxInfo {
};
#endif // __cplusplus
#if defined(CEF_BUILD_BOOTSTRAP)
#define CEF_BOOTSTRAP_EXPORT __declspec(dllimport)
#else
#define CEF_BOOTSTRAP_EXPORT __declspec(dllexport)
#endif
#ifdef __cplusplus
extern "C" {
#endif
///
/// Entry point to be implemented by client DLLs using bootstrap.exe for
/// windows (/SUBSYSTEM:WINDOWS) applications.
///
CEF_BOOTSTRAP_EXPORT int RunWinMain(HINSTANCE hInstance,
LPTSTR lpCmdLine,
int nCmdShow,
void* sandbox_info);
///
/// Entry point to be implemented by client DLLs using bootstrapc.exe for
/// console (/SUBSYSTEM:CONSOLE) applications.
///
CEF_BOOTSTRAP_EXPORT int RunConsoleMain(int argc,
char* argv[],
void* sandbox_info);
#ifdef __cplusplus
}
#endif
#endif // defined(OS_WIN)
#endif // CEF_INCLUDE_CEF_SANDBOX_WIN_H_

View File

@ -41,6 +41,8 @@
#include <windows.h>
#endif
#include "include/internal/cef_export.h"
#ifdef __cplusplus
extern "C" {
#endif

View File

@ -50,6 +50,10 @@
#include "cef/libcef/browser/chrome/chrome_web_contents_view_delegate_cef.h"
#endif
#if BUILDFLAG(IS_WIN)
#include "cef/libcef_dll/bootstrap/bootstrap_util_win.h"
#endif
namespace {
class CefSelectClientCertificateCallbackImpl
@ -214,6 +218,17 @@ void ChromeContentBrowserClientCef::AppendExtraCommandLineSwitches(
command_line->CopySwitchesFrom(*browser_cmd, kSwitchNames);
}
#if BUILDFLAG(IS_WIN)
{
const auto& module_value = bootstrap_util::GetValidatedModuleValue(
*browser_cmd, bootstrap_util::GetExePath());
if (!module_value.empty()) {
command_line->AppendSwitchNative(bootstrap_util::switches::kModule,
module_value);
}
}
#endif
const std::string& process_type =
command_line->GetSwitchValueASCII(switches::kProcessType);

View File

@ -22,9 +22,9 @@
#include "ui/base/ui_base_switches.h"
#if BUILDFLAG(IS_WIN)
#include "base/debug/alias.h"
#include "base/strings/utf_string_conversions.h"
#include "cef/include/internal/cef_win.h"
#include "cef/libcef/browser/preferred_stack_size_win.inc"
#include "chrome/chrome_elf/chrome_elf_main.h"
#include "chrome/install_static/initialize_from_primary_module.h"
#endif
@ -185,110 +185,6 @@ base::FilePath NormalizeCachePathAndSet(cef_string_t& path_str,
return path;
}
// Based on chrome/app/chrome_exe_main_win.cc.
// In 32-bit builds, the main thread starts with the default (small) stack size.
// The ARCH_CPU_32_BITS blocks here and below are in support of moving the main
// thread to a fiber with a larger stack size.
#if BUILDFLAG(IS_WIN) && defined(ARCH_CPU_32_BITS)
// The information needed to transfer control to the large-stack fiber and later
// pass the main routine's exit code back to the small-stack fiber prior to
// termination.
struct FiberState {
FiberState(wWinMainPtr wWinMain,
HINSTANCE hInstance,
LPWSTR lpCmdLine,
int nCmdShow) {
this->wWinMain = wWinMain;
this->hInstance = hInstance;
this->lpCmdLine = lpCmdLine;
this->nCmdShow = nCmdShow;
}
FiberState(mainPtr main, int argc, char** argv) {
this->main = main;
this->argc = argc;
this->argv = argv;
}
wWinMainPtr wWinMain = nullptr;
HINSTANCE hInstance;
LPWSTR lpCmdLine;
int nCmdShow;
mainPtr main = nullptr;
int argc;
char** argv;
LPVOID original_fiber;
int fiber_result;
};
// A PFIBER_START_ROUTINE function run on a large-stack fiber that calls the
// main routine, stores its return value, and returns control to the small-stack
// fiber. |params| must be a pointer to a FiberState struct.
void WINAPI FiberBinder(void* params) {
auto* fiber_state = static_cast<FiberState*>(params);
// Call the main routine from the fiber. Reusing the entry point minimizes
// confusion when examining call stacks in crash reports - seeing wWinMain on
// the stack is a handy hint that this is the main thread of the process.
if (fiber_state->main) {
fiber_state->fiber_result =
fiber_state->main(fiber_state->argc, fiber_state->argv);
} else {
fiber_state->fiber_result =
fiber_state->wWinMain(fiber_state->hInstance, nullptr,
fiber_state->lpCmdLine, fiber_state->nCmdShow);
}
// Switch back to the main thread to exit.
::SwitchToFiber(fiber_state->original_fiber);
}
int RunMainWithPreferredStackSize(FiberState& fiber_state) {
enum class FiberStatus { kConvertFailed, kCreateFiberFailed, kSuccess };
FiberStatus fiber_status = FiberStatus::kSuccess;
// GetLastError result if fiber conversion failed.
DWORD fiber_error = ERROR_SUCCESS;
if (!::IsThreadAFiber()) {
// Make the main thread's stack size 4 MiB so that it has roughly the same
// effective size as the 64-bit build's 8 MiB stack.
constexpr size_t kStackSize = 4 * 1024 * 1024; // 4 MiB
// Leak the fiber on exit.
LPVOID original_fiber =
::ConvertThreadToFiberEx(nullptr, FIBER_FLAG_FLOAT_SWITCH);
if (original_fiber) {
fiber_state.original_fiber = original_fiber;
// Create a fiber with a bigger stack and switch to it. Leak the fiber on
// exit.
LPVOID big_stack_fiber = ::CreateFiberEx(
0, kStackSize, FIBER_FLAG_FLOAT_SWITCH, FiberBinder, &fiber_state);
if (big_stack_fiber) {
::SwitchToFiber(big_stack_fiber);
// The fibers must be cleaned up to avoid obscure TLS-related shutdown
// crashes.
::DeleteFiber(big_stack_fiber);
::ConvertFiberToThread();
// Control returns here after CEF has finished running on FiberMain.
return fiber_state.fiber_result;
}
fiber_status = FiberStatus::kCreateFiberFailed;
} else {
fiber_status = FiberStatus::kConvertFailed;
}
// If we reach here then creating and switching to a fiber has failed. This
// probably means we are low on memory and will soon crash. Try to report
// this error once crash reporting is initialized.
fiber_error = ::GetLastError();
base::debug::Alias(&fiber_error);
}
// If we are already a fiber then continue normal execution.
// Intentionally crash if converting to a fiber failed.
CHECK_EQ(fiber_status, FiberStatus::kSuccess);
return -1;
}
#endif // BUILDFLAG(IS_WIN) && defined(ARCH_CPU_32_BITS)
} // namespace
NO_STACK_PROTECTOR
@ -418,23 +314,6 @@ void CefQuitMessageLoop() {
#if BUILDFLAG(IS_WIN)
#if defined(ARCH_CPU_32_BITS)
int CefRunWinMainWithPreferredStackSize(wWinMainPtr wWinMain,
HINSTANCE hInstance,
LPWSTR lpCmdLine,
int nCmdShow) {
CHECK(wWinMain && hInstance);
FiberState fiber_state(wWinMain, hInstance, lpCmdLine, nCmdShow);
return RunMainWithPreferredStackSize(fiber_state);
}
int CefRunMainWithPreferredStackSize(mainPtr main, int argc, char* argv[]) {
CHECK(main);
FiberState fiber_state(main, argc, argv);
return RunMainWithPreferredStackSize(fiber_state);
}
#endif // defined(ARCH_CPU_32_BITS)
void CefSetOSModalLoop(bool osModalLoop) {
// Verify that the context is in a valid state.
if (!CONTEXT_STATE_VALID()) {

View File

@ -0,0 +1,129 @@
#include "build/build_config.h"
// Based on chrome/app/chrome_exe_main_win.cc.
// In 32-bit builds, the main thread starts with the default (small) stack size.
// The ARCH_CPU_32_BITS blocks here and below are in support of moving the main
// thread to a fiber with a larger stack size.
#if BUILDFLAG(IS_WIN) && defined(ARCH_CPU_32_BITS)
#include "base/debug/alias.h"
#include "include/internal/cef_app_win.h"
namespace {
// The information needed to transfer control to the large-stack fiber and later
// pass the main routine's exit code back to the small-stack fiber prior to
// termination.
struct FiberState {
FiberState(wWinMainPtr wWinMain,
HINSTANCE hInstance,
LPWSTR lpCmdLine,
int nCmdShow) {
this->wWinMain = wWinMain;
this->hInstance = hInstance;
this->lpCmdLine = lpCmdLine;
this->nCmdShow = nCmdShow;
}
FiberState(mainPtr main, int argc, char** argv) {
this->main = main;
this->argc = argc;
this->argv = argv;
}
wWinMainPtr wWinMain = nullptr;
HINSTANCE hInstance;
LPWSTR lpCmdLine;
int nCmdShow;
mainPtr main = nullptr;
int argc;
char** argv;
LPVOID original_fiber;
int fiber_result;
};
// A PFIBER_START_ROUTINE function run on a large-stack fiber that calls the
// main routine, stores its return value, and returns control to the small-stack
// fiber. |params| must be a pointer to a FiberState struct.
void WINAPI FiberBinder(void* params) {
auto* fiber_state = static_cast<FiberState*>(params);
// Call the main routine from the fiber. Reusing the entry point minimizes
// confusion when examining call stacks in crash reports - seeing wWinMain on
// the stack is a handy hint that this is the main thread of the process.
if (fiber_state->main) {
fiber_state->fiber_result =
fiber_state->main(fiber_state->argc, fiber_state->argv);
} else {
fiber_state->fiber_result =
fiber_state->wWinMain(fiber_state->hInstance, nullptr,
fiber_state->lpCmdLine, fiber_state->nCmdShow);
}
// Switch back to the main thread to exit.
::SwitchToFiber(fiber_state->original_fiber);
}
int RunMainWithPreferredStackSize(FiberState& fiber_state) {
enum class FiberStatus { kConvertFailed, kCreateFiberFailed, kSuccess };
FiberStatus fiber_status = FiberStatus::kSuccess;
// GetLastError result if fiber conversion failed.
DWORD fiber_error = ERROR_SUCCESS;
if (!::IsThreadAFiber()) {
// Make the main thread's stack size 4 MiB so that it has roughly the same
// effective size as the 64-bit build's 8 MiB stack.
constexpr size_t kStackSize = 4 * 1024 * 1024; // 4 MiB
// Leak the fiber on exit.
LPVOID original_fiber =
::ConvertThreadToFiberEx(nullptr, FIBER_FLAG_FLOAT_SWITCH);
if (original_fiber) {
fiber_state.original_fiber = original_fiber;
// Create a fiber with a bigger stack and switch to it. Leak the fiber on
// exit.
LPVOID big_stack_fiber = ::CreateFiberEx(
0, kStackSize, FIBER_FLAG_FLOAT_SWITCH, FiberBinder, &fiber_state);
if (big_stack_fiber) {
::SwitchToFiber(big_stack_fiber);
// The fibers must be cleaned up to avoid obscure TLS-related shutdown
// crashes.
::DeleteFiber(big_stack_fiber);
::ConvertFiberToThread();
// Control returns here after CEF has finished running on FiberMain.
return fiber_state.fiber_result;
}
fiber_status = FiberStatus::kCreateFiberFailed;
} else {
fiber_status = FiberStatus::kConvertFailed;
}
// If we reach here then creating and switching to a fiber has failed. This
// probably means we are low on memory and will soon crash. Try to report
// this error once crash reporting is initialized.
fiber_error = ::GetLastError();
base::debug::Alias(&fiber_error);
}
// If we are already a fiber then continue normal execution.
// Intentionally crash if converting to a fiber failed.
CHECK_EQ(fiber_status, FiberStatus::kSuccess);
return -1;
}
} // namespace
int CefRunWinMainWithPreferredStackSize(wWinMainPtr wWinMain,
HINSTANCE hInstance,
LPWSTR lpCmdLine,
int nCmdShow) {
CHECK(wWinMain && hInstance);
FiberState fiber_state(wWinMain, hInstance, lpCmdLine, nCmdShow);
return RunMainWithPreferredStackSize(fiber_state);
}
int CefRunMainWithPreferredStackSize(mainPtr main, int argc, char* argv[]) {
CHECK(main);
FiberState fiber_state(main, argc, argv);
return RunMainWithPreferredStackSize(fiber_state);
}
#endif // BUILDFLAG(IS_WIN) && defined(ARCH_CPU_32_BITS)

View File

@ -0,0 +1,82 @@
// 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 "cef/libcef_dll/bootstrap/bootstrap_util_win.h"
#include "base/check_op.h"
#include "base/command_line.h"
namespace bootstrap_util {
namespace {
constexpr wchar_t kWindowsSelfName[] = TEXT("bootstrap");
constexpr wchar_t kConsoleSelfName[] = TEXT("bootstrapc");
// Returns the file name only without extension, if any.
inline std::wstring NamePart(const base::FilePath& path) {
return path.BaseName().RemoveExtension().value();
}
} // namespace
bool IsDefaultExeName(const std::wstring& name) {
return base::FilePath::CompareEqualIgnoreCase(name, kWindowsSelfName) ||
base::FilePath::CompareEqualIgnoreCase(name, kConsoleSelfName);
}
std::wstring GetModuleValue(const base::CommandLine& command_line) {
if (command_line.HasSwitch(switches::kModule)) {
const auto& value = command_line.GetSwitchValuePath(switches::kModule);
if (!value.empty()) {
return NamePart(value);
}
}
return std::wstring();
}
base::FilePath GetExePath() {
HMODULE hModule = ::GetModuleHandle(nullptr);
CHECK(hModule);
return GetModulePath(hModule);
}
base::FilePath GetModulePath(HMODULE module) {
wchar_t buffer[MAX_PATH];
DWORD length = ::GetModuleFileName(module, buffer, MAX_PATH);
CHECK_NE(length, 0U);
CHECK_LT(length, static_cast<DWORD>(MAX_PATH));
return base::FilePath(buffer);
}
std::wstring GetValidatedModuleValue(const base::CommandLine& command_line,
const base::FilePath& exe_path) {
// Allow module value configuration if the bootstrap executable has the
// default name.
const auto& value = GetModuleValue(command_line);
if (!value.empty() && IsDefaultExeName(NamePart(exe_path))) {
return value;
}
return std::wstring();
}
std::wstring GetDefaultModuleValue(const base::FilePath& exe_path) {
return NamePart(exe_path);
}
bool IsModulePathAllowed(HMODULE module, const base::FilePath& exe_path) {
// Allow any module path if the bootstrap executable has the default name.
if (IsDefaultExeName(NamePart(exe_path))) {
return true;
}
const auto& module_path = GetModulePath(module);
// Module must be at the same path as the executable.
return module_path.DirName() == exe_path.DirName();
}
} // namespace bootstrap_util

View File

@ -0,0 +1,51 @@
// 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_LIBCEF_DLL_BOOTSTRAP_BOOTSTRAP_UTIL_WIN_H_
#define CEF_LIBCEF_DLL_BOOTSTRAP_BOOTSTRAP_UTIL_WIN_H_
#pragma once
#include <windows.h>
#include <string>
#include "base/files/file_path.h"
namespace base {
class CommandLine;
}
namespace bootstrap_util {
namespace switches {
inline constexpr char kModule[] = "module";
}
// Returns true if |name| is one of the default bootstrap executable names.
bool IsDefaultExeName(const std::wstring& name);
// Returns the command-line configured module value without validation.
std::wstring GetModuleValue(const base::CommandLine& command_line);
// The following functions can only be called in unsandboxed processes.
// Returns the fully qualified file path for the executable module.
base::FilePath GetExePath();
// Returns the fully qualified file path for |module|.
base::FilePath GetModulePath(HMODULE module);
// Returns the command-line configured module value if it passes validation.
std::wstring GetValidatedModuleValue(const base::CommandLine& command_line,
const base::FilePath& exe_path);
// Returns the default module name (executable name without extension).
std::wstring GetDefaultModuleValue(const base::FilePath& exe_path);
// Returns true if loading |module| is allowed.
bool IsModulePathAllowed(HMODULE module, const base::FilePath& exe_path);
} // namespace bootstrap_util
#endif // CEF_LIBCEF_DLL_BOOTSTRAP_BOOTSTRAP_UTIL_WIN_H_

View File

@ -0,0 +1,179 @@
// 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 <iostream>
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/process/process_info.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "cef/include/cef_sandbox_win.h"
#include "cef/libcef/browser/preferred_stack_size_win.inc"
#include "cef/libcef_dll/bootstrap/bootstrap_util_win.h"
#include "cef/libcef_dll/bootstrap/win/resource.h"
namespace {
// Load a string from the string table in bootstrap.rc.
std::wstring LoadString(int string_id) {
const int kMaxSize = 100;
TCHAR buff[kMaxSize] = {0};
::LoadString(::GetModuleHandle(nullptr), string_id, buff, kMaxSize);
return buff;
}
// Replace $1-$2-$3..$9 in the format string with values from |subst|.
// Additionally, any number of consecutive '$' characters is replaced by that
// number less one. Eg $$->$, $$$->$$, etc. Supports up to 9 replacements.
std::wstring FormatErrorString(int string_id,
base::span<const std::u16string> subst) {
return base::UTF16ToWide(base::ReplaceStringPlaceholders(
base::WideToUTF16(LoadString(string_id)), subst, nullptr));
}
void ShowError(const std::wstring& error) {
const auto subst = std::to_array<std::u16string>(
{base::WideToUTF16(bootstrap_util::GetExePath().BaseName().value())});
const auto& title = FormatErrorString(IDS_ERROR_TITLE, subst);
const auto& extra_info = LoadString(IDS_ERROR_EXTRA_INFO);
#if defined(CEF_BUILD_BOOTSTRAP_CONSOLE)
std::wcerr << title.c_str() << ": " << error << extra_info;
#else
const std::wstring& msg = error + extra_info;
::MessageBox(nullptr, msg.c_str(), title.c_str(), MB_ICONERROR | MB_OK);
#endif
}
} // namespace
#if defined(CEF_BUILD_BOOTSTRAP_CONSOLE)
int main(int argc, char* argv[]) {
#else // !defined(CEF_BUILD_BOOTSTRAP_CONSOLE)
// Entry point function for all processes.
int APIENTRY wWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow) {
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
#endif // !defined(CEF_BUILD_BOOTSTRAP_CONSOLE)
#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.
#if defined(CEF_BUILD_BOOTSTRAP_CONSOLE)
int exit_code = CefRunMainWithPreferredStackSize(main, argc, argv);
#else
int exit_code = CefRunWinMainWithPreferredStackSize(wWinMain, hInstance,
lpCmdLine, nCmdShow);
#endif
if (exit_code >= 0) {
// The fiber has completed so return here.
return exit_code;
}
#endif
// Parse command-line arguments.
const base::CommandLine command_line =
base::CommandLine::FromString(::GetCommandLineW());
// True if this is a sandboxed sub-process. Uses similar logic to
// Sandbox::IsProcessSandboxed.
const bool is_sandboxed =
command_line.HasSwitch("type") &&
base::GetCurrentProcessIntegrityLevel() < base::MEDIUM_INTEGRITY;
std::wstring dll_name;
base::FilePath exe_path;
if (is_sandboxed) {
// Running as a sandboxed sub-process. May already be locked down, so we
// can't call WinAPI functions. The command-line will already have been
// validated in ChromeContentBrowserClientCef::
// AppendExtraCommandLineSwitches. Retrieve the module value without
// additional validation.
dll_name = bootstrap_util::GetModuleValue(command_line);
if (dll_name.empty()) {
// Default to the command-line program name without extension.
dll_name = command_line.GetProgram().BaseName().RemoveExtension().value();
}
} else {
// Running as the main process or unsandboxed sub-process.
exe_path = bootstrap_util::GetExePath();
// Retrieve the module name with validation.
dll_name = bootstrap_util::GetValidatedModuleValue(command_line, exe_path);
if (dll_name.empty()) {
// Default to the executable module file name without extension. This is
// safer than relying on the command-line program name.
dll_name = bootstrap_util::GetDefaultModuleValue(exe_path);
}
if (bootstrap_util::IsDefaultExeName(dll_name)) {
ShowError(LoadString(IDS_ERROR_NO_MODULE_NAME));
return 1;
}
}
// 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.
CefScopedSandboxInfo scoped_sandbox;
void* sandbox_info = scoped_sandbox.sandbox_info();
#if defined(CEF_BUILD_BOOTSTRAP_CONSOLE)
constexpr char kProcName[] = "RunConsoleMain";
using kProcType = decltype(&RunConsoleMain);
#else
constexpr char kProcName[] = "RunWinMain";
using kProcType = decltype(&RunWinMain);
#endif
std::wstring error;
if (HMODULE hModule = ::LoadLibrary(dll_name.c_str())) {
if (is_sandboxed ||
bootstrap_util::IsModulePathAllowed(hModule, exe_path)) {
if (auto* pFunc = (kProcType)::GetProcAddress(hModule, kProcName)) {
#if defined(CEF_BUILD_BOOTSTRAP_CONSOLE)
return pFunc(argc, argv, sandbox_info);
#else
return pFunc(hInstance, lpCmdLine, nCmdShow, sandbox_info);
#endif
} else if (!is_sandboxed) {
const auto subst = std::to_array<std::u16string>(
{base::WideToUTF16(dll_name),
base::NumberToString16(::GetLastError()),
base::ASCIIToUTF16(std::string(kProcName))});
error = FormatErrorString(IDS_ERROR_NO_PROC_EXPORT, subst);
}
} else if (!is_sandboxed) {
const auto subst =
std::to_array<std::u16string>({base::WideToUTF16(dll_name)});
error = FormatErrorString(IDS_ERROR_INVALID_LOCATION, subst);
}
FreeLibrary(hModule);
} else if (!is_sandboxed) {
const auto subst = std::to_array<std::u16string>(
{base::WideToUTF16(dll_name),
base::NumberToString16(::GetLastError())});
error = FormatErrorString(IDS_ERROR_LOAD_FAILED, subst);
}
// Don't try to show errors while sandboxed.
if (!error.empty() && !is_sandboxed) {
ShowError(error);
}
return 1;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -0,0 +1,133 @@
// Microsoft Visual C++ generated resource script.
//
#include "libcef_dll/bootstrap/win/resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#define APSTUDIO_HIDDEN_SYMBOLS
#include "windows.h"
#include "include/cef_version.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_APPLICATION ICON "bootstrap.ico"
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION CEF_VERSION_MAJOR,CEF_VERSION_MINOR,CEF_VERSION_PATCH,0
PRODUCTVERSION CEF_VERSION_MAJOR,CEF_VERSION_MINOR,CEF_VERSION_PATCH,0
FILEFLAGSMASK 0x17L
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x4L
FILETYPE 0x2L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "FileDescription", "CEF Bootstrap Application"
VALUE "FileVersion", CEF_VERSION
#if defined(CEF_BUILD_BOOTSTRAP_CONSOLE)
VALUE "InternalName", "bootstrapc"
VALUE "OriginalFilename", "bootstrapc.exe"
#else
VALUE "InternalName", "bootstrap"
VALUE "OriginalFilename", "bootstrap.exe"
#endif
VALUE "LegalCopyright", "Copyright (C) " MAKE_STRING(COPYRIGHT_YEAR) " The Chromium Embedded Framework Authors"
VALUE "ProductName", "CEF Bootstrap Application"
VALUE "ProductVersion", CEF_VERSION
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END
/////////////////////////////////////////////////////////////////////////////
//
// String Table
//
STRINGTABLE
BEGIN
IDS_ERROR_TITLE "Runtime Error: $1"
IDS_ERROR_EXTRA_INFO "\nTry reinstalling the application to fix this error."
IDS_ERROR_NO_MODULE_NAME "Missing module name"
IDS_ERROR_NO_PROC_EXPORT "Failed to find $3 in $1.dll (error $2)"
IDS_ERROR_INVALID_LOCATION "Found $1.dll in an unexpected location"
IDS_ERROR_LOAD_FAILED "Failed to load $1.dll (error $2)"
END
#endif // English (U.S.) resources
/////////////////////////////////////////////////////////////////////////////
#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
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

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.
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by cefclient.rc
//
#define IDS_ERROR_TITLE 100
#define IDS_ERROR_EXTRA_INFO 101
#define IDS_ERROR_NO_MODULE_NAME 102
#define IDS_ERROR_NO_PROC_EXPORT 103
#define IDS_ERROR_INVALID_LOCATION 104
#define IDS_ERROR_LOAD_FAILED 105
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NO_MFC 1
#define _APS_NEXT_RESOURCE_VALUE 106
#define _APS_NEXT_COMMAND_VALUE 32000
#define _APS_NEXT_CONTROL_VALUE 1000
#define _APS_NEXT_SYMED_VALUE 100
#endif
#endif

View File

@ -261,7 +261,7 @@ void OsrWindowWin::Create(HWND parent_hwnd, const RECT& rect) {
DCHECK(parent_hwnd);
DCHECK(!::IsRectEmpty(&rect));
HINSTANCE hInst = ::GetModuleHandle(nullptr);
HINSTANCE hInst = GetCodeModuleHandle();
const cef_color_t background_color = MainContext::Get()->GetBackgroundColor();
const HBRUSH background_brush = CreateSolidBrush(

View File

@ -487,7 +487,7 @@ void RootWindowWin::CreateRootWindow(const CefBrowserSettings& settings,
REQUIRE_MAIN_THREAD();
DCHECK(!hwnd_);
HINSTANCE hInstance = GetModuleHandle(nullptr);
HINSTANCE hInstance = GetCodeModuleHandle();
// Load strings from the resource file.
const std::wstring& window_title = GetResourceString(IDS_APP_TITLE);
@ -1079,7 +1079,7 @@ void RootWindowWin::OnFindEvent() {
void RootWindowWin::OnAbout() {
// Show the about box.
DialogBox(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd_,
DialogBox(GetCodeModuleHandle(), MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd_,
AboutWndProc);
}

View File

@ -7,6 +7,7 @@
#include <windows.h>
#include "include/base/cef_logging.h"
#include "tests/shared/browser/util_win.h"
namespace client {
@ -16,7 +17,7 @@ const wchar_t kWndClass[] = L"Client_TempWindow";
// Create the temp window.
HWND CreateTempWindow() {
HINSTANCE hInstance = ::GetModuleHandle(nullptr);
HINSTANCE hInstance = GetCodeModuleHandle();
WNDCLASSEX wc = {0};
wc.cbSize = sizeof(wc);

View File

@ -20,33 +20,12 @@
#include "tests/shared/common/client_switches.h"
#include "tests/shared/renderer/client_app_renderer.h"
// When generating projects with CMake the CEF_USE_SANDBOX value will be defined
// automatically if using the required compiler version. Pass -DUSE_SANDBOX=OFF
// to the CMake command-line to disable use of the sandbox.
// Uncomment this line to manually enable sandbox support.
// #define CEF_USE_SANDBOX 1
#if defined(CEF_USE_SANDBOX)
// The cef_sandbox.lib static library may not link successfully with all VS
// versions.
#pragma comment(lib, "cef_sandbox.lib")
#endif
namespace client {
namespace {
int RunMain(HINSTANCE hInstance, int nCmdShow) {
int RunMain(HINSTANCE hInstance, int nCmdShow, void* sandbox_info) {
CefMainArgs main_args(hInstance);
void* sandbox_info = nullptr;
#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.
CefScopedSandboxInfo scoped_sandbox;
sandbox_info = scoped_sandbox.sandbox_info();
#endif
// Parse command-line arguments.
CefRefPtr<CefCommandLine> command_line = CefCommandLine::CreateCommandLine();
command_line->InitFromString(::GetCommandLineW());
@ -73,9 +52,9 @@ int RunMain(HINSTANCE hInstance, int nCmdShow) {
CefSettings settings;
#if !defined(CEF_USE_SANDBOX)
settings.no_sandbox = true;
#endif
if (!sandbox_info) {
settings.no_sandbox = true;
}
// Populate the settings based on command line arguments.
context->PopulateSettings(&settings);
@ -131,6 +110,18 @@ int RunMain(HINSTANCE hInstance, int nCmdShow) {
} // namespace
} // namespace client
#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) {
return client::RunMain(hInstance, nCmdShow, sandbox_info);
}
#else // !defined(CEF_USE_BOOTSTRAP)
// Program entry point function.
int APIENTRY wWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
@ -155,5 +146,16 @@ int APIENTRY wWinMain(HINSTANCE hInstance,
}
#endif
return client::RunMain(hInstance, nCmdShow);
void* sandbox_info = nullptr;
#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.
CefScopedSandboxInfo scoped_sandbox;
sandbox_info = scoped_sandbox.sandbox_info();
#endif
return client::RunMain(hInstance, nCmdShow, sandbox_info);
}
#endif // !defined(CEF_USE_BOOTSTRAP)

View File

@ -8,53 +8,14 @@
#include "include/cef_sandbox_win.h"
#include "tests/cefsimple/simple_app.h"
// When generating projects with CMake the CEF_USE_SANDBOX value will be defined
// automatically if using the required compiler version. Pass -DUSE_SANDBOX=OFF
// to the CMake command-line to disable use of the sandbox.
// Uncomment this line to manually enable sandbox support.
// #define CEF_USE_SANDBOX 1
#if defined(CEF_USE_SANDBOX)
// The cef_sandbox.lib static library may not link successfully with all VS
// versions.
#pragma comment(lib, "cef_sandbox.lib")
#endif
// Entry point function for all processes.
int APIENTRY wWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow) {
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
namespace {
int RunMain(HINSTANCE hInstance,
LPTSTR lpCmdLine,
int nCmdShow,
void* sandbox_info) {
int exit_code;
#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.
exit_code = CefRunWinMainWithPreferredStackSize(wWinMain, hInstance,
lpCmdLine, nCmdShow);
if (exit_code >= 0) {
// The fiber has completed so return here.
return exit_code;
}
#endif
void* sandbox_info = nullptr;
#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.
CefScopedSandboxInfo scoped_sandbox;
sandbox_info = scoped_sandbox.sandbox_info();
#endif
// Provide CEF with command-line arguments.
CefMainArgs main_args(hInstance);
@ -74,9 +35,9 @@ int APIENTRY wWinMain(HINSTANCE hInstance,
// Specify CEF global settings here.
CefSettings settings;
#if !defined(CEF_USE_SANDBOX)
settings.no_sandbox = true;
#endif
if (!sandbox_info) {
settings.no_sandbox = true;
}
// SimpleApp implements application-level callbacks for the browser process.
// It will create the first browser instance in OnContextInitialized() after
@ -99,3 +60,55 @@ int APIENTRY wWinMain(HINSTANCE hInstance,
return 0;
}
} // namespace
#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) {
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 = CefRunWinMainWithPreferredStackSize(wWinMain, hInstance,
lpCmdLine, nCmdShow);
if (exit_code >= 0) {
// The fiber has completed so return here.
return exit_code;
}
#endif
void* sandbox_info = nullptr;
#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.
CefScopedSandboxInfo scoped_sandbox;
sandbox_info = scoped_sandbox.sandbox_info();
#endif
return ::RunMain(hInstance, lpCmdLine, nCmdShow, sandbox_info);
}
#endif // !defined(CEF_USE_BOOTSTRAP)

View File

@ -21,8 +21,10 @@
#elif defined(OS_LINUX)
#include <X11/keysym.h>
#elif defined(OS_WIN)
#include "tests/shared/browser/util_win.h"
// Required for resource_util_win, which uses this as an extern
HINSTANCE hInst = ::GetModuleHandle(nullptr);
HINSTANCE hInst = client::GetCodeModuleHandle();
#endif
// Set to 1 to enable verbose debugging info logging.

View File

@ -39,15 +39,9 @@
#include "include/wrapper/cef_library_loader.h"
#endif
// When generating projects with CMake the CEF_USE_SANDBOX value will be defined
// automatically if using the required compiler version. Pass -DUSE_SANDBOX=OFF
// to the CMake command-line to disable use of the sandbox.
#if defined(OS_WIN) && defined(CEF_USE_SANDBOX)
#if defined(OS_WIN)
#include "include/cef_sandbox_win.h"
// The cef_sandbox.lib static library may not link successfully with all VS
// versions.
#pragma comment(lib, "cef_sandbox.lib")
#include "tests/shared/browser/util_win.h"
#endif
#if defined(OS_MAC)
@ -132,31 +126,13 @@ class ScopedPlatformSetup final {
};
#endif // defined(OS_MAC)
} // namespace
NO_STACK_PROTECTOR
int main(int argc, char* argv[]) {
int RunMain(int argc, char* argv[], void* sandbox_info) {
int exit_code;
#if CEF_API_VERSION != CEF_EXPERIMENTAL
printf("Running with configured CEF API version %d\n", CEF_API_VERSION);
#endif
#if defined(OS_WIN) && 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.
exit_code = CefRunMainWithPreferredStackSize(main, argc, argv);
if (exit_code >= 0) {
// The fiber has completed so return here.
return exit_code;
}
#endif
#if defined(OS_MAC)
// Load the CEF framework library at runtime instead of linking directly
// as required by the macOS sandbox implementation.
@ -170,19 +146,11 @@ int main(int argc, char* argv[]) {
CefTestSuite test_suite(argc, argv);
#if defined(OS_WIN)
CefMainArgs main_args(::GetModuleHandle(nullptr));
CefMainArgs main_args(client::GetCodeModuleHandle());
#else
CefMainArgs main_args(argc, argv);
#endif
void* windows_sandbox_info = nullptr;
#if defined(OS_WIN) && defined(CEF_USE_SANDBOX)
// Manages the life span of the sandbox information object.
CefScopedSandboxInfo scoped_sandbox;
windows_sandbox_info = scoped_sandbox.sandbox_info();
#endif
// Create a ClientApp of the correct type.
CefRefPtr<CefApp> app;
client::ClientApp::ProcessType process_type =
@ -198,7 +166,7 @@ int main(int argc, char* argv[]) {
}
// Execute the secondary process, if any.
exit_code = CefExecuteProcess(main_args, app, windows_sandbox_info);
exit_code = CefExecuteProcess(main_args, app, sandbox_info);
if (exit_code >= 0) {
return exit_code;
}
@ -211,7 +179,11 @@ int main(int argc, char* argv[]) {
CefSettings settings;
#if !defined(CEF_USE_SANDBOX)
#if defined(OS_WIN)
if (!sandbox_info) {
settings.no_sandbox = true;
}
#elif !defined(CEF_USE_SANDBOX)
settings.no_sandbox = true;
#endif
@ -231,7 +203,7 @@ int main(int argc, char* argv[]) {
#endif
// Initialize CEF.
if (!CefInitialize(main_args, settings, app, windows_sandbox_info)) {
if (!CefInitialize(main_args, settings, app, sandbox_info)) {
exit_code = CefGetExitCode();
LOG(ERROR) << "CefInitialize exited with code " << exit_code;
return exit_code;
@ -304,3 +276,47 @@ int main(int argc, char* argv[]) {
return retval;
}
} // namespace
#if defined(OS_WIN) && defined(CEF_USE_BOOTSTRAP)
// Entry point called by bootstrapc.exe when built as a DLL.
CEF_BOOTSTRAP_EXPORT int RunConsoleMain(int argc,
char* argv[],
void* sandbox_info) {
return ::RunMain(argc, argv, sandbox_info);
}
#else // !(defined(OS_WIN) && defined(CEF_USE_BOOTSTRAP))
// Program entry point function.
NO_STACK_PROTECTOR
int main(int argc, char* argv[]) {
#if defined(OS_WIN) && 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.
exit_code = CefRunMainWithPreferredStackSize(main, argc, argv);
if (exit_code >= 0) {
// The fiber has completed so return here.
return exit_code;
}
#endif
void* sandbox_info = nullptr;
#if defined(OS_WIN) && defined(CEF_USE_SANDBOX)
// Manages the life span of the sandbox information object.
CefScopedSandboxInfo scoped_sandbox;
sandbox_info = scoped_sandbox.sandbox_info();
#endif
return ::RunMain(argc, argv, sandbox_info);
}
#endif // !(defined(OS_WIN) && defined(CEF_USE_BOOTSTRAP))

View File

@ -51,7 +51,7 @@ class MainMessageLoopExternalPumpWin : public MainMessageLoopExternalPump {
};
MainMessageLoopExternalPumpWin::MainMessageLoopExternalPumpWin() {
HINSTANCE hInstance = GetModuleHandle(nullptr);
HINSTANCE hInstance = GetCodeModuleHandle();
const wchar_t* const kClassName = L"CEFMainTargetHWND";
WNDCLASSEX wcex = {};

View File

@ -8,13 +8,14 @@
#include "include/cef_stream.h"
#include "include/wrapper/cef_byte_read_handler.h"
#include "include/wrapper/cef_stream_resource_handler.h"
#include "tests/shared/browser/util_win.h"
namespace client {
namespace {
bool LoadBinaryResource(int binaryId, DWORD& dwSize, LPBYTE& pBytes) {
HINSTANCE hInst = GetModuleHandle(nullptr);
HINSTANCE hInst = GetCodeModuleHandle();
HRSRC hRes =
FindResource(hInst, MAKEINTRESOURCE(binaryId), MAKEINTRESOURCE(256));
if (hRes) {

View File

@ -44,7 +44,7 @@ WNDPROC SetWndProcPtr(HWND hWnd, WNDPROC wndProc) {
std::wstring GetResourceString(UINT id) {
#define MAX_LOADSTRING 100
TCHAR buff[MAX_LOADSTRING] = {0};
LoadString(::GetModuleHandle(nullptr), id, buff, MAX_LOADSTRING);
LoadString(GetCodeModuleHandle(), id, buff, MAX_LOADSTRING);
return buff;
}
@ -191,4 +191,13 @@ float GetDeviceScaleFactor() {
return scale_factor;
}
HINSTANCE GetCodeModuleHandle() {
HMODULE hModule = nullptr;
CHECK(::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
reinterpret_cast<LPCWSTR>(GetCodeModuleHandle),
&hModule));
return hModule;
}
} // namespace client

View File

@ -40,6 +40,10 @@ bool IsKeyDown(WPARAM wparam);
// return 2.0.
float GetDeviceScaleFactor();
// Returns the module handle that contains this code. When built as a DLL this
// will be the DLL handle instead of the EXE handle.
HINSTANCE GetCodeModuleHandle();
} // namespace client
#endif // CEF_TESTS_SHARED_BROWSER_UTIL_WIN_H_

View File

@ -16,13 +16,11 @@ import sys
import tempfile
import zipfile
is_python2 = sys.version_info.major == 2
if sys.version_info.major != 3:
sys.stderr.write('Python3 is required!')
sys.exit(1)
if is_python2:
from urllib import FancyURLopener
from urllib2 import urlopen
else:
from urllib.request import FancyURLopener, urlopen
from urllib.request import FancyURLopener, urlopen
##
# Default URLs.
@ -218,19 +216,12 @@ def read_file(path):
raise Exception("Path does not exist: %s" % (path))
def write_fp(fp, data):
if is_python2:
fp.write(data.decode('utf-8'))
else:
fp.write(data)
def write_file(path, data):
""" Write a file. """
msg('Writing %s' % path)
if not options.dryrun:
with open(path, 'w', encoding='utf-8') as fp:
write_fp(fp, data)
fp.write(data)
def read_config_file(path):
@ -446,21 +437,20 @@ def check_pattern_matches(output_file=None):
if not skip:
if write_msg:
if has_output:
write_fp(fp, '\n')
write_fp(fp,
'!!!! WARNING: FOUND PATTERN: %s\n' % entry['pattern'])
fp.write('\n')
fp.write('!!!! WARNING: FOUND PATTERN: %s\n' % entry['pattern'])
if 'message' in entry:
write_fp(fp, entry['message'] + '\n')
write_fp(fp, '\n')
fp.write(entry['message'] + '\n')
fp.write('\n')
write_msg = False
write_fp(fp, line + '\n')
fp.write(line + '\n')
has_output = True
if not output_file is None:
if has_output:
msg('ERROR Matches found. See %s for output.' % out_file)
else:
write_fp(fp, 'Good news! No matches.\n')
fp.write('Good news! No matches.\n')
fp.close()
if has_output:
@ -901,27 +891,13 @@ if not branch_is_master:
sys.exit(1)
# Verify the minimum supported branch number.
if int(cef_branch) < 3071:
if int(cef_branch) < 5060:
print('The requested branch (%s) is too old to build using this tool. ' +
'The minimum supported branch is 3071.' % cef_branch)
sys.exit(1)
# True if the requested branch is 3538 or newer.
branch_is_3538_or_newer = (branch_is_master or int(cef_branch) >= 3538)
# True if the requested branch is 3945 or newer.
branch_is_3945_or_newer = (branch_is_master or int(cef_branch) >= 3945)
# Enable Python 3 usage in Chromium for branches 3945 and newer.
if branch_is_3945_or_newer and not is_python2 and \
not 'GCLIENT_PY3' in os.environ.keys():
os.environ['GCLIENT_PY3'] = '1'
if not branch_is_3945_or_newer and \
(not is_python2 or bool(int(os.environ.get('GCLIENT_PY3', '0')))):
print('Python 3 is not supported with branch 3904 and older ' +
'(set GCLIENT_PY3=0 and run with Python 2 executable).')
sys.exit(1)
# True if the requested branch is 7151 or older.
branch_is_7151_or_older = not branch_is_master and int(cef_branch) <= 7151
if options.armbuild:
if platform != 'linux':
@ -936,9 +912,9 @@ if platform == 'mac' and not (options.x64build or options.arm64build):
sys.exit(1)
# Platforms that build a cef_sandbox library.
sandbox_lib_platforms = ['windows']
if branch_is_3538_or_newer:
sandbox_lib_platforms.append('mac')
sandbox_lib_platforms = ['mac']
if branch_is_7151_or_older:
sandbox_lib_platforms.append('win')
if not platform in sandbox_lib_platforms and (options.sandboxdistrib or
options.sandboxdistribonly):
@ -1023,7 +999,7 @@ if platform == 'windows':
# Force use of the system installed Git version.
git_exe = 'git.exe'
# Force use of the Python version bundled with depot_tools.
python_bat = 'python.bat' if is_python2 else 'python3.bat'
python_bat = 'python3.bat'
python_exe = os.path.join(depot_tools_dir, python_bat)
if options.dryrun and not os.path.exists(python_exe):
sys.stdout.write("WARNING: --dry-run assumes that depot_tools" \
@ -1180,7 +1156,7 @@ if not os.path.exists(gclient_file) or options.forceconfig:
msg('Writing %s' % gclient_file)
if not options.dryrun:
with open(gclient_file, 'w', encoding='utf-8') as fp:
write_fp(fp, gclient_spec)
fp.write(gclient_spec)
# Initial Chromium checkout.
if not options.nochromiumupdate and not os.path.exists(chromium_src_dir):
@ -1366,6 +1342,8 @@ if not options.nobuild and (chromium_checkout_changed or \
target += ' ' + options.testtarget
if platform == 'linux':
target += ' chrome_sandbox'
if platform == 'windows' and not branch_is_7151_or_older:
target += ' bootstrap bootstrapc'
# Make a CEF Debug build.
if not options.nodebugbuild:

View File

@ -66,12 +66,6 @@ if platform == 'windows':
#
# set WIN_CUSTOM_TOOLCHAIN=1
#
# o Used by tools/msvs_env.bat to configure the MSVS tools environment.
# Should be set to "none" because VC variables for CEF will be set via
# INCLUDE/LIB/PATH.
#
# set CEF_VCVARS=none
#
# o Used by the following scripts:
# (a) build/vs_toolchain.py SetEnvironmentAndGetRuntimeDllDirs when
# determining whether to copy VS runtime binaries to the output directory.

View File

@ -550,17 +550,6 @@ def GetConfigArgsSandbox(platform, args, is_debug, cpu):
'is_cef_sandbox_build': True,
}
if platform == 'windows':
# Avoid Debug build linker errors caused by custom libc++.
add_args['use_custom_libcxx'] = False
# Avoid dependency on //third_party/perfetto:libperfetto which fails to
# build with MSVC libc++.
add_args['enable_base_tracing'] = False
# Allow non-component Debug builds for the sandbox.
add_args['forbid_non_component_debug_builds'] = False
if not is_debug:
# Disable DCHECKs in Release builds.
add_args['dcheck_always_on'] = False
@ -647,8 +636,7 @@ def GetAllPlatformConfigs(build_args, quiet=False):
result['Debug_GN_' + cpu] = GetConfigArgs(args, True, cpu)
result['Release_GN_' + cpu] = GetConfigArgs(args, False, cpu)
if platform in ('windows', 'mac') and GetArgValue(args,
'is_official_build'):
if platform == 'mac' and GetArgValue(args, 'is_official_build'):
# Build cef_sandbox.lib with a different configuration.
if create_debug:
result['Debug_GN_' + cpu + '_sandbox'] = GetConfigArgsSandbox(

View File

@ -561,10 +561,7 @@ def get_undefined_symbols(file):
def combine_libs(platform, build_dir, libs, dest_lib):
""" Combine multiple static libraries into a single static library. """
intermediate_obj = None
if platform == 'windows':
cmdline = 'msvs_env.bat win%s "%s" combine_libs.py -b "%s" -o "%s"' % (
platform_arch, sys.executable, build_dir, dest_lib)
elif platform == 'mac':
if platform == 'mac':
# Find CEF_EXPORT symbols from libcef_sandbox.a (include/cef_sandbox_mac.h)
# Export only symbols that include these strings.
symbol_match = [
@ -589,14 +586,14 @@ def combine_libs(platform, build_dir, libs, dest_lib):
cmdline = 'ld -arch %s -r -o "%s"' % (arch, intermediate_obj)
for symbol in symbols:
cmdline += ' -exported_symbol %s' % symbol
else:
raise Exception('Unsupported platform for combine_libs: %s' % platform)
for lib in libs:
lib_path = os.path.join(build_dir, lib)
for path in get_files(lib_path): # Expand wildcards in |lib_path|.
if not path_exists(path):
raise Exception('File not found: ' + path)
if platform == 'windows':
path = os.path.relpath(path, build_dir)
cmdline += ' "%s"' % path
run(cmdline, os.path.join(cef_dir, 'tools'))
@ -746,7 +743,7 @@ parser.add_option(
action='store_true',
dest='sandbox',
default=False,
help='include only the cef_sandbox static library (macOS and Windows only)')
help='include only the cef_sandbox static library (macOS only)')
parser.add_option(
'--tools',
action='store_true',
@ -794,8 +791,8 @@ if options.armbuild and platform != 'linux':
print_error('--arm-build is only supported on Linux.')
sys.exit()
if options.sandbox and not platform in ('mac', 'windows'):
print_error('--sandbox is only supported on macOS and Windows.')
if options.sandbox and platform != 'mac':
print_error('--sandbox is only supported on macOS.')
sys.exit()
if not options.ninjabuild:
@ -1130,6 +1127,8 @@ elif platform == 'windows':
{'path': 'vulkan-1.dll'},
]
pdb_files = [
{'path': 'bootstrap.exe.pdb'},
{'path': 'bootstrapc.exe.pdb'},
{'path': 'chrome_elf.dll.pdb'},
{'path': 'dxcompiler.dll.pdb', 'conditional': True},
{'path': '%s.pdb' % libcef_dll},
@ -1145,7 +1144,13 @@ elif platform == 'windows':
'path': 'cefsimple.exe' if platform_arch == 'arm64' else 'cefclient.exe'
})
else:
binaries.append({'path': '%s.lib' % libcef_dll, 'out_path': 'libcef.lib'})
# yapf: disable
binaries.extend([
{'path': 'bootstrap.exe'},
{'path': 'bootstrapc.exe'},
{'path': '%s.lib' % libcef_dll, 'out_path': 'libcef.lib'},
])
# yapf: enable
# yapf: disable
resources = [
@ -1157,43 +1162,6 @@ elif platform == 'windows':
]
# yapf: enable
cef_sandbox_lib = 'obj\\cef\\cef_sandbox.lib'
sandbox_libs = [
'obj\\base\\base.lib',
'obj\\base\\base_static.lib',
'obj\\base\\third_party\\cityhash\\cityhash\\*.obj',
'obj\\base\\third_party\\double_conversion\\double_conversion.lib',
'obj\\base\\third_party\\superfasthash\\superfasthash\\*.obj',
'obj\\base\\win\\pe_image.lib',
cef_sandbox_lib,
'obj\\sandbox\\common\\*.obj',
'obj\\sandbox\\win\\sandbox.lib',
'obj\\sandbox\\win\\service_resolver\\*.obj',
'obj\\third_party\\abseil-cpp\\absl\\base\\**\\*.obj',
'obj\\third_party\\abseil-cpp\\absl\\debugging\\**\\*.obj',
'obj\\third_party\\abseil-cpp\\absl\\numeric\\**\\*.obj',
'obj\\third_party\\abseil-cpp\\absl\\strings\\**\\*.obj',
'obj\\third_party\\abseil-cpp\\absl\\synchronization\\**\\*.obj',
'obj\\third_party\\abseil-cpp\\absl\\time\\**\\*.obj',
'obj\\third_party\\abseil-cpp\\absl\\types\\**\\*.obj',
]
# Generate the cef_sandbox.lib merged library. A separate *_sandbox build
# should exist when GN is_official_build=true.
if mode in ('standard', 'minimal', 'sandbox') and not options.nosandbox:
dirs = {
'Debug': (build_dir_debug + '_sandbox', build_dir_debug),
'Release': (build_dir_release + '_sandbox', build_dir_release)
}
for dir_name in dirs.keys():
for src_dir in dirs[dir_name]:
if path_exists(os.path.join(src_dir, cef_sandbox_lib)):
dst_dir = os.path.join(output_dir, dir_name)
make_dir(dst_dir, options.quiet)
combine_libs(platform, src_dir, sandbox_libs,
os.path.join(dst_dir, 'cef_sandbox.lib'))
break
valid_build_dir = None
if mode == 'standard':
@ -1214,23 +1182,22 @@ elif platform == 'windows':
else:
sys.stdout.write("No Debug build files.\n")
if mode != 'sandbox':
# transfer Release files
build_dir = build_dir_release
if not options.allowpartial or path_exists(
os.path.join(build_dir, libcef_dll)):
valid_build_dir = build_dir
dst_dir = os.path.join(output_dir, 'Release')
copy_files_list(build_dir, dst_dir, binaries)
# transfer Release files
build_dir = build_dir_release
if not options.allowpartial or path_exists(
os.path.join(build_dir, libcef_dll)):
valid_build_dir = build_dir
dst_dir = os.path.join(output_dir, 'Release')
copy_files_list(build_dir, dst_dir, binaries)
if not options.nosymbols:
# create the symbol output directory
symbol_output_dir = create_output_dir(
output_dir_name + '_release_symbols', options.outputdir)
# transfer contents
copy_files_list(build_dir, symbol_output_dir, pdb_files)
else:
sys.stdout.write("No Release build files.\n")
if not options.nosymbols:
# create the symbol output directory
symbol_output_dir = create_output_dir(
output_dir_name + '_release_symbols', options.outputdir)
# transfer contents
copy_files_list(build_dir, symbol_output_dir, pdb_files)
else:
sys.stdout.write("No Release build files.\n")
if not valid_build_dir is None:
# transfer resource files

View File

@ -1,73 +0,0 @@
@echo off
:: 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.
:: Set up the environment for use with MSVS tools and then execute whatever
:: was specified on the command-line.
set RC=
:: Support !! syntax for delayed variable expansion.
setlocal enabledelayedexpansion
:: Require that platform is passed as the first argument.
if "%1" == "win32" (
set vcvarsbat=vcvars32.bat
) else if "%1" == "win64" (
set vcvarsbat=vcvars64.bat
) else if "%1" == "winarm64" (
set vcvarsbat=vcvarsamd64_arm64.bat
) else (
echo ERROR: Please specify a target platform: win32, win64 or winarm64
set ERRORLEVEL=1
goto end
)
:: Check if vcvars is already provided via the environment.
set vcvars="%CEF_VCVARS%"
if %vcvars% == "none" goto found_vcvars
if exist %vcvars% goto found_vcvars
:: Search for the default VS installation path.
for %%x in (2022) do (
for %%y in ("%PROGRAMFILES%" "%PROGRAMFILES(X86)%") do (
for %%z in (Professional Enterprise Community BuildTools) do (
set vcvars="%%~y\Microsoft Visual Studio\%%x\%%z\VC\Auxiliary\Build\%vcvarsbat%"
if exist !vcvars! goto found_vcvars
)
)
)
echo ERROR: Failed to find vcvars
set ERRORLEVEL=1
goto end
:found_vcvars
echo vcvars:
echo %vcvars%
if not %vcvars% == "none" (
:: Set this variable to keep VS2017 < 15.5 from changing the current working directory.
set "VSCMD_START_DIR=%CD%"
call %vcvars%
)
echo PATH:
echo %PATH%
:: Remove the first argument and execute the command.
for /f "tokens=1,* delims= " %%a in ("%*") do set ALL_BUT_FIRST=%%b
echo command:
echo %ALL_BUT_FIRST%
%ALL_BUT_FIRST%
:end
endlocal & set RC=%ERRORLEVEL%
goto omega
:returncode
exit /B %RC%
:omega
call :returncode %RC%