wip custom protocol

This commit is contained in:
Lucas Nogueira
2025-03-12 15:12:27 -03:00
committed by Bill Avery
parent 8018cd2ecf
commit afea11430b
4 changed files with 286 additions and 186 deletions

View File

@@ -8,17 +8,31 @@ use std::{
},
};
use tauri_runtime::{
webview::UriSchemeProtocol,
window::{PendingWindow, WindowId},
RunEvent, UserEvent,
};
use crate::{AppWindow, CefRuntime, Message};
mod request_handler;
mod utils;
#[macro_export]
macro_rules! cef_object {
($struct: ident<T: UserEvent>, $inner: ty, $cef_obj: ident, $wrap_trait: ty) => {
($struct: ident<T: UserEvent>, $context: ty, $cef_type: ty, $cef_obj: ident, $wrap_trait: ty) => {
pub struct $struct<T: UserEvent> {
object: *mut RcImpl<cef_dll_sys::$cef_obj, Self>,
inner: $inner,
context: $context,
}
impl<T: UserEvent> $struct<T> {
pub fn new(context: $context) -> $cef_type {
<$cef_type>::new(Self {
object: std::ptr::null_mut(),
context,
})
}
}
impl<T: UserEvent> Rc for $struct<T> {
@@ -46,15 +60,24 @@ macro_rules! cef_object {
Self {
object,
inner: self.inner.clone(),
context: self.context.clone(),
}
}
}
};
($struct: ident, $inner: ty, $cef_obj: ident, $wrap_trait: ty) => {
($struct: ident, $context: ty, $cef_type: ty, $cef_obj: ident, $wrap_trait: ty) => {
pub struct $struct {
object: *mut RcImpl<cef_dll_sys::$cef_obj, Self>,
inner: $inner,
context: $context,
}
impl $struct {
pub fn new(context: $context) -> $cef_type {
<$cef_type>::new(Self {
object: std::ptr::null_mut(),
context,
})
}
}
impl Rc for $struct {
@@ -82,7 +105,7 @@ macro_rules! cef_object {
Self {
object,
inner: self.inner.clone(),
context: self.context.clone(),
}
}
}
@@ -117,16 +140,7 @@ impl<T: UserEvent> Context<T> {
}
}
cef_object!(TauriApp<T: UserEvent>, Context<T>, _cef_app_t, WrapApp);
impl<T: UserEvent> TauriApp<T> {
pub fn new(context: Context<T>) -> App {
App::new(Self {
object: std::ptr::null_mut(),
inner: context,
})
}
}
cef_object!(TauriApp<T: UserEvent>, Context<T>, App, _cef_app_t, WrapApp);
impl<T: UserEvent> ImplApp for TauriApp<T> {
fn get_raw(&self) -> *mut cef_dll_sys::_cef_app_t {
@@ -134,20 +148,11 @@ impl<T: UserEvent> ImplApp for TauriApp<T> {
}
fn get_browser_process_handler(&self) -> Option<BrowserProcessHandler> {
Some(AppBrowserProcessHandler::new(self.inner.clone()))
Some(AppBrowserProcessHandler::new(self.context.clone()))
}
}
cef_object!(AppBrowserProcessHandler<T: UserEvent>, Context<T>, cef_browser_process_handler_t, WrapBrowserProcessHandler);
impl<T: UserEvent> AppBrowserProcessHandler<T> {
pub fn new(context: Context<T>) -> BrowserProcessHandler {
BrowserProcessHandler::new(Self {
object: std::ptr::null_mut(),
inner: context,
})
}
}
cef_object!(AppBrowserProcessHandler<T: UserEvent>, Context<T>, BrowserProcessHandler, cef_browser_process_handler_t, WrapBrowserProcessHandler);
impl<T: UserEvent> ImplBrowserProcessHandler for AppBrowserProcessHandler<T> {
fn get_raw(&self) -> *mut cef_dll_sys::_cef_browser_process_handler_t {
@@ -157,117 +162,11 @@ impl<T: UserEvent> ImplBrowserProcessHandler for AppBrowserProcessHandler<T> {
// The real lifespan of cef starts from `on_context_initialized`, so all the cef objects should be manipulated after that.
fn on_context_initialized(&self) {
println!("cef context initialized");
(self.inner.callback.borrow_mut())(RunEvent::Ready);
}
fn get_default_request_context_handler(&self) -> Option<RequestContextHandler> {
Some(WebRequestContextHandler::new())
}
fn get_default_client(&self) -> Option<Client> {
None
//Some(BrowserClient::new())
(self.context.callback.borrow_mut())(RunEvent::Ready);
}
}
cef_object!(
WebRequestContextHandler,
(),
_cef_request_context_handler_t,
WrapRequestContextHandler
);
impl WebRequestContextHandler {
fn new() -> RequestContextHandler {
RequestContextHandler::new(Self {
object: std::ptr::null_mut(),
inner: (),
})
}
}
impl ImplRequestContextHandler for WebRequestContextHandler {
fn get_raw(&self) -> *mut cef_dll_sys::_cef_request_context_handler_t {
self.object.cast()
}
fn get_resource_request_handler(
&self,
browser: Option<&mut impl ImplBrowser>,
frame: Option<&mut impl ImplFrame>,
request: Option<&mut impl ImplRequest>,
is_navigation: ::std::os::raw::c_int,
is_download: ::std::os::raw::c_int,
request_initiator: Option<&CefStringUtf16>,
disable_default_handling: Option<&mut ::std::os::raw::c_int>,
) -> Option<ResourceRequestHandler> {
Some(WebResourceRequestHandler::new())
}
}
cef_object!(
WebResourceRequestHandler,
(),
_cef_resource_request_handler_t,
WrapResourceRequestHandler
);
impl WebResourceRequestHandler {
fn new() -> ResourceRequestHandler {
ResourceRequestHandler::new(Self {
object: std::ptr::null_mut(),
inner: (),
})
}
}
impl ImplResourceRequestHandler for WebResourceRequestHandler {
fn get_resource_handler(
&self,
browser: Option<&mut impl ImplBrowser>,
frame: Option<&mut impl ImplFrame>,
request: Option<&mut impl ImplRequest>,
) -> Option<ResourceHandler> {
println!(
"get_resource_handler {:?}",
request
.as_ref()
.map(|r| r.get_url().map(|s| CefStringUtf8::from(&s).to_string()))
);
None
}
fn on_before_resource_load(
&self,
browser: Option<&mut impl ImplBrowser>,
frame: Option<&mut impl ImplFrame>,
request: Option<&mut impl ImplRequest>,
callback: Option<&mut impl ImplCallback>,
) -> ReturnValue {
println!(
"on_before_resource_load {:?}",
request
.as_ref()
.map(|r| r.get_url().map(|s| CefStringUtf8::from(&s).to_string()))
);
Default::default()
}
fn get_raw(&self) -> *mut cef_dll_sys::_cef_resource_request_handler_t {
self.object.cast()
}
}
cef_object!(BrowserClient, (), _cef_client_t, WrapClient);
impl BrowserClient {
pub fn new() -> Client {
Client::new(Self {
object: std::ptr::null_mut(),
inner: (),
})
}
}
cef_object!(BrowserClient, (), Client, _cef_client_t, WrapClient);
impl ImplClient for BrowserClient {
fn get_raw(&self) -> *mut cef_dll_sys::_cef_client_t {
@@ -275,42 +174,7 @@ impl ImplClient for BrowserClient {
}
fn get_request_handler(&self) -> Option<RequestHandler> {
Some(WebRequestHandler::new())
}
}
cef_object!(
WebRequestHandler,
(),
_cef_request_handler_t,
WrapRequestHandler
);
impl WebRequestHandler {
fn new() -> RequestHandler {
RequestHandler::new(Self {
object: std::ptr::null_mut(),
inner: (),
})
}
}
impl ImplRequestHandler for WebRequestHandler {
fn get_raw(&self) -> *mut cef_dll_sys::_cef_request_handler_t {
self.object.cast()
}
fn get_resource_request_handler(
&self,
browser: Option<&mut impl ImplBrowser>,
frame: Option<&mut impl ImplFrame>,
request: Option<&mut impl ImplRequest>,
is_navigation: ::std::os::raw::c_int,
is_download: ::std::os::raw::c_int,
request_initiator: Option<&CefStringUtf16>,
disable_default_handling: Option<&mut ::std::os::raw::c_int>,
) -> Option<ResourceRequestHandler> {
Some(WebResourceRequestHandler::new())
Some(request_handler::WebRequestHandler::new(()))
}
}
@@ -487,19 +351,37 @@ fn create_window<T: UserEvent>(
) {
let label = pending.label.clone();
let mut client = BrowserClient::new();
let url = pending
.webview
.as_ref()
.map(|w| w.url.as_str())
.map(|url| CefString::from(&CefStringUtf8::from(url)));
let webview = pending.webview.unwrap();
let mut client = BrowserClient::new(());
let url = CefString::from(&CefStringUtf8::from(webview.url.as_str()));
let mut request_context = request_context_create_context(
Some(&RequestContextSettings::default()),
Option::<&mut RequestContextHandler>::None,
);
if let Some(request_context) = &request_context {
for (scheme, handler) in webview.uri_scheme_protocols {
request_context.register_scheme_handler_factory(
Some(&scheme.as_str().into()),
None,
Some(&mut request_handler::UriSchemeHandlerFactory::new(
request_handler::UriSchemeContext {
handler: Arc::new(handler) as Arc<UriSchemeProtocol>,
},
)),
);
}
} else {
eprintln!("failed to create context");
}
let browser_view = browser_view_create(
Some(&mut client),
url.as_ref(),
Some(&url),
Some(&Default::default()),
Option::<&mut DictionaryValue>::None,
Option::<&mut RequestContext>::None,
request_context.as_mut(),
Option::<&mut BrowserViewDelegate>::None,
)
.expect("Failed to create browser view");

View File

@@ -0,0 +1,202 @@
use std::sync::Arc;
use cef::{rc::*, *};
use tauri_runtime::webview::UriSchemeProtocol;
use url::Url;
use crate::{cef_impl::utils::utf16_string_to_utf8, cef_object};
cef_object!(
WebResourceRequestHandler,
(),
ResourceRequestHandler,
_cef_resource_request_handler_t,
WrapResourceRequestHandler
);
impl ImplResourceRequestHandler for WebResourceRequestHandler {
fn get_resource_handler(
&self,
browser: Option<&mut impl ImplBrowser>,
frame: Option<&mut impl ImplFrame>,
request: Option<&mut impl ImplRequest>,
) -> Option<ResourceHandler> {
None
}
fn on_resource_response(
&self,
browser: Option<&mut impl ImplBrowser>,
frame: Option<&mut impl ImplFrame>,
request: Option<&mut impl ImplRequest>,
response: Option<&mut impl ImplResponse>,
) -> ::std::os::raw::c_int {
Default::default()
}
fn on_before_resource_load(
&self,
browser: Option<&mut impl ImplBrowser>,
frame: Option<&mut impl ImplFrame>,
request: Option<&mut impl ImplRequest>,
callback: Option<&mut impl ImplCallback>,
) -> ReturnValue {
cef_dll_sys::cef_return_value_t::RV_CONTINUE.into()
}
fn get_raw(&self) -> *mut cef_dll_sys::_cef_resource_request_handler_t {
self.object.cast()
}
}
cef_object!(
WebRequestHandler,
(),
RequestHandler,
_cef_request_handler_t,
WrapRequestHandler
);
impl ImplRequestHandler for WebRequestHandler {
fn get_raw(&self) -> *mut cef_dll_sys::_cef_request_handler_t {
self.object.cast()
}
fn get_resource_request_handler(
&self,
browser: Option<&mut impl ImplBrowser>,
frame: Option<&mut impl ImplFrame>,
request: Option<&mut impl ImplRequest>,
is_navigation: ::std::os::raw::c_int,
is_download: ::std::os::raw::c_int,
request_initiator: Option<&CefStringUtf16>,
disable_default_handling: Option<&mut ::std::os::raw::c_int>,
) -> Option<ResourceRequestHandler> {
Some(WebResourceRequestHandler::new(self.context.clone()))
}
}
cef_object!(
WebResourceHandler,
UriSchemeContext,
ResourceHandler,
_cef_resource_handler_t,
WrapResourceHandler
);
impl ImplResourceHandler for WebResourceHandler {
fn get_raw(&self) -> *mut cef_dll_sys::_cef_resource_handler_t {
self.object.cast()
}
fn process_request(
&self,
request: Option<&mut impl ImplRequest>,
callback: Option<&mut impl ImplCallback>,
) -> ::std::os::raw::c_int {
let Some(request) = request else { return 0 };
let Some(callback) = callback else { return 0 };
let url = request
.get_url()
.map(utf16_string_to_utf8)
.map(|url| url.to_string())
.and_then(|url| Url::parse(&url).ok());
println!("{:?}", url.as_ref().map(ToString::to_string));
//callback.cont();
//return 1;
if let Some(url) = url {
// keep the callback around
unsafe {
callback.add_ref();
}
let callback = ThreadSafe(callback.get_raw());
std::thread::spawn(move || {
std::thread::sleep_ms(5);
let cb = callback.into_owned();
unsafe {
(*cb).cont.inspect(|f| {
f(cb);
});
// release after use
(*cb).release();
}
});
1
} else {
0
}
}
fn read_response(
&self,
data_out: *mut u8,
bytes_to_read: ::std::os::raw::c_int,
bytes_read: Option<&mut ::std::os::raw::c_int>,
callback: Option<&mut impl ImplCallback>,
) -> ::std::os::raw::c_int {
callback.inspect(|cb| cb.cont());
bytes_read.map(|read| {
*read = 5;
});
1
}
fn get_response_headers(
&self,
response: Option<&mut impl ImplResponse>,
response_length: Option<&mut i64>,
redirect_url: Option<&mut CefStringUtf16>,
) {
let Some(response) = response else { return };
response.set_status(200);
response.set_header_by_name(Some(&"content-type".into()), Some(&"text/plain".into()), 1);
response_length.map(|length| {
*length = -1;
});
}
}
cef_object!(
UriSchemeHandlerFactory,
UriSchemeContext,
SchemeHandlerFactory,
cef_scheme_handler_factory_t,
WrapSchemeHandlerFactory
);
impl ImplSchemeHandlerFactory for UriSchemeHandlerFactory {
fn get_raw(&self) -> *mut cef_dll_sys::_cef_scheme_handler_factory_t {
self.object.cast()
}
fn create(
&self,
browser: Option<&mut impl ImplBrowser>,
frame: Option<&mut impl ImplFrame>,
scheme_name: Option<&CefStringUtf16>,
request: Option<&mut impl ImplRequest>,
) -> Option<ResourceHandler> {
Some(WebResourceHandler::new(self.context.clone()))
}
}
#[derive(Clone)]
pub struct UriSchemeContext {
pub handler: Arc<UriSchemeProtocol>,
}
struct ThreadSafe<T>(T);
impl<T> ThreadSafe<T> {
fn into_owned(self) -> T {
self.0
}
}
unsafe impl<T> Send for ThreadSafe<T> {}
unsafe impl<T> Sync for ThreadSafe<T> {}

View File

@@ -0,0 +1,16 @@
use cef::{CefStringUtf16, CefStringUtf8};
pub fn utf16_string_to_utf8(s: CefStringUtf16) -> CefStringUtf8 {
let value: *const cef_dll_sys::_cef_string_utf16_t = (&s).into();
unsafe {
let mut cef_string = std::mem::zeroed();
if let Some((str_, length)) = value.as_ref().map(|value| (value.str_, value.length)) {
cef_dll_sys::cef_string_utf16_to_utf8(str_, length, &mut cef_string);
}
cef_string
}
.into()
}

View File

@@ -23,23 +23,23 @@ use std::{
sync::Arc,
};
type UriSchemeProtocol = dyn Fn(&str, http::Request<Vec<u8>>, Box<dyn FnOnce(http::Response<Cow<'static, [u8]>>) + Send>)
pub type UriSchemeProtocol = dyn Fn(&str, http::Request<Vec<u8>>, Box<dyn FnOnce(http::Response<Cow<'static, [u8]>>) + Send>)
+ Send
+ Sync
+ 'static;
type WebResourceRequestHandler =
pub type WebResourceRequestHandler =
dyn Fn(http::Request<Vec<u8>>, &mut http::Response<Cow<'static, [u8]>>) + Send + Sync;
type NavigationHandler = dyn Fn(&Url) -> bool + Send;
pub type NavigationHandler = dyn Fn(&Url) -> bool + Send;
type NewWindowHandler = dyn Fn(Url, NewWindowFeatures) -> NewWindowResponse + Send + Sync;
pub type NewWindowHandler = dyn Fn(Url, NewWindowFeatures) -> NewWindowResponse + Send + Sync;
type OnPageLoadHandler = dyn Fn(Url, PageLoadEvent) + Send;
pub type OnPageLoadHandler = dyn Fn(Url, PageLoadEvent) + Send;
type DocumentTitleChangedHandler = dyn Fn(String) + Send + 'static;
pub type DocumentTitleChangedHandler = dyn Fn(String) + Send + 'static;
type DownloadHandler = dyn Fn(DownloadEvent) -> bool + Send + Sync;
pub type DownloadHandler = dyn Fn(DownloadEvent) -> bool + Send + Sync;
#[cfg(target_os = "ios")]
type InputAccessoryViewBuilderFn = dyn Fn(&objc2_ui_kit::UIView) -> Option<objc2::rc::Retained<objc2_ui_kit::UIView>>