Skip to content

Commit 50fc393

Browse files
timon-schellingadamgerhantTrueDoctor
committed
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>
1 parent 07f6529 commit 50fc393

6 files changed

Lines changed: 121 additions & 2 deletions

File tree

desktop/src/cef.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ pub(crate) trait CefEventHandler: Clone {
2121
/// Scheudule the main event loop to run the cef event loop after the timeout
2222
/// [`_cef_browser_process_handler_t::on_schedule_message_pump_work`] for more documentation.
2323
fn schedule_cef_message_loop_work(&self, scheduled_time: Instant);
24+
fn receive_web_message(&self, message: &[u8]);
2425
}
2526

2627
#[derive(Clone, Copy)]
@@ -118,4 +119,6 @@ impl CefEventHandler for CefHandler {
118119
fn schedule_cef_message_loop_work(&self, scheduled_time: std::time::Instant) {
119120
let _ = self.event_loop_proxy.send_event(CustomEvent::ScheduleBrowserWork(scheduled_time));
120121
}
122+
123+
fn receive_web_message(&self, message: &[u8]) {}
121124
}

desktop/src/cef/context.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use winit::event::WindowEvent;
77
use crate::cef::dirs::{cef_cache_dir, cef_data_dir};
88

99
use super::input::InputState;
10+
use super::ipc::{MessageType, SendMessage};
1011
use super::scheme_handler::{FRONTEND_DOMAIN, GRAPHITE_SCHEME};
1112
use super::{CefEventHandler, input};
1213

@@ -129,6 +130,10 @@ impl Context<Initialized> {
129130
browser.host().unwrap().was_resized();
130131
}
131132
}
133+
134+
pub(crate) fn send_web_message(&self, message: &[u8]) {
135+
self.send_message(MessageType::SendToJS, message);
136+
}
132137
}
133138

134139
impl<S: ContextState> Drop for Context<S> {

desktop/src/cef/internal.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ mod browser_process_handler;
44
mod render_handler;
55
mod render_process_app;
66
mod render_process_handler;
7+
mod render_process_v8_handler;
78

89
pub(crate) use browser_process_app::BrowserProcessAppImpl;
910
pub(crate) use browser_process_client::BrowserProcessClientImpl;

desktop/src/cef/internal/browser_process_client.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ impl<H: CefEventHandler> ImplClient for BrowserProcessClientImpl<H> {
3232
Some(UnpackedMessage {
3333
message_type: MessageType::SendToNative,
3434
data,
35-
}) => {}
35+
}) => self.event_handler.receive_web_message(data),
36+
3637
_ => {
3738
tracing::error!("Unexpected message type received in browser process");
3839
return 0;

desktop/src/cef/internal/render_process_handler.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
use cef::rc::{ConvertReturnValue, Rc, RcImpl};
22
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};
3-
use cef::{CefString, ImplFrame, ImplRenderProcessHandler, ImplV8Context, ImplV8Value, V8Value, WrapRenderProcessHandler, v8_context_get_entered_context};
3+
use cef::{
4+
CefString, ImplFrame, ImplRenderProcessHandler, ImplV8Context, ImplV8Value, V8Handler, V8Propertyattribute, V8Value, WrapRenderProcessHandler, v8_context_get_entered_context,
5+
v8_value_create_function,
6+
};
47

58
use crate::cef::ipc::{MessageType, UnpackMessage, UnpackedMessage};
69

10+
use super::render_process_v8_handler::BrowserProcessV8HandlerImpl;
11+
712
pub(crate) struct RenderProcessHandlerImpl {
813
object: *mut RcImpl<cef_render_process_handler_t, Self>,
914
}
@@ -70,6 +75,27 @@ impl ImplRenderProcessHandler for RenderProcessHandlerImpl {
7075
1
7176
}
7277

