#![allow(clippy::type_complexity)]
mod common;
mod cursor_icon;
mod drag;
mod focus;
mod keyboard;
mod mute;
mod navigation;
mod profile;
mod resize;
mod system_param;
mod title;
mod webview;
mod zoom;
use crate::common::{
CefRootCachePath, CefStatus, LocalHostPlugin, MessageLoopPlugin, SandboxMode,
WebviewCoreComponentsPlugin, WebviewDpiPlugin, add_failed_startup, resolve_no_sandbox,
};
use crate::cursor_icon::SystemCursorIconPlugin;
use crate::drag::DragPlugin;
use crate::focus::FocusPlugin;
use crate::keyboard::KeyboardPlugin;
use crate::mute::AudioMutePlugin;
use crate::prelude::{IpcPlugin, NavigationPlugin, WebviewPlugin};
use crate::profile::ProfilePlugin;
use crate::resize::plugin::ResizePlugin;
use crate::title::TitlePlugin;
use crate::zoom::ZoomPlugin;
use bevy::prelude::*;
use bevy_cef_core::prelude::{
CefCustomScheme, CefExtensions, CommandLineConfig, effective_command_line_config, switches,
};
use bevy_remote::RemotePlugin;
use std::time::Duration;
pub mod prelude {
pub use crate::common::{CefStartupError, CefStatus};
pub use crate::focus::FocusedWebview;
pub use crate::keyboard::{CefKeyboardFilter, KeyboardDeliverSet, ModifiersState};
pub use crate::profile::{
ProfileName, ProfileNameError, WebviewProfile, WebviewProfileError,
WebviewProfileErrorReason,
};
pub use crate::resize::components::{AspectLockMode, WebviewResizable};
pub use crate::{CefPlugin, RunOnMainThread, navigation::*, title::*, webview::prelude::*};
pub use bevy_cef_core::prelude::{
CefCustomScheme, CefExtensions, CefSchemeBody, CefSchemeHandler, CefSchemeOptions,
CefSchemeRequest, CefSchemeResponse, CommandLineConfig, switches,
};
}
pub struct RunOnMainThread;
pub struct CefPlugin {
pub command_line_config: CommandLineConfig,
pub extensions: CefExtensions,
pub root_cache_path: Option<String>,
pub custom_schemes: Vec<CefCustomScheme>,
pub sandbox: SandboxMode,
pub cookie_flush_interval: Duration,
}
impl Default for CefPlugin {
fn default() -> Self {
Self {
command_line_config: Default::default(),
extensions: Default::default(),
root_cache_path: Default::default(),
custom_schemes: Default::default(),
sandbox: Default::default(),
cookie_flush_interval: Duration::from_secs(5),
}
}
}
impl Plugin for CefPlugin {
fn build(&self, app: &mut App) {
bevy_cef_core::prelude::init_registered_schemes(self.custom_schemes.clone());
let no_sandbox = resolve_no_sandbox(self.sandbox);
let strip_no_zygote = cfg!(target_os = "linux") && !no_sandbox;
let effective_config =
effective_command_line_config(&self.command_line_config, strip_no_zygote);
let risky = switches::risky_present(&effective_config.switches);
if !risky.is_empty() {
warn!(
"bevy_cef: web-security relaxations active: {risky:?}. \
Only enable these when loading fully trusted content."
);
}
#[cfg(target_os = "macos")]
if self.sandbox == SandboxMode::Enabled {
warn!(
"bevy_cef: SandboxMode::Enabled requested on macOS, but the render \
process is not linked against cef_sandbox / does not call \
cef_sandbox_initialize(); the sandbox will not function and may abort. \
See the plugin-configuration docs."
);
}
#[cfg(target_os = "linux")]
if self.sandbox == SandboxMode::Enabled {
warn!(
"bevy_cef: SandboxMode::Enabled requested on Linux; the sandbox requires a \
correctly-installed SUID-root chrome-sandbox helper alongside the render \
process. Without it, Chromium aborts the renderer with no bevy_cef-side \
diagnostic. See the plugin-configuration docs."
);
}
let message_loop = MessageLoopPlugin {
config: effective_config,
extensions: self.extensions.clone(),
root_cache_path: self.root_cache_path.clone(),
no_sandbox,
};
if let Err(e) = message_loop.start(app) {
error!("bevy_cef: CEF failed to start: {e}");
add_failed_startup(app, e, self.root_cache_path.clone());
return;
}
app.insert_resource(CefStatus::Running)
.insert_resource(CefRootCachePath(self.root_cache_path.clone()))
.add_plugins((
LocalHostPlugin,
message_loop,
WebviewCoreComponentsPlugin,
WebviewDpiPlugin,
WebviewPlugin,
IpcPlugin,
KeyboardPlugin,
FocusPlugin,
SystemCursorIconPlugin,
DragPlugin,
ResizePlugin,
NavigationPlugin,
TitlePlugin,
ZoomPlugin,
AudioMutePlugin,
))
.add_plugins(ProfilePlugin {
cookie_flush_interval: self.cookie_flush_interval,
});
if !app.is_plugin_added::<RemotePlugin>() {
app.add_plugins(RemotePlugin::default());
}
}
}