use std::sync::mpsc::Sender;
use cef::rc::Rc as _;
use cef::{
App, BrowserProcessHandler, CefString, CommandLine, ImplApp, ImplBrowserProcessHandler,
ImplCommandLine, WrapApp, WrapBrowserProcessHandler,
};
use crate::runtime::PumpDeadline;
#[cfg(target_os = "macos")]
const MACOS_SYSTEM_NOTIFICATION_FEATURES: [&str; 2] =
["NativeNotifications", "SystemNotifications"];
#[cfg(target_os = "macos")]
fn merge_disabled_features(existing: &str, required: &[&str]) -> String {
let mut features = existing
.split(',')
.filter(|feature| !feature.is_empty())
.collect::<Vec<_>>();
for feature in required {
if !features.contains(feature) {
features.push(feature);
}
}
features.join(",")
}
#[cfg(target_os = "macos")]
fn disable_features(command_line: &CommandLine, required: &[&str]) {
let switch_name: CefString = "disable-features".into();
let existing = command_line.switch_value(Some(&switch_name));
let existing = CefString::from(&existing).to_string();
let features: CefString = merge_disabled_features(&existing, required).as_str().into();
command_line.append_switch_with_value(Some(&switch_name), Some(&features));
}
#[allow(
clippy::transmute_ptr_to_ptr,
reason = "CEF wrapper macros generate ABI pointer casts outside WaterUI's control"
)]
fn new_browser_process_handler(schedule: Sender<PumpDeadline>) -> BrowserProcessHandler {
cef::wrap_browser_process_handler! {
struct WaterBrowserProcessHandler {
schedule: Sender<PumpDeadline>,
}
impl BrowserProcessHandler {
fn on_schedule_message_pump_work(&self, delay_ms: i64) {
let _ = self.schedule.send(PumpDeadline::after_millis(delay_ms));
}
}
}
WaterBrowserProcessHandler::new(schedule)
}
#[allow(
clippy::transmute_ptr_to_ptr,
reason = "CEF wrapper macros generate ABI pointer casts outside WaterUI's control"
)]
fn new_cef_app(handler: BrowserProcessHandler) -> App {
cef::wrap_app! {
struct WaterCefApp {
handler: BrowserProcessHandler,
}
impl App {
fn on_before_command_line_processing(
&self,
_process_type: Option<&CefString>,
command_line: Option<&mut CommandLine>,
) {
let command_line = command_line
.expect("CEF must provide a command line before process launch");
for switch in [
"disable-background-networking",
"disable-component-update",
"disable-default-apps",
"disable-domain-reliability",
"disable-notifications",
"disable-sync",
"enable-gpu",
"enable-gpu-compositing",
"no-default-browser-check",
"no-first-run",
"no-startup-window",
] {
command_line.append_switch(Some(&switch.into()));
}
#[cfg(target_os = "macos")]
disable_features(command_line, &MACOS_SYSTEM_NOTIFICATION_FEATURES);
#[cfg(all(target_os = "macos", debug_assertions))]
command_line.append_switch(Some(&"use-mock-keychain".into()));
}
fn browser_process_handler(&self) -> Option<BrowserProcessHandler> {
Some(self.handler.clone())
}
}
}
WaterCefApp::new(handler)
}
pub fn new_app(schedule: Sender<PumpDeadline>) -> App {
new_cef_app(new_browser_process_handler(schedule))
}
#[cfg(all(test, target_os = "macos"))]
mod tests {
use super::{MACOS_SYSTEM_NOTIFICATION_FEATURES, merge_disabled_features};
#[test]
fn disables_macos_system_notifications_without_erasing_existing_features() {
assert_eq!(
merge_disabled_features(
"ExistingFeature,NativeNotifications",
&MACOS_SYSTEM_NOTIFICATION_FEATURES,
),
"ExistingFeature,NativeNotifications,SystemNotifications"
);
}
}