78+
fn on_context_created(&self, _browser: Option<&mut cef::Browser>, _frame: Option<&mut cef::Frame>, context: Option<&mut cef::V8Context>) {
79+
let function_name = "sendNativeMessage";
80+
81+
let Some(context) = context else {
82+
tracing::error!("V8 context is not available");
83+
return;
84+
};
85+
86+
let mut v8_handler = V8Handler::new(BrowserProcessV8HandlerImpl::new());
87+
let Some(mut function) = v8_value_create_function(Some(&CefString::from(function_name)), Some(&mut v8_handler)) else {
88+
tracing::error!("Failed to create V8 function {function_name}");
89+
return;
90+
};
91+
92+
let Some(global) = context.global() else {
93+
tracing::error!("Global object is not available in V8 context");
94+
return;
95+
};
96+
global.set_value_bykey(Some(&CefString::from(function_name)), Some(&mut function), V8Propertyattribute::default());
97+
}
98+
7399
fn get_raw(&self) -> *mut _cef_render_process_handler_t {
74100
self.object.cast()
75101
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
use cef::{ImplV8Handler, ImplV8Value, V8Value, WrapV8Handler, rc::Rc, v8_context_get_current_context};
2+
3+
use crate::cef::ipc::{MessageType, SendMessage};
4+
5+
pub struct BrowserProcessV8HandlerImpl {
6+
object: *mut cef::rc::RcImpl<cef::sys::_cef_v8_handler_t, Self>,
7+
}
8+
9+
impl BrowserProcessV8HandlerImpl {
10+
pub(crate) fn new() -> Self {
11+
Self { object: std::ptr::null_mut() }
12+
}
13+
}
14+
15+
impl ImplV8Handler for BrowserProcessV8HandlerImpl {
16+
fn execute(
17+
&self,
18+
name: Option<&cef::CefString>,
19+
_object: Option<&mut V8Value>,
20+
arguments: Option<&[Option<V8Value>]>,
21+
_retval: Option<&mut Option<V8Value>>,
22+
_exception: Option<&mut cef::CefString>,
23+
) -> ::std::os::raw::c_int {
24+
if let Some(name) = name {
25+
if name.to_string() == "sendNativeMessage" {
26+
let Some(args) = arguments else {
27+
tracing::error!("No arguments provided to sendNativeMessage");
28+
return 0;
29+
};
30+
let Some(arg1) = args.first() else {
31+
tracing::error!("No arguments provided to sendNativeMessage");
32+
return 0;
33+
};
34+
let Some(arg1) = arg1.as_ref() else {
35+
tracing::error!("First argument to sendNativeMessage is not an ArrayBuffer");
36+
return 0;
37+
};
38+
if arg1.is_array_buffer() == 0 {
39+
tracing::error!("First argument to sendNativeMessage is not an ArrayBuffer");
40+
return 0;
41+
}
42+
43+
let size = arg1.array_buffer_byte_length();
44+
let ptr = arg1.array_buffer_data();
45+
let data = unsafe { std::slice::from_raw_parts_mut(ptr as *mut u8, size) };
46+
47+
v8_context_get_current_context().send_message(MessageType::SendToNative, data);
48+
49+
return 1;
50+
}
51+
}
52+
1
53+
}
54+
55+
fn get_raw(&self) -> *mut cef::sys::_cef_v8_handler_t {
56+
self.object.cast()
57+
}
58+
}
59+
60+
impl Clone for BrowserProcessV8HandlerImpl {
61+
fn clone(&self) -> Self {
62+
unsafe {
63+
let rc_impl = &mut *self.object;
64+
rc_impl.interface.add_ref();
65+
}
66+
Self { object: self.object }
67+
}
68+
}
69+
70+
impl Rc for BrowserProcessV8HandlerImpl {
71+
fn as_base(&self) -> &cef::sys::cef_base_ref_counted_t {
72+
unsafe {
73+
let base = &*self.object;
74+
std::mem::transmute(&base.cef_object)
75+
}
76+
}
77+
}
78+
79+
impl WrapV8Handler for BrowserProcessV8HandlerImpl {
80+
fn wrap_rc(&mut self, object: *mut cef::rc::RcImpl<cef::sys::_cef_v8_handler_t, Self>) {
81+
self.object = object;
82+
}
83+
}

0 commit comments

Comments
 (0)