mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-26 11:58:12 +08:00
Desktop: Cleaner CEF bidirectional message implementation (#2950)
* Rename CEF implementations to match the process they are called in * Rename CEF implementations to match the process they are called in * Implement ipc abstraction * Call js function `receiveNativeMessage` for all SendToJS ipc messages * Allow js to call `sendNativeMessage` for sending messages to the browser process Co-authored-by: Adam <adamgerhant@gmail.com> Co-authored-by: Dennis Kobert <dennis@kobert.dev> * Fix missing safety consideration --------- Co-authored-by: Adam <adamgerhant@gmail.com> Co-authored-by: Dennis Kobert <dennis@kobert.dev>
This commit is contained in:
co-authored by
Adam
Dennis Kobert
parent
7bdf1670b5
commit
83d39fb320
@@ -5,15 +5,16 @@ use cef::sys::{_cef_app_t, cef_base_ref_counted_t};
|
||||
use cef::{BrowserProcessHandler, CefString, ImplApp, ImplCommandLine, SchemeRegistrar, WrapApp};
|
||||
|
||||
use crate::cef::CefEventHandler;
|
||||
|
||||
use crate::cef::scheme_handler::GraphiteSchemeHandlerFactory;
|
||||
|
||||
use super::browser_process_handler::BrowserProcessHandlerImpl;
|
||||
|
||||
pub(crate) struct AppImpl<H: CefEventHandler> {
|
||||
pub(crate) struct BrowserProcessAppImpl<H: CefEventHandler> {
|
||||
object: *mut RcImpl<_cef_app_t, Self>,
|
||||
event_handler: H,
|
||||
}
|
||||
impl<H: CefEventHandler + Clone> AppImpl<H> {
|
||||
impl<H: CefEventHandler + Clone> BrowserProcessAppImpl<H> {
|
||||
pub(crate) fn new(event_handler: H) -> Self {
|
||||
Self {
|
||||
object: std::ptr::null_mut(),
|
||||
@@ -22,7 +23,7 @@ impl<H: CefEventHandler + Clone> AppImpl<H> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: CefEventHandler + Clone> ImplApp for AppImpl<H> {
|
||||
impl<H: CefEventHandler + Clone> ImplApp for BrowserProcessAppImpl<H> {
|
||||
fn browser_process_handler(&self) -> Option<BrowserProcessHandler> {
|
||||
Some(BrowserProcessHandler::new(BrowserProcessHandlerImpl::new(self.event_handler.clone())))
|
||||
}
|
||||
@@ -58,7 +59,7 @@ impl<H: CefEventHandler + Clone> ImplApp for AppImpl<H> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: CefEventHandler + Clone> Clone for AppImpl<H> {
|
||||
impl<H: CefEventHandler + Clone> Clone for BrowserProcessAppImpl<H> {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
let rc_impl = &mut *self.object;
|
||||
@@ -70,7 +71,7 @@ impl<H: CefEventHandler + Clone> Clone for AppImpl<H> {
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<H: CefEventHandler> Rc for AppImpl<H> {
|
||||
impl<H: CefEventHandler> Rc for BrowserProcessAppImpl<H> {
|
||||
fn as_base(&self) -> &cef_base_ref_counted_t {
|
||||
unsafe {
|
||||
let base = &*self.object;
|
||||
@@ -78,7 +79,7 @@ impl<H: CefEventHandler> Rc for AppImpl<H> {
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<H: CefEventHandler + Clone> WrapApp for AppImpl<H> {
|
||||
impl<H: CefEventHandler + Clone> WrapApp for BrowserProcessAppImpl<H> {
|
||||
fn wrap_rc(&mut self, object: *mut RcImpl<_cef_app_t, Self>) {
|
||||
self.object = object;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
use cef::rc::{Rc, RcImpl};
|
||||
use cef::sys::{_cef_client_t, cef_base_ref_counted_t};
|
||||
use cef::{ImplClient, RenderHandler, WrapClient};
|
||||
|
||||
use crate::cef::CefEventHandler;
|
||||
use crate::cef::ipc::{MessageType, UnpackMessage, UnpackedMessage};
|
||||
|
||||
pub(crate) struct BrowserProcessClientImpl<H: CefEventHandler> {
|
||||
object: *mut RcImpl<_cef_client_t, Self>,
|
||||
render_handler: RenderHandler,
|
||||
event_handler: H,
|
||||
}
|
||||
impl<H: CefEventHandler> BrowserProcessClientImpl<H> {
|
||||
pub(crate) fn new(render_handler: RenderHandler, event_handler: H) -> Self {
|
||||
Self {
|
||||
object: std::ptr::null_mut(),
|
||||
render_handler,
|
||||
event_handler,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: CefEventHandler> ImplClient for BrowserProcessClientImpl<H> {
|
||||
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::os::raw::c_int {
|
||||
let unpacked_message = unsafe { message.and_then(|m| m.unpack()) };
|
||||
match unpacked_message {
|
||||
Some(UnpackedMessage {
|
||||
message_type: MessageType::SendToNative,
|
||||
data,
|
||||
}) => self.event_handler.receive_web_message(data),
|
||||
|
||||
_ => {
|
||||
tracing::error!("Unexpected message type received in browser process");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
1
|
||||
}
|
||||
|
||||
fn render_handler(&self) -> Option<RenderHandler> {
|
||||
Some(self.render_handler.clone())
|
||||
}
|
||||
|
||||
fn get_raw(&self) -> *mut _cef_client_t {
|
||||
self.object.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: CefEventHandler> Clone for BrowserProcessClientImpl<H> {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
let rc_impl = &mut *self.object;
|
||||
rc_impl.interface.add_ref();
|
||||
}
|
||||
Self {
|
||||
object: self.object,
|
||||
render_handler: self.render_handler.clone(),
|
||||
event_handler: self.event_handler.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<H: CefEventHandler> Rc for BrowserProcessClientImpl<H> {
|
||||
fn as_base(&self) -> &cef_base_ref_counted_t {
|
||||
unsafe {
|
||||
let base = &*self.object;
|
||||
std::mem::transmute(&base.cef_object)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<H: CefEventHandler> WrapClient for BrowserProcessClientImpl<H> {
|
||||
fn wrap_rc(&mut self, object: *mut RcImpl<_cef_client_t, Self>) {
|
||||
self.object = object;
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
use cef::rc::{Rc, RcImpl};
|
||||
use cef::sys::{_cef_client_t, cef_base_ref_counted_t};
|
||||
use cef::{ImplClient, RenderHandler, WrapClient};
|
||||
|
||||
pub(crate) struct ClientImpl {
|
||||
object: *mut RcImpl<_cef_client_t, Self>,
|
||||
render_handler: RenderHandler,
|
||||
}
|
||||
impl ClientImpl {
|
||||
pub(crate) fn new(render_handler: RenderHandler) -> Self {
|
||||
Self {
|
||||
object: std::ptr::null_mut(),
|
||||
render_handler,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ImplClient for ClientImpl {
|
||||
fn render_handler(&self) -> Option<RenderHandler> {
|
||||
Some(self.render_handler.clone())
|
||||
}
|
||||
|
||||
fn get_raw(&self) -> *mut _cef_client_t {
|
||||
self.object.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for ClientImpl {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
let rc_impl = &mut *self.object;
|
||||
rc_impl.interface.add_ref();
|
||||
}
|
||||
Self {
|
||||
object: self.object,
|
||||
render_handler: self.render_handler.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Rc for ClientImpl {
|
||||
fn as_base(&self) -> &cef_base_ref_counted_t {
|
||||
unsafe {
|
||||
let base = &*self.object;
|
||||
std::mem::transmute(&base.cef_object)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl WrapClient for ClientImpl {
|
||||
fn wrap_rc(&mut self, object: *mut RcImpl<_cef_client_t, Self>) {
|
||||
self.object = object;
|
||||
}
|
||||
}
|
||||
+21
-9
@@ -1,38 +1,50 @@
|
||||
use cef::rc::{Rc, RcImpl};
|
||||
use cef::sys::{_cef_app_t, cef_base_ref_counted_t};
|
||||
use cef::{App, ImplApp, SchemeRegistrar, WrapApp};
|
||||
use cef::{App, ImplApp, RenderProcessHandler, SchemeRegistrar, WrapApp};
|
||||
|
||||
use super::render_process_handler::RenderProcessHandlerImpl;
|
||||
use crate::cef::scheme_handler::GraphiteSchemeHandlerFactory;
|
||||
|
||||
pub(crate) struct NonBrowserAppImpl {
|
||||
pub(crate) struct RenderProcessAppImpl {
|
||||
object: *mut RcImpl<_cef_app_t, Self>,
|
||||
render_process_handler: RenderProcessHandler,
|
||||
}
|
||||
impl NonBrowserAppImpl {
|
||||
impl RenderProcessAppImpl {
|
||||
pub(crate) fn app() -> App {
|
||||
App::new(Self { object: std::ptr::null_mut() })
|
||||
App::new(Self {
|
||||
object: std::ptr::null_mut(),
|
||||
render_process_handler: RenderProcessHandler::new(RenderProcessHandlerImpl::new()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ImplApp for NonBrowserAppImpl {
|
||||
impl ImplApp for RenderProcessAppImpl {
|
||||
fn on_register_custom_schemes(&self, registrar: Option<&mut SchemeRegistrar>) {
|
||||
GraphiteSchemeHandlerFactory::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 NonBrowserAppImpl {
|
||||
impl Clone for RenderProcessAppImpl {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
let rc_impl = &mut *self.object;
|
||||
rc_impl.interface.add_ref();
|
||||
}
|
||||
Self { object: self.object }
|
||||
Self {
|
||||
object: self.object,
|
||||
render_process_handler: self.render_process_handler.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Rc for NonBrowserAppImpl {
|
||||
impl Rc for RenderProcessAppImpl {
|
||||
fn as_base(&self) -> &cef_base_ref_counted_t {
|
||||
unsafe {
|
||||
let base = &*self.object;
|
||||
@@ -40,7 +52,7 @@ impl Rc for NonBrowserAppImpl {
|
||||
}
|
||||
}
|
||||
}
|
||||
impl WrapApp for NonBrowserAppImpl {
|
||||
impl WrapApp for RenderProcessAppImpl {
|
||||
fn wrap_rc(&mut self, object: *mut RcImpl<_cef_app_t, Self>) {
|
||||
self.object = object;
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
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::{
|
||||
CefString, ImplFrame, ImplRenderProcessHandler, ImplV8Context, ImplV8Value, V8Handler, V8Propertyattribute, V8Value, WrapRenderProcessHandler, v8_context_get_entered_context,
|
||||
v8_value_create_function,
|
||||
};
|
||||
|
||||
use crate::cef::ipc::{MessageType, UnpackMessage, UnpackedMessage};
|
||||
|
||||
use super::render_process_v8_handler::BrowserProcessV8HandlerImpl;
|
||||
|
||||
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::os::raw::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(&CefString::from(property_name)),
|
||||
Some(&mut value),
|
||||
cef_v8_propertyattribute_t::V8_PROPERTY_ATTRIBUTE_READONLY.wrap_result(),
|
||||
);
|
||||
|
||||
frame.execute_java_script(Some(&CefString::from(function_call.as_str())), 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 function_name = "sendNativeMessage";
|
||||
|
||||
let Some(context) = context else {
|
||||
tracing::error!("V8 context is not available");
|
||||
return;
|
||||
};
|
||||
|
||||
let mut v8_handler = V8Handler::new(BrowserProcessV8HandlerImpl::new());
|
||||
let Some(mut function) = v8_value_create_function(Some(&CefString::from(function_name)), Some(&mut v8_handler)) else {
|
||||
tracing::error!("Failed to create V8 function {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(&CefString::from(function_name)), Some(&mut function), V8Propertyattribute::default());
|
||||
}
|
||||
|
||||
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,83 @@
|
||||
use cef::{ImplV8Handler, ImplV8Value, V8Value, WrapV8Handler, rc::Rc, v8_context_get_current_context};
|
||||
|
||||
use crate::cef::ipc::{MessageType, SendMessage};
|
||||
|
||||
pub struct BrowserProcessV8HandlerImpl {
|
||||
object: *mut cef::rc::RcImpl<cef::sys::_cef_v8_handler_t, Self>,
|
||||
}
|
||||
|
||||
impl BrowserProcessV8HandlerImpl {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self { object: std::ptr::null_mut() }
|
||||
}
|
||||
}
|
||||
|
||||
impl ImplV8Handler for BrowserProcessV8HandlerImpl {
|
||||
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::os::raw::c_int {
|
||||
if let Some(name) = name {
|
||||
if name.to_string() == "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;
|
||||
}
|
||||
}
|
||||
1
|
||||
}
|
||||
|
||||
fn get_raw(&self) -> *mut cef::sys::_cef_v8_handler_t {
|
||||
self.object.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for BrowserProcessV8HandlerImpl {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
let rc_impl = &mut *self.object;
|
||||
rc_impl.interface.add_ref();
|
||||
}
|
||||
Self { object: self.object }
|
||||
}
|
||||
}
|
||||
|
||||
impl Rc for BrowserProcessV8HandlerImpl {
|
||||
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 BrowserProcessV8HandlerImpl {
|
||||
fn wrap_rc(&mut self, object: *mut cef::rc::RcImpl<cef::sys::_cef_v8_handler_t, Self>) {
|
||||
self.object = object;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user