use std::{ffi::CStr, os::raw::c_int, sync::OnceLock};
use cef::{
resources,
sys::{cef_id_for_command_id_name, cef_menu_item_type_t},
*,
};
const BROWSER_ONLY_COMMANDS: &[&CStr] = &[
resources::IDC_BACK,
resources::IDC_FORWARD,
resources::IDC_RELOAD,
resources::IDC_RELOAD_BYPASSING_CACHE,
resources::IDC_RELOAD_CLEARING_CACHE,
resources::IDC_CONTENT_CONTEXT_RELOADFRAME,
resources::IDC_SAVE_PAGE,
resources::IDC_PRINT,
resources::IDC_BASIC_PRINT,
resources::IDC_VIEW_SOURCE,
resources::IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE,
resources::IDC_CONTENT_CONTEXT_VIEWPAGEINFO,
resources::IDC_CONTENT_CONTEXT_VIEWFRAMEINFO,
resources::IDC_CONTENT_CONTEXT_SAVELINKAS,
resources::IDC_CONTENT_CONTEXT_SAVEIMAGEAS,
resources::IDC_CONTENT_CONTEXT_SAVEAVAS,
resources::IDC_CONTENT_CONTEXT_SAVEPLUGINAS,
resources::IDC_CONTENT_CONTEXT_SAVEVIDEOFRAMEAS,
resources::IDC_CONTENT_CONTEXT_OPENLINKNEWTAB,
resources::IDC_CONTENT_CONTEXT_OPENLINKNEWWINDOW,
resources::IDC_CONTENT_CONTEXT_OPENLINKOFFTHERECORD,
resources::IDC_CONTENT_CONTEXT_OPENLINKINPROFILE,
resources::IDC_CONTENT_CONTEXT_OPENLINKWITH,
resources::IDC_CONTENT_CONTEXT_OPENLINKBOOKMARKAPP,
resources::IDC_CONTENT_CONTEXT_OPENLINKSPLITVIEW,
resources::IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB,
resources::IDC_CONTENT_CONTEXT_OPEN_ORIGINAL_IMAGE_NEW_TAB,
resources::IDC_CONTENT_CONTEXT_OPENAVNEWTAB,
resources::IDC_CONTENT_CONTEXT_GOTOURL,
resources::IDC_CONTENT_CONTEXT_OPEN_IN_READING_MODE,
resources::IDC_CONTENT_CONTEXT_ADD_LINK_TO_READING_LIST,
resources::IDC_CONTENT_CONTEXT_TRANSLATE,
resources::IDC_CONTENT_CONTEXT_PARTIAL_TRANSLATE,
resources::IDC_CONTENT_CONTEXT_SEARCHWEBFOR,
resources::IDC_CONTENT_CONTEXT_SEARCHWEBFORNEWTAB,
resources::IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE,
resources::IDC_CONTENT_CONTEXT_SEARCHWEBFORVIDEOFRAME,
resources::IDC_CONTENT_CONTEXT_SEARCHLENSFORIMAGE,
resources::IDC_CONTENT_CONTEXT_SEARCHLENSFORVIDEOFRAME,
resources::IDC_CONTENT_CONTEXT_LENS_OVERLAY,
resources::IDC_CONTENT_CONTEXT_LENS_REGION_SEARCH,
resources::IDC_CONTENT_CONTEXT_WEB_REGION_SEARCH,
resources::IDC_CONTENT_CONTEXT_INSPECTELEMENT_WITH_GEMINI,
resources::IDC_CONTENT_CONTEXT_SHARING_SUBMENU,
resources::IDC_CONTENT_CONTEXT_GENERATE_QR_CODE,
resources::IDC_ROUTE_MEDIA,
];
const DEVTOOLS_COMMANDS: &[&CStr] = &[
resources::IDC_CONTENT_CONTEXT_INSPECTELEMENT,
resources::IDC_CONTENT_CONTEXT_INSPECTELEMENT_WITH_DEVTOOLS,
resources::IDC_CONTENT_CONTEXT_INSPECTBACKGROUNDPAGE,
resources::IDC_DEV_TOOLS,
resources::IDC_DEV_TOOLS_INSPECT,
resources::IDC_DEV_TOOLS_CONSOLE,
resources::IDC_DEV_TOOLS_DEVICES,
resources::IDC_DEV_TOOLS_TOGGLE,
];
const UNKNOWN_COMMAND_ID: c_int = -1;
struct CommandIds {
browser_only: Vec<c_int>,
devtools: Vec<c_int>,
}
fn command_ids() -> &'static CommandIds {
static COMMAND_IDS: OnceLock<CommandIds> = OnceLock::new();
COMMAND_IDS.get_or_init(|| CommandIds {
browser_only: resolve_command_ids(BROWSER_ONLY_COMMANDS),
devtools: resolve_command_ids(DEVTOOLS_COMMANDS),
})
}
fn resolve_command_ids(names: &[&CStr]) -> Vec<c_int> {
names
.iter()
.map(|name| unsafe { cef_id_for_command_id_name(name.as_ptr()) })
.filter(|id| *id != UNKNOWN_COMMAND_ID)
.collect()
}
fn remove_redundant_separators(model: &MenuModel) {
let separator = MenuItemType::from(cef_menu_item_type_t::MENUITEMTYPE_SEPARATOR);
let is_separator = |index: usize| model.type_at(index) == separator;
let removed = |result: c_int| result != 0;
let mut previous_was_separator = true;
let mut index = 0;
while index < model.count() {
if is_separator(index) {
if previous_was_separator && removed(model.remove_at(index)) {
continue;
}
previous_was_separator = true;
} else {
previous_was_separator = false;
}
index += 1;
}
while model.count() > 0
&& is_separator(model.count() - 1)
&& removed(model.remove_at(model.count() - 1))
{}
}
wrap_context_menu_handler! {
pub struct TauriCefContextMenuHandler {
devtools_enabled: bool,
}
impl ContextMenuHandler {
fn on_before_context_menu(
&self,
_browser: Option<&mut Browser>,
_frame: Option<&mut Frame>,
_params: Option<&mut ContextMenuParams>,
model: Option<&mut MenuModel>,
) {
let Some(model) = model else {
return;
};
let command_ids = command_ids();
for id in &command_ids.browser_only {
model.remove(*id);
}
if !self.devtools_enabled {
for id in &command_ids.devtools {
model.remove(*id);
}
}
remove_redundant_separators(model);
}
}
}