diff --git a/crates/tauri-runtime-cef/src/cef_impl.rs b/crates/tauri-runtime-cef/src/cef_impl.rs index f901d2288..e526722b2 100644 --- a/crates/tauri-runtime-cef/src/cef_impl.rs +++ b/crates/tauri-runtime-cef/src/cef_impl.rs @@ -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, $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, - 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 { @@ -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, - 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 Context { } } -cef_object!(TauriApp, Context, _cef_app_t, WrapApp); - -impl TauriApp { - pub fn new(context: Context) -> App { - App::new(Self { - object: std::ptr::null_mut(), - inner: context, - }) - } -} +cef_object!(TauriApp, Context, App, _cef_app_t, WrapApp); impl ImplApp for TauriApp { fn get_raw(&self) -> *mut cef_dll_sys::_cef_app_t { @@ -134,20 +148,11 @@ impl ImplApp for TauriApp { } fn get_browser_process_handler(&self) -> Option { - Some(AppBrowserProcessHandler::new(self.inner.clone())) + Some(AppBrowserProcessHandler::new(self.context.clone())) } } -cef_object!(AppBrowserProcessHandler, Context, cef_browser_process_handler_t, WrapBrowserProcessHandler); - -impl AppBrowserProcessHandler { - pub fn new(context: Context) -> BrowserProcessHandler { - BrowserProcessHandler::new(Self { - object: std::ptr::null_mut(), - inner: context, - }) - } -} +cef_object!(AppBrowserProcessHandler, Context, BrowserProcessHandler, cef_browser_process_handler_t, WrapBrowserProcessHandler); impl ImplBrowserProcessHandler for AppBrowserProcessHandler { fn get_raw(&self) -> *mut cef_dll_sys::_cef_browser_process_handler_t { @@ -157,117 +162,11 @@ impl ImplBrowserProcessHandler for AppBrowserProcessHandler { // 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 { - Some(WebRequestContextHandler::new()) - } - - fn get_default_client(&self) -> Option { - 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 { - 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 { - 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 { - 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 { - Some(WebResourceRequestHandler::new()) + Some(request_handler::WebRequestHandler::new(())) } } @@ -487,19 +351,37 @@ fn create_window( ) { 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, + }, + )), + ); + } + } 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"); diff --git a/crates/tauri-runtime-cef/src/cef_impl/request_handler.rs b/crates/tauri-runtime-cef/src/cef_impl/request_handler.rs new file mode 100644 index 000000000..afdd1fde3 --- /dev/null +++ b/crates/tauri-runtime-cef/src/cef_impl/request_handler.rs @@ -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 { + 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 { + 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 { + Some(WebResourceHandler::new(self.context.clone())) + } +} + +#[derive(Clone)] +pub struct UriSchemeContext { + pub handler: Arc, +} + +struct ThreadSafe(T); + +impl ThreadSafe { + fn into_owned(self) -> T { + self.0 + } +} + +unsafe impl Send for ThreadSafe {} +unsafe impl Sync for ThreadSafe {} diff --git a/crates/tauri-runtime-cef/src/cef_impl/utils.rs b/crates/tauri-runtime-cef/src/cef_impl/utils.rs new file mode 100644 index 000000000..f81d3048e --- /dev/null +++ b/crates/tauri-runtime-cef/src/cef_impl/utils.rs @@ -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() +} diff --git a/crates/tauri-runtime/src/webview.rs b/crates/tauri-runtime/src/webview.rs index f9c904ef9..f1af8cb93 100644 --- a/crates/tauri-runtime/src/webview.rs +++ b/crates/tauri-runtime/src/webview.rs @@ -23,23 +23,23 @@ use std::{ sync::Arc, }; -type UriSchemeProtocol = dyn Fn(&str, http::Request>, Box>) + Send>) +pub type UriSchemeProtocol = dyn Fn(&str, http::Request>, Box>) + Send>) + Send + Sync + 'static; -type WebResourceRequestHandler = +pub type WebResourceRequestHandler = dyn Fn(http::Request>, &mut http::Response>) + 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>