mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-26 05:38:12 +08:00
Desktop: Isolate CEF-rendered UI into separate crate and process (#4321)
* Extract CEF rendered UI into a separate process and crate * Review * Review * Review * Review * Remove necessary workarounds * Block on frame copy ack * Crop and resample frames correctly * Skip blank frames * Fix deps * Fix fmt * Fix clippy warning * Review * Fix todo
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
use cef::rc::{Rc, RcImpl};
|
||||
use cef::sys::{_cef_app_t, cef_base_ref_counted_t};
|
||||
use cef::{BrowserProcessHandler, CefString, ImplApp, ImplCommandLine, SchemeRegistrar, WrapApp};
|
||||
|
||||
use super::browser_process_handler::BrowserProcessHandlerImpl;
|
||||
use super::scheme_handler_factory::register_schemes;
|
||||
|
||||
pub(crate) struct BrowserProcessAppImpl {
|
||||
object: *mut RcImpl<_cef_app_t, Self>,
|
||||
accelerated_paint: bool,
|
||||
}
|
||||
impl BrowserProcessAppImpl {
|
||||
pub(crate) fn new(accelerated_paint: bool) -> Self {
|
||||
Self {
|
||||
object: std::ptr::null_mut(),
|
||||
accelerated_paint,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ImplApp for BrowserProcessAppImpl {
|
||||
fn browser_process_handler(&self) -> Option<BrowserProcessHandler> {
|
||||
Some(BrowserProcessHandler::new(BrowserProcessHandlerImpl::new()))
|
||||
}
|
||||
|
||||
fn on_register_custom_schemes(&self, registrar: Option<&mut SchemeRegistrar>) {
|
||||
register_schemes(registrar);
|
||||
}
|
||||
|
||||
fn on_before_command_line_processing(&self, _process_type: Option<&cef::CefString>, command_line: Option<&mut cef::CommandLine>) {
|
||||
if let Some(cmd) = command_line {
|
||||
cmd.append_switch_with_value(Some(&"renderer-process-limit".into()), Some(&"1".into()));
|
||||
cmd.append_switch_with_value(Some(&"password-store".into()), Some(&"basic".into()));
|
||||
cmd.append_switch_with_value(Some(&"disk-cache-size".into()), Some(&"0".into()));
|
||||
cmd.append_switch(Some(&"no-sandbox".into()));
|
||||
cmd.append_switch(Some(&"no-first-run".into()));
|
||||
cmd.append_switch(Some(&"noerrdialogs".into()));
|
||||
cmd.append_switch(Some(&"no-default-browser-check".into()));
|
||||
cmd.append_switch(Some(&"mute-audio".into()));
|
||||
cmd.append_switch(Some(&"use-fake-device-for-media-stream".into()));
|
||||
cmd.append_switch(Some(&"incognito".into()));
|
||||
cmd.append_switch(Some(&"disable-sync".into()));
|
||||
cmd.append_switch(Some(&"disable-file-system".into()));
|
||||
cmd.append_switch(Some(&"disable-component-update".into()));
|
||||
cmd.append_switch(Some(&"disable-geolocation".into()));
|
||||
cmd.append_switch(Some(&"disable-notifications".into()));
|
||||
cmd.append_switch(Some(&"disable-background-networking".into()));
|
||||
cmd.append_switch(Some(&"disable-default-apps".into()));
|
||||
cmd.append_switch(Some(&"disable-breakpad".into()));
|
||||
cmd.append_switch_with_value(Some(&"disable-blink-features".into()), Some(&"WebBluetooth,WebUSB,Serial".into()));
|
||||
|
||||
let extra_disabled_features = ["OptimizationHints", "OnDeviceModelService", "TranslateUI"];
|
||||
let disabled_features_switch = Some(&"disable-features".into());
|
||||
let mut disabled_features: Vec<String> = CefString::from(&cmd.switch_value(disabled_features_switch))
|
||||
.to_string()
|
||||
.split(',')
|
||||
.filter(|feature| !feature.is_empty())
|
||||
.map(ToOwned::to_owned)
|
||||
.collect();
|
||||
disabled_features.extend(extra_disabled_features.into_iter().map(ToOwned::to_owned));
|
||||
cmd.append_switch_with_value(disabled_features_switch, Some(&disabled_features.join(",").as_str().into()));
|
||||
|
||||
if self.accelerated_paint {
|
||||
cmd.append_switch(Some(&"enable-gpu".into()));
|
||||
cmd.append_switch(Some(&"enable-gpu-compositing".into()));
|
||||
cmd.append_switch(Some(&"enable-begin-frame-scheduling".into()));
|
||||
cmd.append_switch(Some(&"off-screen-rendering-enabled".into()));
|
||||
cmd.append_switch(Some(&"enable-accelerated-2d-canvas".into()));
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
cmd.append_switch_with_value(Some(&"use-angle".into()), Some(&"gl-egl".into()));
|
||||
|
||||
let use_wayland = std::env::var("WAYLAND_DISPLAY")
|
||||
.ok()
|
||||
.filter(|var| !var.is_empty())
|
||||
.or_else(|| std::env::var("WAYLAND_SOCKET").ok())
|
||||
.filter(|var| !var.is_empty())
|
||||
.is_some();
|
||||
if use_wayland {
|
||||
cmd.append_switch_with_value(Some(&"ozone-platform".into()), Some(&"wayland".into()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cmd.append_switch(Some(&"disable-gpu".into()));
|
||||
cmd.append_switch(Some(&"disable-gpu-compositing".into()));
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
// Hide user prompt asking for keychain access
|
||||
cmd.append_switch(Some(&"use-mock-keychain".into()));
|
||||
}
|
||||
|
||||
// Enable browser debugging via environment variable
|
||||
if let Some(env) = std::env::var("GRAPHITE_BROWSER_DEBUG_PORT").ok()
|
||||
&& let Some(port) = env.parse::<u16>().ok()
|
||||
{
|
||||
cmd.append_switch_with_value(Some(&"remote-debugging-port".into()), Some(&port.to_string().as_str().into()));
|
||||
cmd.append_switch_with_value(Some(&"remote-allow-origins".into()), Some(&"*".into()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_raw(&self) -> *mut _cef_app_t {
|
||||
self.object.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for BrowserProcessAppImpl {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
let rc_impl = &mut *self.object;
|
||||
rc_impl.interface.add_ref();
|
||||
}
|
||||
Self {
|
||||
object: self.object,
|
||||
accelerated_paint: self.accelerated_paint,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Rc for BrowserProcessAppImpl {
|
||||
fn as_base(&self) -> &cef_base_ref_counted_t {
|
||||
unsafe {
|
||||
let base = &*self.object;
|
||||
std::mem::transmute(&base.cef_object)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl WrapApp for BrowserProcessAppImpl {
|
||||
fn wrap_rc(&mut self, object: *mut RcImpl<_cef_app_t, Self>) {
|
||||
self.object = object;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
use cef::rc::{Rc, RcImpl};
|
||||
use cef::sys::{_cef_client_t, cef_base_ref_counted_t};
|
||||
use cef::{ContextMenuHandler, DisplayHandler, ImplClient, LifeSpanHandler, LoadHandler, RenderHandler, RequestHandler, WrapClient};
|
||||
|
||||
use crate::delegate::BrowserDelegate;
|
||||
use crate::frames::FrameStreamer;
|
||||
use crate::ipc::{MessageType, UnpackMessage, UnpackedMessage};
|
||||
|
||||
use super::context_menu_handler::ContextMenuHandlerImpl;
|
||||
use super::display_handler::DisplayHandlerImpl;
|
||||
use super::life_span_handler::LifeSpanHandlerImpl;
|
||||
use super::load_handler::LoadHandlerImpl;
|
||||
use super::render_handler::RenderHandlerImpl;
|
||||
use super::request_handler::RequestHandlerImpl;
|
||||
|
||||
pub(crate) struct BrowserProcessClientImpl {
|
||||
object: *mut RcImpl<_cef_client_t, Self>,
|
||||
delegate: BrowserDelegate,
|
||||
load_handler: LoadHandler,
|
||||
render_handler: RenderHandler,
|
||||
display_handler: DisplayHandler,
|
||||
request_handler: RequestHandler,
|
||||
}
|
||||
impl BrowserProcessClientImpl {
|
||||
pub(crate) fn new(delegate: &BrowserDelegate, frames: FrameStreamer) -> Self {
|
||||
Self {
|
||||
object: std::ptr::null_mut(),
|
||||
delegate: delegate.clone(),
|
||||
load_handler: LoadHandler::new(LoadHandlerImpl::new(delegate.clone())),
|
||||
render_handler: RenderHandler::new(RenderHandlerImpl::new(delegate.clone(), frames)),
|
||||
display_handler: DisplayHandler::new(DisplayHandlerImpl::new(delegate.clone())),
|
||||
request_handler: RequestHandler::new(RequestHandlerImpl::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ImplClient for BrowserProcessClientImpl {
|
||||
fn on_process_message_received(
|
||||
&self,
|
||||
_browser: Option<&mut cef::Browser>,
|
||||
_frame: Option<&mut cef::Frame>,
|
||||
_source_process: cef::ProcessId,
|
||||
message: Option<&mut cef::ProcessMessage>,
|
||||
) -> std::ffi::c_int {
|
||||
let unpacked_message = unsafe { message.and_then(|m| m.unpack()) };
|
||||
match unpacked_message {
|
||||
Some(UnpackedMessage {
|
||||
message_type: MessageType::Initialized,
|
||||
data: _,
|
||||
}) => self.delegate.initialized_web_communication(),
|
||||
Some(UnpackedMessage {
|
||||
message_type: MessageType::SendToNative,
|
||||
data,
|
||||
}) => self.delegate.receive_web_message(data),
|
||||
|
||||
_ => {
|
||||
tracing::error!("Unexpected message type received in browser process");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
1
|
||||
}
|
||||
|
||||
fn load_handler(&self) -> Option<cef::LoadHandler> {
|
||||
Some(self.load_handler.clone())
|
||||
}
|
||||
|
||||
fn render_handler(&self) -> Option<RenderHandler> {
|
||||
Some(self.render_handler.clone())
|
||||
}
|
||||
|
||||
fn life_span_handler(&self) -> Option<cef::LifeSpanHandler> {
|
||||
Some(LifeSpanHandler::new(LifeSpanHandlerImpl::new()))
|
||||
}
|
||||
|
||||
fn display_handler(&self) -> Option<cef::DisplayHandler> {
|
||||
Some(self.display_handler.clone())
|
||||
}
|
||||
|
||||
fn request_handler(&self) -> Option<cef::RequestHandler> {
|
||||
Some(self.request_handler.clone())
|
||||
}
|
||||
|
||||
fn context_menu_handler(&self) -> Option<cef::ContextMenuHandler> {
|
||||
Some(ContextMenuHandler::new(ContextMenuHandlerImpl::new()))
|
||||
}
|
||||
|
||||
fn get_raw(&self) -> *mut _cef_client_t {
|
||||
self.object.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for BrowserProcessClientImpl {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
let rc_impl = &mut *self.object;
|
||||
rc_impl.interface.add_ref();
|
||||
}
|
||||
Self {
|
||||
object: self.object,
|
||||
delegate: self.delegate.clone(),
|
||||
load_handler: self.load_handler.clone(),
|
||||
render_handler: self.render_handler.clone(),
|
||||
display_handler: self.display_handler.clone(),
|
||||
request_handler: self.request_handler.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Rc for BrowserProcessClientImpl {
|
||||
fn as_base(&self) -> &cef_base_ref_counted_t {
|
||||
unsafe {
|
||||
let base = &*self.object;
|
||||
std::mem::transmute(&base.cef_object)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl WrapClient for BrowserProcessClientImpl {
|
||||
fn wrap_rc(&mut self, object: *mut RcImpl<_cef_client_t, Self>) {
|
||||
self.object = object;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
use cef::rc::{Rc, RcImpl};
|
||||
use cef::sys::{_cef_browser_process_handler_t, cef_base_ref_counted_t, cef_browser_process_handler_t};
|
||||
use cef::{CefString, ImplBrowserProcessHandler, WrapBrowserProcessHandler};
|
||||
|
||||
pub(crate) struct BrowserProcessHandlerImpl {
|
||||
object: *mut RcImpl<cef_browser_process_handler_t, Self>,
|
||||
}
|
||||
impl BrowserProcessHandlerImpl {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self { object: std::ptr::null_mut() }
|
||||
}
|
||||
}
|
||||
|
||||
impl ImplBrowserProcessHandler for BrowserProcessHandlerImpl {
|
||||
fn on_already_running_app_relaunch(&self, _command_line: Option<&mut cef::CommandLine>, _current_directory: Option<&CefString>) -> std::ffi::c_int {
|
||||
1 // Return 1 to prevent default behavior of opening a empty browser window
|
||||
}
|
||||
|
||||
fn get_raw(&self) -> *mut _cef_browser_process_handler_t {
|
||||
self.object.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for BrowserProcessHandlerImpl {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
let rc_impl = &mut *self.object;
|
||||
rc_impl.interface.add_ref();
|
||||
}
|
||||
Self { object: self.object }
|
||||
}
|
||||
}
|
||||
impl Rc for BrowserProcessHandlerImpl {
|
||||
fn as_base(&self) -> &cef_base_ref_counted_t {
|
||||
unsafe {
|
||||
let base = &*self.object;
|
||||
std::mem::transmute(&base.cef_object)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl WrapBrowserProcessHandler for BrowserProcessHandlerImpl {
|
||||
fn wrap_rc(&mut self, object: *mut RcImpl<_cef_browser_process_handler_t, Self>) {
|
||||
self.object = object;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
use cef::rc::{Rc, RcImpl};
|
||||
use cef::sys::{_cef_context_menu_handler_t, cef_base_ref_counted_t};
|
||||
use cef::{ImplContextMenuHandler, WrapContextMenuHandler};
|
||||
|
||||
pub(crate) struct ContextMenuHandlerImpl {
|
||||
object: *mut RcImpl<_cef_context_menu_handler_t, Self>,
|
||||
}
|
||||
impl ContextMenuHandlerImpl {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self { object: std::ptr::null_mut() }
|
||||
}
|
||||
}
|
||||
|
||||
impl ImplContextMenuHandler for ContextMenuHandlerImpl {
|
||||
fn run_context_menu(
|
||||
&self,
|
||||
_browser: Option<&mut cef::Browser>,
|
||||
_frame: Option<&mut cef::Frame>,
|
||||
_params: Option<&mut cef::ContextMenuParams>,
|
||||
_model: Option<&mut cef::MenuModel>,
|
||||
_callback: Option<&mut cef::RunContextMenuCallback>,
|
||||
) -> std::ffi::c_int {
|
||||
// Prevent context menu
|
||||
1
|
||||
}
|
||||
|
||||
fn run_quick_menu(
|
||||
&self,
|
||||
_browser: Option<&mut cef::Browser>,
|
||||
_frame: Option<&mut cef::Frame>,
|
||||
_location: Option<&cef::Point>,
|
||||
_size: Option<&cef::Size>,
|
||||
_edit_state_flags: cef::QuickMenuEditStateFlags,
|
||||
_callback: Option<&mut cef::RunQuickMenuCallback>,
|
||||
) -> std::ffi::c_int {
|
||||
// Prevent quick menu
|
||||
1
|
||||
}
|
||||
|
||||
fn get_raw(&self) -> *mut _cef_context_menu_handler_t {
|
||||
self.object.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for ContextMenuHandlerImpl {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
let rc_impl = &mut *self.object;
|
||||
rc_impl.interface.add_ref();
|
||||
}
|
||||
Self { object: self.object }
|
||||
}
|
||||
}
|
||||
impl Rc for ContextMenuHandlerImpl {
|
||||
fn as_base(&self) -> &cef_base_ref_counted_t {
|
||||
unsafe {
|
||||
let base = &*self.object;
|
||||
std::mem::transmute(&base.cef_object)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl WrapContextMenuHandler for ContextMenuHandlerImpl {
|
||||
fn wrap_rc(&mut self, object: *mut RcImpl<_cef_context_menu_handler_t, Self>) {
|
||||
self.object = object;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
use cef::rc::{Rc, RcImpl};
|
||||
use cef::sys::{_cef_display_handler_t, cef_base_ref_counted_t, cef_cursor_type_t::*, cef_log_severity_t::*};
|
||||
use cef::{CefString, ImplDisplayHandler, Point, Size, WrapDisplayHandler};
|
||||
use winit::cursor::CursorIcon;
|
||||
|
||||
use crate::delegate::BrowserDelegate;
|
||||
|
||||
pub(crate) struct DisplayHandlerImpl {
|
||||
object: *mut RcImpl<_cef_display_handler_t, Self>,
|
||||
delegate: BrowserDelegate,
|
||||
}
|
||||
|
||||
impl DisplayHandlerImpl {
|
||||
pub fn new(delegate: BrowserDelegate) -> Self {
|
||||
Self {
|
||||
object: std::ptr::null_mut(),
|
||||
delegate,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
type CefCursorHandle = cef::CursorHandle;
|
||||
#[cfg(target_os = "macos")]
|
||||
type CefCursorHandle = *mut u8;
|
||||
|
||||
impl ImplDisplayHandler for DisplayHandlerImpl {
|
||||
fn on_cursor_change(&self, _browser: Option<&mut cef::Browser>, _cursor: CefCursorHandle, cursor_type: cef::CursorType, custom_cursor_info: Option<&cef::CursorInfo>) -> std::ffi::c_int {
|
||||
if let Some(custom_cursor_info) = custom_cursor_info {
|
||||
let Size { width, height } = custom_cursor_info.size;
|
||||
let Point { x: hotspot_x, y: hotspot_y } = custom_cursor_info.hotspot;
|
||||
let buffer_size = (width * height * 4) as usize;
|
||||
let buffer_ptr = custom_cursor_info.buffer as *const u8;
|
||||
|
||||
if !buffer_ptr.is_null() && buffer_ptr.align_offset(std::mem::align_of::<u8>()) == 0 {
|
||||
let buffer = unsafe { std::slice::from_raw_parts(buffer_ptr, buffer_size) }.to_vec();
|
||||
self.delegate.cursor_change(crate::Cursor::Custom {
|
||||
rgba: buffer,
|
||||
width: width as u16,
|
||||
height: height as u16,
|
||||
hotspot_x: hotspot_x as u16,
|
||||
hotspot_y: hotspot_y as u16,
|
||||
});
|
||||
return 1; // We handled the cursor change.
|
||||
}
|
||||
}
|
||||
|
||||
let cursor = match cursor_type.into() {
|
||||
CT_POINTER => CursorIcon::Default,
|
||||
CT_CROSS => CursorIcon::Crosshair,
|
||||
CT_HAND => CursorIcon::Pointer,
|
||||
CT_IBEAM => CursorIcon::Text,
|
||||
CT_WAIT => CursorIcon::Wait,
|
||||
CT_HELP => CursorIcon::Help,
|
||||
CT_EASTRESIZE => CursorIcon::EResize,
|
||||
CT_NORTHRESIZE => CursorIcon::NResize,
|
||||
CT_NORTHEASTRESIZE => CursorIcon::NeResize,
|
||||
CT_NORTHWESTRESIZE => CursorIcon::NwResize,
|
||||
CT_SOUTHRESIZE => CursorIcon::SResize,
|
||||
CT_SOUTHEASTRESIZE => CursorIcon::SeResize,
|
||||
CT_SOUTHWESTRESIZE => CursorIcon::SwResize,
|
||||
CT_WESTRESIZE => CursorIcon::WResize,
|
||||
CT_NORTHSOUTHRESIZE => CursorIcon::NsResize,
|
||||
CT_EASTWESTRESIZE => CursorIcon::EwResize,
|
||||
CT_NORTHEASTSOUTHWESTRESIZE => CursorIcon::NeswResize,
|
||||
CT_NORTHWESTSOUTHEASTRESIZE => CursorIcon::NwseResize,
|
||||
CT_COLUMNRESIZE => CursorIcon::ColResize,
|
||||
CT_ROWRESIZE => CursorIcon::RowResize,
|
||||
CT_MIDDLEPANNING => CursorIcon::AllScroll,
|
||||
CT_EASTPANNING => CursorIcon::AllScroll,
|
||||
CT_NORTHPANNING => CursorIcon::AllScroll,
|
||||
CT_NORTHEASTPANNING => CursorIcon::AllScroll,
|
||||
CT_NORTHWESTPANNING => CursorIcon::AllScroll,
|
||||
CT_SOUTHPANNING => CursorIcon::AllScroll,
|
||||
CT_SOUTHEASTPANNING => CursorIcon::AllScroll,
|
||||
CT_SOUTHWESTPANNING => CursorIcon::AllScroll,
|
||||
CT_WESTPANNING => CursorIcon::AllScroll,
|
||||
CT_MOVE => CursorIcon::Move,
|
||||
CT_VERTICALTEXT => CursorIcon::VerticalText,
|
||||
CT_CELL => CursorIcon::Cell,
|
||||
CT_CONTEXTMENU => CursorIcon::ContextMenu,
|
||||
CT_ALIAS => CursorIcon::Alias,
|
||||
CT_PROGRESS => CursorIcon::Progress,
|
||||
CT_NODROP => CursorIcon::NoDrop,
|
||||
CT_COPY => CursorIcon::Copy,
|
||||
CT_NOTALLOWED => CursorIcon::NotAllowed,
|
||||
CT_ZOOMIN => CursorIcon::ZoomIn,
|
||||
CT_ZOOMOUT => CursorIcon::ZoomOut,
|
||||
CT_GRAB => CursorIcon::Grab,
|
||||
CT_GRABBING => CursorIcon::Grabbing,
|
||||
CT_MIDDLE_PANNING_VERTICAL => CursorIcon::AllScroll,
|
||||
CT_MIDDLE_PANNING_HORIZONTAL => CursorIcon::AllScroll,
|
||||
CT_DND_NONE => CursorIcon::Default,
|
||||
CT_DND_MOVE => CursorIcon::Move,
|
||||
CT_DND_COPY => CursorIcon::Copy,
|
||||
CT_DND_LINK => CursorIcon::Alias,
|
||||
CT_NUM_VALUES => CursorIcon::Default,
|
||||
CT_NONE => {
|
||||
self.delegate.cursor_change(crate::Cursor::None);
|
||||
return 1; // We handled the cursor change.
|
||||
}
|
||||
_ => CursorIcon::Default,
|
||||
};
|
||||
|
||||
self.delegate.cursor_change(cursor.into());
|
||||
|
||||
1 // We handled the cursor change.
|
||||
}
|
||||
|
||||
fn on_console_message(&self, _browser: Option<&mut cef::Browser>, level: cef::LogSeverity, message: Option<&CefString>, source: Option<&CefString>, line: std::ffi::c_int) -> std::ffi::c_int {
|
||||
let message = message.map(|m| m.to_string()).unwrap_or_default();
|
||||
let source = source.map(|s| s.to_string()).unwrap_or_default();
|
||||
let line = line as i64;
|
||||
let browser_source = format!("{source}:{line}");
|
||||
static BROWSER: &str = "browser";
|
||||
match level.as_ref() {
|
||||
LOGSEVERITY_FATAL | LOGSEVERITY_ERROR => tracing::error!(target: BROWSER, "{browser_source} {message}"),
|
||||
LOGSEVERITY_WARNING => tracing::warn!(target: BROWSER, "{browser_source} {message}"),
|
||||
LOGSEVERITY_INFO => tracing::info!(target: BROWSER, "{browser_source} {message}"),
|
||||
LOGSEVERITY_DEFAULT | LOGSEVERITY_VERBOSE => tracing::debug!(target: BROWSER, "{browser_source} {message}"),
|
||||
_ => tracing::trace!(target: BROWSER, "{browser_source} {message}"),
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
fn get_raw(&self) -> *mut _cef_display_handler_t {
|
||||
self.object.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for DisplayHandlerImpl {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
let rc_impl = &mut *self.object;
|
||||
rc_impl.interface.add_ref();
|
||||
}
|
||||
Self {
|
||||
object: self.object,
|
||||
delegate: self.delegate.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Rc for DisplayHandlerImpl {
|
||||
fn as_base(&self) -> &cef_base_ref_counted_t {
|
||||
unsafe {
|
||||
let base = &*self.object;
|
||||
std::mem::transmute(&base.cef_object)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl WrapDisplayHandler for DisplayHandlerImpl {
|
||||
fn wrap_rc(&mut self, object: *mut RcImpl<_cef_display_handler_t, Self>) {
|
||||
self.object = object;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
use cef::rc::{Rc, RcImpl};
|
||||
use cef::sys::{_cef_life_span_handler_t, cef_base_ref_counted_t};
|
||||
use cef::{ImplLifeSpanHandler, WrapLifeSpanHandler};
|
||||
|
||||
pub(crate) struct LifeSpanHandlerImpl {
|
||||
object: *mut RcImpl<_cef_life_span_handler_t, Self>,
|
||||
}
|
||||
impl LifeSpanHandlerImpl {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self { object: std::ptr::null_mut() }
|
||||
}
|
||||
}
|
||||
|
||||
impl ImplLifeSpanHandler for LifeSpanHandlerImpl {
|
||||
fn on_before_popup(
|
||||
&self,
|
||||
_browser: Option<&mut cef::Browser>,
|
||||
_frame: Option<&mut cef::Frame>,
|
||||
_popup_id: std::ffi::c_int,
|
||||
target_url: Option<&cef::CefString>,
|
||||
_target_frame_name: Option<&cef::CefString>,
|
||||
_target_disposition: cef::WindowOpenDisposition,
|
||||
_user_gesture: std::ffi::c_int,
|
||||
_popup_features: Option<&cef::PopupFeatures>,
|
||||
_window_info: Option<&mut cef::WindowInfo>,
|
||||
_client: Option<&mut Option<cef::Client>>,
|
||||
_settings: Option<&mut cef::BrowserSettings>,
|
||||
_extra_info: Option<&mut Option<cef::DictionaryValue>>,
|
||||
_no_javascript_access: Option<&mut std::ffi::c_int>,
|
||||
) -> std::ffi::c_int {
|
||||
let target = target_url.map(|url| url.to_string()).unwrap_or("unknown".to_string());
|
||||
tracing::error!("Browser tried to open a popup at URL: {}", target);
|
||||
|
||||
// Deny any popup by returning 1
|
||||
1
|
||||
}
|
||||
|
||||
fn get_raw(&self) -> *mut _cef_life_span_handler_t {
|
||||
self.object.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for LifeSpanHandlerImpl {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
let rc_impl = &mut *self.object;
|
||||
rc_impl.interface.add_ref();
|
||||
}
|
||||
Self { object: self.object }
|
||||
}
|
||||
}
|
||||
impl Rc for LifeSpanHandlerImpl {
|
||||
fn as_base(&self) -> &cef_base_ref_counted_t {
|
||||
unsafe {
|
||||
let base = &*self.object;
|
||||
std::mem::transmute(&base.cef_object)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl WrapLifeSpanHandler for LifeSpanHandlerImpl {
|
||||
fn wrap_rc(&mut self, object: *mut RcImpl<_cef_life_span_handler_t, Self>) {
|
||||
self.object = object;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
use cef::rc::{Rc, RcImpl};
|
||||
use cef::sys::{_cef_load_handler_t, cef_base_ref_counted_t, cef_load_handler_t};
|
||||
use cef::{ImplBrowser, ImplBrowserHost, ImplLoadHandler, WrapLoadHandler};
|
||||
|
||||
use crate::delegate::BrowserDelegate;
|
||||
|
||||
pub(crate) struct LoadHandlerImpl {
|
||||
object: *mut RcImpl<cef_load_handler_t, Self>,
|
||||
delegate: BrowserDelegate,
|
||||
}
|
||||
impl LoadHandlerImpl {
|
||||
pub(crate) fn new(delegate: BrowserDelegate) -> Self {
|
||||
Self {
|
||||
object: std::ptr::null_mut(),
|
||||
delegate,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ImplLoadHandler for LoadHandlerImpl {
|
||||
fn on_loading_state_change(&self, browser: Option<&mut cef::Browser>, is_loading: std::ffi::c_int, _can_go_back: std::ffi::c_int, _can_go_forward: std::ffi::c_int) {
|
||||
let view_info = self.delegate.view_info();
|
||||
|
||||
if let Some(browser) = browser
|
||||
&& is_loading == 0
|
||||
{
|
||||
browser.host().unwrap().set_zoom_level(view_info.zoom());
|
||||
}
|
||||
}
|
||||
|
||||
fn get_raw(&self) -> *mut _cef_load_handler_t {
|
||||
self.object.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for LoadHandlerImpl {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
let rc_impl = &mut *self.object;
|
||||
rc_impl.interface.add_ref();
|
||||
}
|
||||
Self {
|
||||
object: self.object,
|
||||
delegate: self.delegate.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Rc for LoadHandlerImpl {
|
||||
fn as_base(&self) -> &cef_base_ref_counted_t {
|
||||
unsafe {
|
||||
let base = &*self.object;
|
||||
std::mem::transmute(&base.cef_object)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl WrapLoadHandler for LoadHandlerImpl {
|
||||
fn wrap_rc(&mut self, object: *mut RcImpl<_cef_load_handler_t, Self>) {
|
||||
self.object = object;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
use cef::rc::{Rc, RcImpl};
|
||||
use cef::sys::{_cef_render_handler_t, cef_base_ref_counted_t};
|
||||
use cef::{Browser, ImplRenderHandler, PaintElementType, Rect, WrapRenderHandler};
|
||||
|
||||
use crate::delegate::BrowserDelegate;
|
||||
use crate::frames::FrameStreamer;
|
||||
|
||||
pub(crate) struct RenderHandlerImpl {
|
||||
object: *mut RcImpl<_cef_render_handler_t, Self>,
|
||||
delegate: BrowserDelegate,
|
||||
frames: FrameStreamer,
|
||||
}
|
||||
impl RenderHandlerImpl {
|
||||
pub(crate) fn new(delegate: BrowserDelegate, frames: FrameStreamer) -> Self {
|
||||
Self {
|
||||
object: std::ptr::null_mut(),
|
||||
delegate,
|
||||
frames,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ImplRenderHandler for RenderHandlerImpl {
|
||||
fn view_rect(&self, _browser: Option<&mut Browser>, rect: Option<&mut Rect>) {
|
||||
if let Some(rect) = rect {
|
||||
let view_info = self.delegate.view_info();
|
||||
*rect = Rect {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: view_info.width() as i32,
|
||||
height: view_info.height() as i32,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn on_paint(&self, _browser: Option<&mut Browser>, type_: PaintElementType, _dirty_rects: Option<&[Rect]>, buffer: *const u8, width: std::ffi::c_int, height: std::ffi::c_int) {
|
||||
if type_ != PaintElementType::default() {
|
||||
return;
|
||||
}
|
||||
|
||||
let buffer_size = (width * height * 4) as usize;
|
||||
let buffer_slice = unsafe { std::slice::from_raw_parts(buffer, buffer_size) };
|
||||
|
||||
self.frames.stage_buffer(buffer_slice, width as u32, height as u32);
|
||||
self.frames.publish();
|
||||
}
|
||||
|
||||
#[cfg(feature = "accelerated_paint")]
|
||||
fn on_accelerated_paint(&self, _browser: Option<&mut Browser>, type_: PaintElementType, _dirty_rects: Option<&[Rect]>, info: Option<&cef::AcceleratedPaintInfo>) {
|
||||
if type_ != PaintElementType::default() {
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(info) = info else {
|
||||
tracing::error!("Accelerated paint callback received no info about the painted frame");
|
||||
return;
|
||||
};
|
||||
self.frames.stage_texture(info);
|
||||
self.frames.publish();
|
||||
}
|
||||
|
||||
fn get_raw(&self) -> *mut _cef_render_handler_t {
|
||||
self.object.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for RenderHandlerImpl {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
let rc_impl = &mut *self.object;
|
||||
rc_impl.interface.add_ref();
|
||||
}
|
||||
Self {
|
||||
object: self.object,
|
||||
delegate: self.delegate.clone(),
|
||||
frames: self.frames.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Rc for RenderHandlerImpl {
|
||||
fn as_base(&self) -> &cef_base_ref_counted_t {
|
||||
unsafe {
|
||||
let base = &*self.object;
|
||||
std::mem::transmute(&base.cef_object)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl WrapRenderHandler for RenderHandlerImpl {
|
||||
fn wrap_rc(&mut self, object: *mut RcImpl<_cef_render_handler_t, Self>) {
|
||||
self.object = object;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
use cef::rc::{Rc, RcImpl};
|
||||
use cef::sys::{_cef_app_t, cef_base_ref_counted_t};
|
||||
use cef::{App, ImplApp, RenderProcessHandler, SchemeRegistrar, WrapApp};
|
||||
|
||||
use super::render_process_handler::RenderProcessHandlerImpl;
|
||||
use super::scheme_handler_factory::register_schemes;
|
||||
|
||||
pub(crate) struct RenderProcessAppImpl {
|
||||
object: *mut RcImpl<_cef_app_t, Self>,
|
||||
render_process_handler: RenderProcessHandler,
|
||||
}
|
||||
impl RenderProcessAppImpl {
|
||||
pub(crate) fn app() -> App {
|
||||
App::new(Self {
|
||||
object: std::ptr::null_mut(),
|
||||
render_process_handler: RenderProcessHandler::new(RenderProcessHandlerImpl::new()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ImplApp for RenderProcessAppImpl {
|
||||
fn on_register_custom_schemes(&self, registrar: Option<&mut SchemeRegistrar>) {
|
||||
register_schemes(registrar);
|
||||
}
|
||||
|
||||
fn render_process_handler(&self) -> Option<RenderProcessHandler> {
|
||||
Some(self.render_process_handler.clone())
|
||||
}
|
||||
|
||||
fn get_raw(&self) -> *mut _cef_app_t {
|
||||
self.object.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for RenderProcessAppImpl {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
let rc_impl = &mut *self.object;
|
||||
rc_impl.interface.add_ref();
|
||||
}
|
||||
Self {
|
||||
object: self.object,
|
||||
render_process_handler: self.render_process_handler.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Rc for RenderProcessAppImpl {
|
||||
fn as_base(&self) -> &cef_base_ref_counted_t {
|
||||
unsafe {
|
||||
let base = &*self.object;
|
||||
std::mem::transmute(&base.cef_object)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl WrapApp for RenderProcessAppImpl {
|
||||
fn wrap_rc(&mut self, object: *mut RcImpl<_cef_app_t, Self>) {
|
||||
self.object = object;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
use cef::rc::{ConvertReturnValue, Rc, RcImpl};
|
||||
use cef::sys::{_cef_render_process_handler_t, cef_base_ref_counted_t, cef_render_process_handler_t, cef_v8_propertyattribute_t, cef_v8_value_create_array_buffer_with_copy};
|
||||
use cef::{ImplFrame, ImplRenderProcessHandler, ImplV8Context, ImplV8Value, V8Handler, V8Propertyattribute, V8Value, WrapRenderProcessHandler, v8_value_create_function};
|
||||
|
||||
use crate::ipc::{MessageType, UnpackMessage, UnpackedMessage};
|
||||
|
||||
use super::render_process_v8_handler::RenderProcessV8HandlerImpl;
|
||||
|
||||
pub(crate) struct RenderProcessHandlerImpl {
|
||||
object: *mut RcImpl<cef_render_process_handler_t, Self>,
|
||||
}
|
||||
impl RenderProcessHandlerImpl {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self { object: std::ptr::null_mut() }
|
||||
}
|
||||
}
|
||||
|
||||
impl ImplRenderProcessHandler for RenderProcessHandlerImpl {
|
||||
fn on_process_message_received(
|
||||
&self,
|
||||
_browser: Option<&mut cef::Browser>,
|
||||
frame: Option<&mut cef::Frame>,
|
||||
_source_process: cef::ProcessId,
|
||||
message: Option<&mut cef::ProcessMessage>,
|
||||
) -> std::ffi::c_int {
|
||||
let unpacked_message = unsafe { message.and_then(|m| m.unpack()) };
|
||||
match unpacked_message {
|
||||
Some(UnpackedMessage {
|
||||
message_type: MessageType::SendToJS,
|
||||
data,
|
||||
}) => {
|
||||
let Some(frame) = frame else {
|
||||
tracing::error!("Frame is not available");
|
||||
return 0;
|
||||
};
|
||||
let Some(context) = frame.v8_context() else {
|
||||
tracing::error!("V8 context is not available");
|
||||
return 0;
|
||||
};
|
||||
if context.enter() == 0 {
|
||||
tracing::error!("Failed to enter V8 context");
|
||||
return 0;
|
||||
}
|
||||
let mut value: V8Value = unsafe { cef_v8_value_create_array_buffer_with_copy(data.as_ptr() as *mut std::ffi::c_void, data.len()) }.wrap_result();
|
||||
let Some(global) = context.global() else {
|
||||
tracing::error!("Global object is not available in V8 context");
|
||||
return 0;
|
||||
};
|
||||
|
||||
let function_name = "receiveNativeMessage";
|
||||
let property_name = "receiveNativeMessageData";
|
||||
|
||||
let function_call = format!("window.{function_name}(window.{property_name})");
|
||||
|
||||
global.set_value_bykey(Some(&property_name.into()), Some(&mut value), cef_v8_propertyattribute_t::V8_PROPERTY_ATTRIBUTE_READONLY.wrap_result());
|
||||
|
||||
if global.value_bykey(Some(&function_name.into())).is_some() {
|
||||
frame.execute_java_script(Some(&function_call.as_str().into()), None, 0);
|
||||
}
|
||||
|
||||
if context.exit() == 0 {
|
||||
tracing::error!("Failed to exit V8 context");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
tracing::error!("Unexpected message type received in render process");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
1
|
||||
}
|
||||
|
||||
fn on_context_created(&self, _browser: Option<&mut cef::Browser>, _frame: Option<&mut cef::Frame>, context: Option<&mut cef::V8Context>) {
|
||||
let register_js_function = |context: &mut cef::V8Context, name: &'static str| {
|
||||
let mut v8_handler = V8Handler::new(RenderProcessV8HandlerImpl::new());
|
||||
let Some(mut function) = v8_value_create_function(Some(&name.into()), Some(&mut v8_handler)) else {
|
||||
tracing::error!("Failed to create V8 function {name}");
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(global) = context.global() else {
|
||||
tracing::error!("Global object is not available in V8 context");
|
||||
return;
|
||||
};
|
||||
global.set_value_bykey(Some(&name.into()), Some(&mut function), V8Propertyattribute::default());
|
||||
};
|
||||
|
||||
let Some(context) = context else {
|
||||
tracing::error!("V8 context is not available");
|
||||
return;
|
||||
};
|
||||
|
||||
let initialized_function_name = "initializeNativeCommunication";
|
||||
let send_function_name = "sendNativeMessage";
|
||||
|
||||
register_js_function(context, initialized_function_name);
|
||||
register_js_function(context, send_function_name);
|
||||
}
|
||||
|
||||
fn get_raw(&self) -> *mut _cef_render_process_handler_t {
|
||||
self.object.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for RenderProcessHandlerImpl {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
let rc_impl = &mut *self.object;
|
||||
rc_impl.interface.add_ref();
|
||||
}
|
||||
Self { object: self.object }
|
||||
}
|
||||
}
|
||||
impl Rc for RenderProcessHandlerImpl {
|
||||
fn as_base(&self) -> &cef_base_ref_counted_t {
|
||||
unsafe {
|
||||
let base = &*self.object;
|
||||
std::mem::transmute(&base.cef_object)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl WrapRenderProcessHandler for RenderProcessHandlerImpl {
|
||||
fn wrap_rc(&mut self, object: *mut RcImpl<_cef_render_process_handler_t, Self>) {
|
||||
self.object = object;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
use cef::{ImplV8Handler, ImplV8Value, V8Value, WrapV8Handler, rc::Rc, v8_context_get_current_context};
|
||||
|
||||
use crate::ipc::{MessageType, SendMessage};
|
||||
|
||||
pub struct RenderProcessV8HandlerImpl {
|
||||
object: *mut cef::rc::RcImpl<cef::sys::_cef_v8_handler_t, Self>,
|
||||
}
|
||||
impl RenderProcessV8HandlerImpl {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self { object: std::ptr::null_mut() }
|
||||
}
|
||||
}
|
||||
|
||||
impl ImplV8Handler for RenderProcessV8HandlerImpl {
|
||||
fn execute(
|
||||
&self,
|
||||
name: Option<&cef::CefString>,
|
||||
_object: Option<&mut V8Value>,
|
||||
arguments: Option<&[Option<V8Value>]>,
|
||||
_retval: Option<&mut Option<V8Value>>,
|
||||
_exception: Option<&mut cef::CefString>,
|
||||
) -> std::ffi::c_int {
|
||||
match name.map(|s| s.to_string()).unwrap_or_default().as_str() {
|
||||
"initializeNativeCommunication" => {
|
||||
v8_context_get_current_context().send_message(MessageType::Initialized, vec![0u8].as_slice());
|
||||
}
|
||||
"sendNativeMessage" => {
|
||||
let Some(args) = arguments else {
|
||||
tracing::error!("No arguments provided to sendNativeMessage");
|
||||
return 0;
|
||||
};
|
||||
let Some(arg1) = args.first() else {
|
||||
tracing::error!("No arguments provided to sendNativeMessage");
|
||||
return 0;
|
||||
};
|
||||
let Some(arg1) = arg1.as_ref() else {
|
||||
tracing::error!("First argument to sendNativeMessage is not an ArrayBuffer");
|
||||
return 0;
|
||||
};
|
||||
if arg1.is_array_buffer() == 0 {
|
||||
tracing::error!("First argument to sendNativeMessage is not an ArrayBuffer");
|
||||
return 0;
|
||||
}
|
||||
|
||||
let size = arg1.array_buffer_byte_length();
|
||||
let ptr = arg1.array_buffer_data();
|
||||
let data = unsafe { std::slice::from_raw_parts_mut(ptr as *mut u8, size) };
|
||||
|
||||
v8_context_get_current_context().send_message(MessageType::SendToNative, data);
|
||||
|
||||
return 1;
|
||||
}
|
||||
name => {
|
||||
tracing::error!("Unknown V8 function called: {}", name);
|
||||
}
|
||||
}
|
||||
1
|
||||
}
|
||||
|
||||
fn get_raw(&self) -> *mut cef::sys::_cef_v8_handler_t {
|
||||
self.object.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for RenderProcessV8HandlerImpl {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
let rc_impl = &mut *self.object;
|
||||
rc_impl.interface.add_ref();
|
||||
}
|
||||
Self { object: self.object }
|
||||
}
|
||||
}
|
||||
impl Rc for RenderProcessV8HandlerImpl {
|
||||
fn as_base(&self) -> &cef::sys::cef_base_ref_counted_t {
|
||||
unsafe {
|
||||
let base = &*self.object;
|
||||
std::mem::transmute(&base.cef_object)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl WrapV8Handler for RenderProcessV8HandlerImpl {
|
||||
fn wrap_rc(&mut self, object: *mut cef::rc::RcImpl<cef::sys::_cef_v8_handler_t, Self>) {
|
||||
self.object = object;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
use cef::rc::{Rc, RcImpl};
|
||||
use cef::sys::{_cef_request_handler_t, cef_base_ref_counted_t};
|
||||
use cef::{AuthCallback, Browser, CefString, Frame, ImplRequest, ImplRequestHandler, Request, ResourceRequestHandler, WrapRequestHandler};
|
||||
use std::ffi::c_int;
|
||||
|
||||
use super::resource_request_handler::ResourceRequestHandlerImpl;
|
||||
use crate::consts::{RESOURCE_DOMAIN, RESOURCE_SCHEME};
|
||||
|
||||
pub(crate) struct RequestHandlerImpl {
|
||||
object: *mut RcImpl<_cef_request_handler_t, Self>,
|
||||
}
|
||||
|
||||
impl RequestHandlerImpl {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self { object: std::ptr::null_mut() }
|
||||
}
|
||||
}
|
||||
|
||||
impl ImplRequestHandler for RequestHandlerImpl {
|
||||
fn on_before_browse(&self, _browser: Option<&mut Browser>, _frame: Option<&mut Frame>, request: Option<&mut Request>, _user_gesture: c_int, _is_redirect: c_int) -> c_int {
|
||||
let Some(request) = request else { return 1 };
|
||||
let url = CefString::from(&request.url()).to_string();
|
||||
if url.starts_with(&format!("{RESOURCE_SCHEME}://{RESOURCE_DOMAIN}/")) {
|
||||
0
|
||||
} else {
|
||||
tracing::warn!("Blocked navigation to: {}", url);
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
fn resource_request_handler(
|
||||
&self,
|
||||
_browser: Option<&mut Browser>,
|
||||
_frame: Option<&mut Frame>,
|
||||
_request: Option<&mut Request>,
|
||||
_is_navigation: c_int,
|
||||
_is_download: c_int,
|
||||
_request_initiator: Option<&CefString>,
|
||||
_disable_default_handling: Option<&mut c_int>,
|
||||
) -> Option<ResourceRequestHandler> {
|
||||
Some(ResourceRequestHandler::new(ResourceRequestHandlerImpl::new()))
|
||||
}
|
||||
|
||||
fn auth_credentials(
|
||||
&self,
|
||||
_browser: Option<&mut Browser>,
|
||||
_origin_url: Option<&CefString>,
|
||||
_is_proxy: c_int,
|
||||
_host: Option<&CefString>,
|
||||
_port: c_int,
|
||||
_realm: Option<&CefString>,
|
||||
_scheme: Option<&CefString>,
|
||||
_callback: Option<&mut AuthCallback>,
|
||||
) -> c_int {
|
||||
0
|
||||
}
|
||||
|
||||
fn get_raw(&self) -> *mut _cef_request_handler_t {
|
||||
self.object.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for RequestHandlerImpl {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
let rc_impl = &mut *self.object;
|
||||
rc_impl.interface.add_ref();
|
||||
}
|
||||
Self { object: self.object }
|
||||
}
|
||||
}
|
||||
impl Rc for RequestHandlerImpl {
|
||||
fn as_base(&self) -> &cef_base_ref_counted_t {
|
||||
unsafe {
|
||||
let base = &*self.object;
|
||||
std::mem::transmute(&base.cef_object)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl WrapRequestHandler for RequestHandlerImpl {
|
||||
fn wrap_rc(&mut self, object: *mut RcImpl<_cef_request_handler_t, Self>) {
|
||||
self.object = object;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
use cef::rc::{Rc, RcImpl};
|
||||
use cef::sys::{_cef_resource_handler_t, cef_base_ref_counted_t};
|
||||
use cef::{Callback, CefString, ImplResourceHandler, ImplResponse, Request, ResourceReadCallback, Response, WrapResourceHandler};
|
||||
use std::cell::RefCell;
|
||||
use std::ffi::c_int;
|
||||
use std::io::Read;
|
||||
|
||||
use crate::resources::{Resource, ResourceReader};
|
||||
|
||||
pub(crate) struct ResourceHandlerImpl {
|
||||
object: *mut RcImpl<_cef_resource_handler_t, Self>,
|
||||
reader: Option<RefCell<ResourceReader>>,
|
||||
mimetype: Option<String>,
|
||||
}
|
||||
|
||||
impl ResourceHandlerImpl {
|
||||
pub fn new(resource: Option<Resource>) -> Self {
|
||||
if let Some(resource) = resource {
|
||||
Self {
|
||||
object: std::ptr::null_mut(),
|
||||
reader: Some(resource.reader.into()),
|
||||
mimetype: resource.mimetype,
|
||||
}
|
||||
} else {
|
||||
Self {
|
||||
object: std::ptr::null_mut(),
|
||||
reader: None,
|
||||
mimetype: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ImplResourceHandler for ResourceHandlerImpl {
|
||||
fn open(&self, _request: Option<&mut Request>, handle_request: Option<&mut c_int>, _callback: Option<&mut Callback>) -> c_int {
|
||||
if let Some(handle_request) = handle_request {
|
||||
*handle_request = 1;
|
||||
}
|
||||
1
|
||||
}
|
||||
|
||||
fn response_headers(&self, response: Option<&mut Response>, response_length: Option<&mut i64>, _redirect_url: Option<&mut CefString>) {
|
||||
if let Some(response_length) = response_length {
|
||||
*response_length = -1; // Indicating that the length is unknown
|
||||
}
|
||||
if let Some(response) = response {
|
||||
if self.reader.is_some() {
|
||||
if let Some(mimetype) = &self.mimetype {
|
||||
response.set_mime_type(Some(&mimetype.as_str().into()));
|
||||
} else {
|
||||
response.set_mime_type(None);
|
||||
}
|
||||
response.set_status(200);
|
||||
} else {
|
||||
response.set_status(404);
|
||||
response.set_mime_type(Some(&"text/plain".into()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read(&self, data_out: *mut u8, bytes_to_read: c_int, bytes_read: Option<&mut c_int>, _callback: Option<&mut ResourceReadCallback>) -> c_int {
|
||||
let Some(bytes_read) = bytes_read else { unreachable!() };
|
||||
let out = unsafe { std::slice::from_raw_parts_mut(data_out, bytes_to_read as usize) };
|
||||
if let Some(reader) = &self.reader {
|
||||
if let Ok(read) = reader.borrow_mut().read(out) {
|
||||
*bytes_read = read as i32;
|
||||
if read > 0 {
|
||||
return 1; // Indicating that data was read
|
||||
}
|
||||
} else {
|
||||
*bytes_read = -2; // Indicating ERR_FAILED
|
||||
}
|
||||
}
|
||||
0 // Indicating no data was read
|
||||
}
|
||||
|
||||
fn get_raw(&self) -> *mut _cef_resource_handler_t {
|
||||
self.object.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for ResourceHandlerImpl {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
let rc_impl = &mut *self.object;
|
||||
rc_impl.interface.add_ref();
|
||||
}
|
||||
Self {
|
||||
object: self.object,
|
||||
reader: self.reader.clone(),
|
||||
mimetype: self.mimetype.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Rc for ResourceHandlerImpl {
|
||||
fn as_base(&self) -> &cef_base_ref_counted_t {
|
||||
unsafe {
|
||||
let base = &*self.object;
|
||||
std::mem::transmute(&base.cef_object)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl WrapResourceHandler for ResourceHandlerImpl {
|
||||
fn wrap_rc(&mut self, object: *mut RcImpl<_cef_resource_handler_t, Self>) {
|
||||
self.object = object;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
use cef::rc::{Rc, RcImpl};
|
||||
use cef::sys::{_cef_resource_request_handler_t, cef_base_ref_counted_t};
|
||||
use cef::{Browser, Callback, CefString, Frame, ImplRequest, ImplResourceRequestHandler, Request, ReturnValue, WrapResourceRequestHandler};
|
||||
|
||||
use crate::consts::{RESOURCE_DOMAIN, RESOURCE_SCHEME};
|
||||
|
||||
// TODO: Deny all external requests once we stop relying on google fonts for font preview
|
||||
fn is_allowed_url(url: &str) -> bool {
|
||||
url.starts_with(&format!("{RESOURCE_SCHEME}://{RESOURCE_DOMAIN}/")) || url.starts_with("https://fonts.googleapis.com/css2") || url.starts_with("https://fonts.gstatic.com/")
|
||||
}
|
||||
|
||||
pub(crate) struct ResourceRequestHandlerImpl {
|
||||
object: *mut RcImpl<_cef_resource_request_handler_t, Self>,
|
||||
}
|
||||
|
||||
impl ResourceRequestHandlerImpl {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self { object: std::ptr::null_mut() }
|
||||
}
|
||||
}
|
||||
|
||||
impl ImplResourceRequestHandler for ResourceRequestHandlerImpl {
|
||||
fn on_before_resource_load(&self, _browser: Option<&mut Browser>, _frame: Option<&mut Frame>, request: Option<&mut Request>, _callback: Option<&mut Callback>) -> ReturnValue {
|
||||
let Some(request) = request else { return ReturnValue::CANCEL };
|
||||
let url = CefString::from(&request.url()).to_string();
|
||||
if is_allowed_url(&url) {
|
||||
ReturnValue::CONTINUE
|
||||
} else {
|
||||
tracing::error!("Blocked resource load: {}", url);
|
||||
ReturnValue::CANCEL
|
||||
}
|
||||
}
|
||||
|
||||
fn get_raw(&self) -> *mut _cef_resource_request_handler_t {
|
||||
self.object.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for ResourceRequestHandlerImpl {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
let rc_impl = &mut *self.object;
|
||||
rc_impl.interface.add_ref();
|
||||
}
|
||||
Self { object: self.object }
|
||||
}
|
||||
}
|
||||
impl Rc for ResourceRequestHandlerImpl {
|
||||
fn as_base(&self) -> &cef_base_ref_counted_t {
|
||||
unsafe {
|
||||
let base = &*self.object;
|
||||
std::mem::transmute(&base.cef_object)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl WrapResourceRequestHandler for ResourceRequestHandlerImpl {
|
||||
fn wrap_rc(&mut self, object: *mut RcImpl<_cef_resource_request_handler_t, Self>) {
|
||||
self.object = object;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
use cef::rc::{Rc, RcImpl};
|
||||
use cef::sys::{_cef_scheme_handler_factory_t, cef_base_ref_counted_t, cef_scheme_options_t};
|
||||
use cef::{Browser, CefString, Frame, ImplRequest, ImplSchemeHandlerFactory, ImplSchemeRegistrar, Request, ResourceHandler, SchemeRegistrar, WrapSchemeHandlerFactory};
|
||||
|
||||
use super::resource_handler::ResourceHandlerImpl;
|
||||
use crate::consts::{RESOURCE_DOMAIN, RESOURCE_SCHEME};
|
||||
use crate::delegate::BrowserDelegate;
|
||||
|
||||
pub(crate) struct SchemeHandlerFactoryImpl {
|
||||
object: *mut RcImpl<_cef_scheme_handler_factory_t, Self>,
|
||||
delegate: BrowserDelegate,
|
||||
}
|
||||
impl SchemeHandlerFactoryImpl {
|
||||
pub(crate) fn new(delegate: BrowserDelegate) -> Self {
|
||||
Self {
|
||||
object: std::ptr::null_mut(),
|
||||
delegate,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn register_schemes(registrar: Option<&mut SchemeRegistrar>) {
|
||||
if let Some(registrar) = registrar {
|
||||
let mut scheme_options = 0;
|
||||
scheme_options |= cef_scheme_options_t::CEF_SCHEME_OPTION_STANDARD as i32;
|
||||
scheme_options |= cef_scheme_options_t::CEF_SCHEME_OPTION_FETCH_ENABLED as i32;
|
||||
scheme_options |= cef_scheme_options_t::CEF_SCHEME_OPTION_SECURE as i32;
|
||||
scheme_options |= cef_scheme_options_t::CEF_SCHEME_OPTION_CORS_ENABLED as i32;
|
||||
registrar.add_custom_scheme(Some(&RESOURCE_SCHEME.into()), scheme_options);
|
||||
}
|
||||
}
|
||||
|
||||
impl ImplSchemeHandlerFactory for SchemeHandlerFactoryImpl {
|
||||
fn create(&self, _browser: Option<&mut Browser>, _frame: Option<&mut Frame>, _scheme_name: Option<&CefString>, request: Option<&mut Request>) -> Option<ResourceHandler> {
|
||||
if let Some(request) = request {
|
||||
let url = CefString::from(&request.url()).to_string();
|
||||
let path = url
|
||||
.strip_prefix(&format!("{RESOURCE_SCHEME}://{RESOURCE_DOMAIN}/"))
|
||||
.expect("CEF should only call this for our custom scheme and domain that we registered this factory for");
|
||||
let resource = self.delegate.load_resource(path.to_string().into());
|
||||
return Some(ResourceHandler::new(ResourceHandlerImpl::new(resource)));
|
||||
}
|
||||
None
|
||||
}
|
||||
fn get_raw(&self) -> *mut _cef_scheme_handler_factory_t {
|
||||
self.object.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for SchemeHandlerFactoryImpl {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
let rc_impl = &mut *self.object;
|
||||
rc_impl.interface.add_ref();
|
||||
}
|
||||
Self {
|
||||
object: self.object,
|
||||
delegate: self.delegate.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Rc for SchemeHandlerFactoryImpl {
|
||||
fn as_base(&self) -> &cef_base_ref_counted_t {
|
||||
unsafe {
|
||||
let base = &*self.object;
|
||||
std::mem::transmute(&base.cef_object)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl WrapSchemeHandlerFactory for SchemeHandlerFactoryImpl {
|
||||
fn wrap_rc(&mut self, object: *mut RcImpl<_cef_scheme_handler_factory_t, Self>) {
|
||||
self.object = object;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
use cef::rc::{Rc, RcImpl};
|
||||
use cef::sys::{_cef_task_t, cef_base_ref_counted_t};
|
||||
use cef::{ImplTask, WrapTask};
|
||||
use std::cell::RefCell;
|
||||
|
||||
// Closure-based task wrapper following CEF patterns
|
||||
pub struct ClosureTask<F> {
|
||||
pub(crate) object: *mut RcImpl<_cef_task_t, Self>,
|
||||
pub(crate) closure: RefCell<Option<F>>,
|
||||
}
|
||||
|
||||
impl<F: FnOnce() + Send + 'static> ClosureTask<F> {
|
||||
pub fn new(closure: F) -> Self {
|
||||
Self {
|
||||
object: std::ptr::null_mut(),
|
||||
closure: RefCell::new(Some(closure)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: FnOnce() + Send + 'static> ImplTask for ClosureTask<F> {
|
||||
fn execute(&self) {
|
||||
if let Some(closure) = self.closure.borrow_mut().take() {
|
||||
closure();
|
||||
}
|
||||
}
|
||||
|
||||
fn get_raw(&self) -> *mut _cef_task_t {
|
||||
self.object.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: FnOnce() + Send + 'static> Clone for ClosureTask<F> {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
if !self.object.is_null() {
|
||||
let rc_impl = &mut *self.object;
|
||||
rc_impl.interface.add_ref();
|
||||
}
|
||||
}
|
||||
Self {
|
||||
object: self.object,
|
||||
closure: RefCell::new(None), // Closure can only be executed once
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: FnOnce() + Send + 'static> Rc for ClosureTask<F> {
|
||||
fn as_base(&self) -> &cef_base_ref_counted_t {
|
||||
unsafe {
|
||||
let base = &*self.object;
|
||||
std::mem::transmute(&base.cef_object)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: FnOnce() + Send + 'static> WrapTask for ClosureTask<F> {
|
||||
fn wrap_rc(&mut self, object: *mut RcImpl<_cef_task_t, Self>) {
|
||||
self.object = object;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user