feat: Add configurable CEF command line flags (#16)

## Summary
- Add CommandLineConfig struct for CEF command line switches
- Use direct struct initialization with optional helper methods
- Change default to secure: only use-mock-keychain enabled on macOS debug builds
- Add comprehensive documentation with usage examples

## Usage
```rust
use bevy_cef::prelude::*;

// Default (secure, includes use-mock-keychain on macOS debug)
app.add_plugins((DefaultPlugins, CefPlugin::default()));

// Add switches while preserving defaults (recommended)
app.add_plugins((
    DefaultPlugins,
    CefPlugin {
        command_line_config: CommandLineConfig::default()
            .with_switch("disable-gpu")
            .with_switch_value("remote-debugging-port", "9222"),
    },
));

// Full customization with direct initialization
app.add_plugins((
    DefaultPlugins,
    CefPlugin {
        command_line_config: CommandLineConfig {
            switches: vec!["disable-gpu"],
            switch_values: vec![("remote-debugging-port", "9222")],
        },
    },
));
```
This commit is contained in:
elm
2026-02-03 02:06:08 +09:00
committed by GitHub
parent 5be6380474
commit 12b29c1ffc
16 changed files with 99 additions and 19 deletions

2
.gitignore vendored
View File

@@ -2,3 +2,5 @@ target/
.idea/ .idea/
book/ book/
.DS_Store**/.DS_Store .DS_Store**/.DS_Store
.claude/memo.md
docs/plans/

View File

@@ -2,6 +2,7 @@ mod app;
mod browser_process_handler; mod browser_process_handler;
mod browsers; mod browsers;
mod client_handler; mod client_handler;
mod command_line_config;
mod context_menu_handler; mod context_menu_handler;
mod display_handler; mod display_handler;
mod localhost; mod localhost;
@@ -13,6 +14,7 @@ pub use app::*;
pub use browser_process_handler::*; pub use browser_process_handler::*;
pub use browsers::*; pub use browsers::*;
pub use client_handler::*; pub use client_handler::*;
pub use command_line_config::*;
pub use context_menu_handler::*; pub use context_menu_handler::*;
pub use localhost::*; pub use localhost::*;
pub use message_pump::*; pub use message_pump::*;

View File

@@ -1,3 +1,4 @@
use crate::browser_process::CommandLineConfig;
use crate::browser_process::MessageLoopTimer; use crate::browser_process::MessageLoopTimer;
use crate::browser_process::browser_process_handler::BrowserProcessHandlerBuilder; use crate::browser_process::browser_process_handler::BrowserProcessHandlerBuilder;
use crate::util::{SCHEME_CEF, cef_scheme_flags}; use crate::util::{SCHEME_CEF, cef_scheme_flags};
@@ -15,13 +16,18 @@ use std::sync::mpsc::Sender;
pub struct BrowserProcessAppBuilder { pub struct BrowserProcessAppBuilder {
object: *mut RcImpl<_cef_app_t, Self>, object: *mut RcImpl<_cef_app_t, Self>,
message_loop_working_requester: Sender<MessageLoopTimer>, message_loop_working_requester: Sender<MessageLoopTimer>,
config: CommandLineConfig,
} }
impl BrowserProcessAppBuilder { impl BrowserProcessAppBuilder {
pub fn build(message_loop_working_requester: Sender<MessageLoopTimer>) -> cef::App { pub fn build(
message_loop_working_requester: Sender<MessageLoopTimer>,
config: CommandLineConfig,
) -> cef::App {
cef::App::new(Self { cef::App::new(Self {
object: core::ptr::null_mut(), object: core::ptr::null_mut(),
message_loop_working_requester, message_loop_working_requester,
config,
}) })
} }
} }
@@ -36,6 +42,7 @@ impl Clone for BrowserProcessAppBuilder {
Self { Self {
object, object,
message_loop_working_requester: self.message_loop_working_requester.clone(), message_loop_working_requester: self.message_loop_working_requester.clone(),
config: self.config.clone(),
} }
} }
} }
@@ -58,10 +65,14 @@ impl ImplApp for BrowserProcessAppBuilder {
let Some(command_line) = command_line else { let Some(command_line) = command_line else {
return; return;
}; };
command_line.append_switch(Some(&"use-mock-keychain".into()));
// command_line.append_switch(Some(&"disable-gpu".into())); for switch in &self.config.switches {
// command_line.append_switch(Some(&"disable-gpu-compositing".into())); command_line.append_switch(Some(&(*switch).into()));
// command_line.append_switch(Some(&" disable-gpu-shader-disk-cache".into())); }
for (name, value) in &self.config.switch_values {
command_line.append_switch_with_value(Some(&(*name).into()), Some(&(*value).into()));
}
} }
fn on_register_custom_schemes(&self, registrar: Option<&mut SchemeRegistrar>) { fn on_register_custom_schemes(&self, registrar: Option<&mut SchemeRegistrar>) {

View File

@@ -0,0 +1,56 @@
/// Configuration for CEF command line switches.
///
/// Used to customize CEF behavior at startup.
///
/// # Default Switches
///
/// On macOS debug builds, the following switches are enabled by default:
/// - `use-mock-keychain`: Uses a mock keychain for testing
///
/// # Example
///
/// ```no_run
/// use bevy_cef::prelude::*;
///
/// // Add switches while preserving defaults (recommended)
/// let config = CommandLineConfig::default()
/// .with_switch("disable-gpu")
/// .with_switch_value("remote-debugging-port", "9222");
///
/// // Or use direct initialization (replaces defaults)
/// let config = CommandLineConfig {
/// switches: vec!["disable-gpu"],
/// switch_values: vec![("remote-debugging-port", "9222")],
/// };
/// ```
#[derive(Clone, Debug)]
pub struct CommandLineConfig {
pub switches: Vec<&'static str>,
pub switch_values: Vec<(&'static str, &'static str)>,
}
impl Default for CommandLineConfig {
fn default() -> Self {
Self {
switches: vec![
#[cfg(all(target_os = "macos", debug_assertions))]
"use-mock-keychain",
],
switch_values: Vec::new(),
}
}
}
impl CommandLineConfig {
/// Add a command line switch (e.g., "disable-gpu", "disable-web-security").
pub fn with_switch(mut self, name: &'static str) -> Self {
self.switches.push(name);
self
}
/// Add a command line switch with a value (e.g., "remote-debugging-port", "9222").
pub fn with_switch_value(mut self, name: &'static str, value: &'static str) -> Self {
self.switch_values.push((name, value));
self
}
}

View File

@@ -13,7 +13,7 @@ fn main() {
.add_plugins(( .add_plugins((
DefaultPlugins, DefaultPlugins,
RemotePlugin::default().with_method("greet", greet), RemotePlugin::default().with_method("greet", greet),
CefPlugin, CefPlugin::default(),
)) ))
.add_systems( .add_systems(
Startup, Startup,

View File

@@ -12,7 +12,7 @@ fn main() {
App::new() App::new()
.add_plugins(( .add_plugins((
DefaultPlugins, DefaultPlugins,
CefPlugin, CefPlugin::default(),
WebviewExtendMaterialPlugin::<CustomExtension>::default(), WebviewExtendMaterialPlugin::<CustomExtension>::default(),
)) ))
.add_systems(Startup, (spawn_camera, spawn_webview)) .add_systems(Startup, (spawn_camera, spawn_webview))

View File

@@ -10,7 +10,7 @@ use bevy_cef::prelude::*;
fn main() { fn main() {
App::new() App::new()
.add_plugins((DefaultPlugins, CefPlugin)) .add_plugins((DefaultPlugins, CefPlugin::default()))
.add_systems( .add_systems(
Startup, Startup,
(spawn_camera, spawn_directional_light, spawn_webview), (spawn_camera, spawn_directional_light, spawn_webview),

View File

@@ -7,7 +7,7 @@ use std::time::Duration;
fn main() { fn main() {
App::new() App::new()
.add_plugins((DefaultPlugins, CefPlugin)) .add_plugins((DefaultPlugins, CefPlugin::default()))
.add_systems( .add_systems(
Startup, Startup,
(spawn_camera, spawn_directional_light, spawn_webview), (spawn_camera, spawn_directional_light, spawn_webview),

View File

@@ -8,7 +8,7 @@ fn main() {
App::new() App::new()
.add_plugins(( .add_plugins((
DefaultPlugins, DefaultPlugins,
CefPlugin, CefPlugin::default(),
JsEmitEventPlugin::<Message>::default(), JsEmitEventPlugin::<Message>::default(),
)) ))
.add_systems( .add_systems(

View File

@@ -11,7 +11,7 @@ use bevy_cef::prelude::*;
fn main() { fn main() {
App::new() App::new()
.add_plugins((DefaultPlugins, CefPlugin)) .add_plugins((DefaultPlugins, CefPlugin::default()))
.add_systems( .add_systems(
Startup, Startup,
(spawn_camera, spawn_directional_light, spawn_webview), (spawn_camera, spawn_directional_light, spawn_webview),

View File

@@ -7,7 +7,7 @@ use bevy_cef::prelude::*;
fn main() { fn main() {
App::new() App::new()
.add_plugins((DefaultPlugins, CefPlugin)) .add_plugins((DefaultPlugins, CefPlugin::default()))
.add_systems( .add_systems(
Startup, Startup,
(spawn_camera, spawn_directional_light, spawn_webview), (spawn_camera, spawn_directional_light, spawn_webview),

View File

@@ -5,7 +5,7 @@ use bevy_cef::prelude::*;
fn main() { fn main() {
App::new() App::new()
.add_plugins((DefaultPlugins, CefPlugin)) .add_plugins((DefaultPlugins, CefPlugin::default()))
.add_systems( .add_systems(
Startup, Startup,
(spawn_camera, spawn_directional_light, spawn_webview), (spawn_camera, spawn_directional_light, spawn_webview),

View File

@@ -5,7 +5,7 @@ use bevy_cef::prelude::*;
fn main() { fn main() {
App::new() App::new()
.add_plugins((DefaultPlugins, CefPlugin)) .add_plugins((DefaultPlugins, CefPlugin::default()))
.add_systems(Startup, (spawn_camera_2d, spawn_sprite_webview)) .add_systems(Startup, (spawn_camera_2d, spawn_sprite_webview))
.run(); .run();
} }

View File

@@ -6,7 +6,7 @@ use bevy_cef::prelude::*;
fn main() { fn main() {
App::new() App::new()
.add_plugins((DefaultPlugins, CefPlugin)) .add_plugins((DefaultPlugins, CefPlugin::default()))
.add_systems( .add_systems(
Startup, Startup,
(spawn_camera, spawn_directional_light, spawn_webview), (spawn_camera, spawn_directional_light, spawn_webview),

View File

@@ -8,7 +8,9 @@ use cef::{Settings, api_hash, execute_process, initialize, shutdown, sys};
/// ///
/// - Windows and Linux: Support [`multi_threaded_message_loop`](https://cef-builds.spotifycdn.com/docs/106.1/structcef__settings__t.html#a518ac90db93ca5133a888faa876c08e0), so it is used. /// - Windows and Linux: Support [`multi_threaded_message_loop`](https://cef-builds.spotifycdn.com/docs/106.1/structcef__settings__t.html#a518ac90db93ca5133a888faa876c08e0), so it is used.
/// - macOS: Calls [`CefDoMessageLoopWork`](https://cef-builds.spotifycdn.com/docs/106.1/cef__app_8h.html#a830ae43dcdffcf4e719540204cefdb61) every frame. /// - macOS: Calls [`CefDoMessageLoopWork`](https://cef-builds.spotifycdn.com/docs/106.1/cef__app_8h.html#a830ae43dcdffcf4e719540204cefdb61) every frame.
pub struct MessageLoopPlugin; pub struct MessageLoopPlugin {
pub config: CommandLineConfig,
}
impl Plugin for MessageLoopPlugin { impl Plugin for MessageLoopPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
@@ -19,7 +21,7 @@ impl Plugin for MessageLoopPlugin {
let args = Args::new(); let args = Args::new();
let (tx, rx) = std::sync::mpsc::channel(); let (tx, rx) = std::sync::mpsc::channel();
let mut cef_app = BrowserProcessAppBuilder::build(tx); let mut cef_app = BrowserProcessAppBuilder::build(tx, self.config.clone());
let ret = execute_process( let ret = execute_process(
Some(args.as_main_args()), Some(args.as_main_args()),
Some(&mut cef_app), Some(&mut cef_app),

View File

@@ -16,21 +16,28 @@ use crate::mute::AudioMutePlugin;
use crate::prelude::{IpcPlugin, NavigationPlugin, WebviewPlugin}; use crate::prelude::{IpcPlugin, NavigationPlugin, WebviewPlugin};
use crate::zoom::ZoomPlugin; use crate::zoom::ZoomPlugin;
use bevy::prelude::*; use bevy::prelude::*;
use bevy_cef_core::prelude::CommandLineConfig;
use bevy_remote::RemotePlugin; use bevy_remote::RemotePlugin;
pub mod prelude { pub mod prelude {
pub use crate::{CefPlugin, RunOnMainThread, common::*, navigation::*, webview::prelude::*}; pub use crate::{CefPlugin, RunOnMainThread, common::*, navigation::*, webview::prelude::*};
pub use bevy_cef_core::prelude::CommandLineConfig;
} }
pub struct RunOnMainThread; pub struct RunOnMainThread;
pub struct CefPlugin; #[derive(Debug, Default)]
pub struct CefPlugin {
pub command_line_config: CommandLineConfig,
}
impl Plugin for CefPlugin { impl Plugin for CefPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_plugins(( app.add_plugins((
LocalHostPlugin, LocalHostPlugin,
MessageLoopPlugin, MessageLoopPlugin {
config: self.command_line_config.clone(),
},
WebviewCoreComponentsPlugin, WebviewCoreComponentsPlugin,
WebviewPlugin, WebviewPlugin,
IpcPlugin, IpcPlugin,