diff --git a/Cargo.lock b/Cargo.lock index 1725eaf66..c9630f873 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -352,7 +352,7 @@ checksum = "844e00dc1e665b3cf0bba745aa9c6464292ca512db0c11384511586701eb0335" dependencies = [ "base64 0.21.7", "bcder", - "bzip2", + "bzip2 0.4.4", "chrono", "cryptographic-message-syntax", "digest", @@ -998,6 +998,15 @@ dependencies = [ "libc", ] +[[package]] +name = "bzip2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bea8dcd42434048e4f7a304411d9273a411f647446c1234a65ce0554923f4cff" +dependencies = [ + "libbz2-rs-sys", +] + [[package]] name = "bzip2-sys" version = "0.1.11+1.0.8" @@ -1161,6 +1170,28 @@ dependencies = [ "shlex", ] +[[package]] +name = "cef" +version = "133.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1611266fdfe9be23af3a20a9f7a1c62c706ec6562d1cf7eed7b4d9d6ea91da5" +dependencies = [ + "cef-dll-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "cef-dll-sys" +version = "133.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f310a1b6b61ff954dac822dc23798446cbd62c13e89b9b1a9f6acd7f0d849cdb" +dependencies = [ + "anyhow", + "cmake", + "download-cef", + "serde_json", +] + [[package]] name = "cesu8" version = "1.1.0" @@ -1300,6 +1331,15 @@ dependencies = [ "digest", ] +[[package]] +name = "cmake" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +dependencies = [ + "cc", +] + [[package]] name = "color_quant" version = "1.1.0" @@ -1319,7 +1359,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" dependencies = [ "lazy_static", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -1408,6 +1448,7 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" dependencies = [ + "percent-encoding", "time", "version_check", ] @@ -1421,6 +1462,24 @@ dependencies = [ "futures", ] +[[package]] +name = "cookie_store" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eac901828f88a5241ee0600950ab981148a18f2f756900ffba1b125ca6a3ef9" +dependencies = [ + "cookie", + "document-features", + "idna", + "indexmap 2.7.0", + "log", + "serde", + "serde_derive", + "serde_json", + "time", + "url", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -2021,6 +2080,33 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +[[package]] +name = "document-features" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d" +dependencies = [ + "litrs", +] + +[[package]] +name = "download-cef" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8aefa3312e0a3384eecc32a707b1b81c2e96f19dd1de5372d2bf09b14084179" +dependencies = [ + "bzip2 0.6.0", + "indicatif", + "regex", + "semver", + "serde", + "serde_json", + "sha1_smol", + "tar", + "thiserror 2.0.12", + "ureq", +] + [[package]] name = "dpi" version = "0.1.1" @@ -3723,6 +3809,19 @@ dependencies = [ "serde", ] +[[package]] +name = "indicatif" +version = "0.17.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235" +dependencies = [ + "console", + "number_prefix", + "portable-atomic", + "unicode-width 0.2.0", + "web-time", +] + [[package]] name = "infer" version = "0.19.0" @@ -4278,6 +4377,12 @@ dependencies = [ "once_cell", ] +[[package]] +name = "libbz2-rs-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7" + [[package]] name = "libc" version = "0.2.169" @@ -4311,7 +4416,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -4389,6 +4494,12 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" +[[package]] +name = "litrs" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed" + [[package]] name = "local-ip-address" version = "0.6.3" @@ -5022,6 +5133,12 @@ dependencies = [ "libc", ] +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + [[package]] name = "objc-sys" version = "0.3.5" @@ -6116,6 +6233,12 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "portable-atomic" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" + [[package]] name = "powerfmt" version = "0.2.0" @@ -6992,7 +7115,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1630639f4dbc1c71ad7b704cda2171584c80735c502efae94804d02763fc6f7d" dependencies = [ "bitflags 2.7.0", - "bzip2", + "bzip2 0.4.4", "chrono", "cpio", "digest", @@ -8528,6 +8651,7 @@ dependencies = [ "tauri-build", "tauri-macros", "tauri-runtime", + "tauri-runtime-cef", "tauri-runtime-wry", "tauri-utils", "thiserror 2.0.12", @@ -8873,6 +8997,19 @@ dependencies = [ "windows 0.61.1", ] +[[package]] +name = "tauri-runtime-cef" +version = "0.1.0" +dependencies = [ + "cef", + "cef-dll-sys", + "gtk", + "raw-window-handle", + "tauri-runtime", + "tauri-utils", + "url", +] + [[package]] name = "tauri-runtime-wry" version = "2.8.1" @@ -9774,6 +9911,7 @@ checksum = "217751151c53226090391713e533d9a5e904ba2570dabaaace29032687589c3e" dependencies = [ "base64 0.22.1", "cc", + "cookie_store", "der", "flate2", "log", @@ -9783,6 +9921,8 @@ dependencies = [ "rustls-pemfile 2.2.0", "rustls-pki-types", "rustls-platform-verifier", + "serde", + "serde_json", "socks", "ureq-proto", "utf-8", @@ -10314,7 +10454,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 7e5af58c1..6ebb6e1b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ members = [ "crates/tauri", "crates/tauri-runtime", "crates/tauri-runtime-wry", + "crates/tauri-runtime-cef", "crates/tauri-macros", "crates/tauri-utils", "crates/tauri-build", diff --git a/crates/tauri-runtime-cef/Cargo.toml b/crates/tauri-runtime-cef/Cargo.toml new file mode 100644 index 000000000..e115407b8 --- /dev/null +++ b/crates/tauri-runtime-cef/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "tauri-runtime-cef" +version = "0.1.0" +authors.workspace = true +homepage.workspace = true +repository.workspace = true +categories.workspace = true +license.workspace = true +edition.workspace = true +rust-version.workspace = true + +[dependencies] +tauri-runtime = { version = "2.4.0", path = "../tauri-runtime" } +tauri-utils = { version = "2.2.0", path = "../tauri-utils" } +raw-window-handle = "0.6" +url = "2" +cef = "133.4.3" +cef-dll-sys = "133.4.3" + +[target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies] +gtk = { version = "0.18", features = ["v3_24"] } + +[features] +devtools = [] +macos-private-api = [] diff --git a/crates/tauri-runtime-cef/src/cef_impl.rs b/crates/tauri-runtime-cef/src/cef_impl.rs new file mode 100644 index 000000000..3a9378715 --- /dev/null +++ b/crates/tauri-runtime-cef/src/cef_impl.rs @@ -0,0 +1,390 @@ +use cef::{rc::*, *}; +use std::{ + cell::RefCell, + collections::HashMap, + sync::{ + atomic::{AtomicU32, Ordering}, + Arc, + }, +}; +use tauri_runtime::{ + window::{PendingWindow, WindowId}, + RunEvent, UserEvent, +}; + +use crate::{AppWindow, CefRuntime, Message}; + +#[derive(Clone)] +pub struct Context { + pub windows: Arc>>, + pub callback: Arc)>>>, + pub next_window_id: Arc, + pub next_webview_id: Arc, + pub next_window_event_id: Arc, + pub next_webview_event_id: Arc, +} + +impl Context { + pub fn next_window_id(&self) -> WindowId { + self.next_window_id.fetch_add(1, Ordering::Relaxed).into() + } + + pub fn next_webview_id(&self) -> u32 { + self.next_webview_id.fetch_add(1, Ordering::Relaxed) + } + + pub fn next_window_event_id(&self) -> u32 { + self.next_window_event_id.fetch_add(1, Ordering::Relaxed) + } + + pub fn next_webview_event_id(&self) -> u32 { + self.next_webview_event_id.fetch_add(1, Ordering::Relaxed) + } +} + +pub struct TauriApp { + object: *mut RcImpl, + context: Context, +} + +impl TauriApp { + pub fn new(context: Context) -> App { + App::new(Self { + object: std::ptr::null_mut(), + context, + }) + } +} + +impl WrapApp for TauriApp { + fn wrap_rc(&mut self, object: *mut RcImpl) { + self.object = object; + } +} + +impl Clone for TauriApp { + fn clone(&self) -> Self { + let object = unsafe { + let rc_impl = &mut *self.object; + rc_impl.interface.add_ref(); + self.object + }; + let context = self.context.clone(); + + Self { object, context } + } +} + +impl Rc for TauriApp { + fn as_base(&self) -> &cef_dll_sys::cef_base_ref_counted_t { + unsafe { + let base = &*self.object; + std::mem::transmute(&base.cef_object) + } + } +} + +impl ImplApp for TauriApp { + fn get_raw(&self) -> *mut cef_dll_sys::_cef_app_t { + self.object as *mut cef_dll_sys::_cef_app_t + } + + fn get_browser_process_handler(&self) -> Option { + Some(AppBrowserProcessHandler::new(self.context.clone())) + } +} + +struct AppBrowserProcessHandler { + object: *mut RcImpl, + context: Context, +} + +impl AppBrowserProcessHandler { + pub fn new(context: Context) -> BrowserProcessHandler { + BrowserProcessHandler::new(Self { + object: std::ptr::null_mut(), + context, + }) + } +} + +impl Rc for AppBrowserProcessHandler { + fn as_base(&self) -> &cef_dll_sys::cef_base_ref_counted_t { + unsafe { + let base = &*self.object; + std::mem::transmute(&base.cef_object) + } + } +} + +impl WrapBrowserProcessHandler for AppBrowserProcessHandler { + fn wrap_rc(&mut self, object: *mut RcImpl) { + self.object = object; + } +} + +impl Clone for AppBrowserProcessHandler { + fn clone(&self) -> Self { + let object = unsafe { + let rc_impl = &mut *self.object; + rc_impl.interface.add_ref(); + rc_impl + }; + + let context = self.context.clone(); + + Self { object, context } + } +} + +impl ImplBrowserProcessHandler for AppBrowserProcessHandler { + fn get_raw(&self) -> *mut cef_dll_sys::_cef_browser_process_handler_t { + self.object.cast() + } + + // 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.context.callback.borrow_mut())(RunEvent::Ready); + } +} + +struct BrowserClient(*mut RcImpl); + +impl BrowserClient { + pub fn new() -> Client { + Client::new(Self(std::ptr::null_mut())) + } +} + +impl WrapClient for BrowserClient { + fn wrap_rc(&mut self, object: *mut RcImpl) { + self.0 = object; + } +} + +impl Clone for BrowserClient { + fn clone(&self) -> Self { + unsafe { + let rc_impl = &mut *self.0; + rc_impl.interface.add_ref(); + } + + Self(self.0) + } +} + +impl Rc for BrowserClient { + fn as_base(&self) -> &cef_dll_sys::cef_base_ref_counted_t { + unsafe { + let base = &*self.0; + std::mem::transmute(&base.cef_object) + } + } +} + +impl ImplClient for BrowserClient { + fn get_raw(&self) -> *mut cef_dll_sys::_cef_client_t { + self.0 as *mut cef_dll_sys::_cef_client_t + } +} + +struct AppWindowDelegate { + base: *mut RcImpl, + browser_view: BrowserView, +} + +impl AppWindowDelegate { + pub fn new(browser_view: BrowserView) -> WindowDelegate { + WindowDelegate::new(Self { + base: std::ptr::null_mut(), + browser_view, + }) + } +} + +impl WrapWindowDelegate for AppWindowDelegate { + fn wrap_rc(&mut self, object: *mut RcImpl) { + self.base = object; + } +} + +impl Clone for AppWindowDelegate { + fn clone(&self) -> Self { + unsafe { + let rc_impl = &mut *self.base; + rc_impl.interface.add_ref(); + } + + Self { + base: self.base, + browser_view: self.browser_view.clone(), + } + } +} + +impl Rc for AppWindowDelegate { + fn as_base(&self) -> &cef_dll_sys::cef_base_ref_counted_t { + unsafe { + let base = &*self.base; + std::mem::transmute(&base.cef_object) + } + } +} + +impl ImplViewDelegate for AppWindowDelegate { + fn on_child_view_changed( + &self, + _view: Option<&mut impl ImplView>, + _added: ::std::os::raw::c_int, + _child: Option<&mut impl ImplView>, + ) { + // view.as_panel().map(|x| x.as_window().map(|w| w.close())); + } + + fn get_raw(&self) -> *mut cef_dll_sys::_cef_view_delegate_t { + self.base as *mut cef_dll_sys::_cef_view_delegate_t + } +} + +impl ImplPanelDelegate for AppWindowDelegate {} + +impl ImplWindowDelegate for AppWindowDelegate { + fn on_window_created(&self, window: Option<&mut impl ImplWindow>) { + if let Some(window) = window { + let mut view = self.browser_view.clone(); + window.add_child_view(Some(&mut view)); + window.show(); + } + } + + fn on_window_destroyed(&self, _window: Option<&mut impl ImplWindow>) { + quit_message_loop(); + } + + fn with_standard_window_buttons( + &self, + _window: Option<&mut impl ImplWindow>, + ) -> ::std::os::raw::c_int { + 1 + } + + fn can_resize(&self, _window: Option<&mut impl ImplWindow>) -> ::std::os::raw::c_int { + 1 + } + + fn can_maximize(&self, _window: Option<&mut impl ImplWindow>) -> ::std::os::raw::c_int { + 1 + } + + fn can_minimize(&self, _window: Option<&mut impl ImplWindow>) -> ::std::os::raw::c_int { + 1 + } + + fn can_close(&self, _window: Option<&mut impl ImplWindow>) -> ::std::os::raw::c_int { + 1 + } +} + +pub struct SendMessageTask { + context: Context, + message: Arc>>, + object: *mut RcImpl, +} + +impl SendMessageTask { + pub fn new(context: Context, message: Message) -> Task { + Task::new(Self { + context, + message: Arc::new(RefCell::new(message)), + object: std::ptr::null_mut(), + }) + } +} + +impl Rc for SendMessageTask { + fn as_base(&self) -> &cef_dll_sys::cef_base_ref_counted_t { + unsafe { + let base = &*self.object; + std::mem::transmute(&base.cef_object) + } + } +} + +impl Clone for SendMessageTask { + fn clone(&self) -> Self { + let object = unsafe { + let rc_impl = &mut *self.object; + rc_impl.interface.add_ref(); + self.object + }; + Self { + context: self.context.clone(), + message: self.message.clone(), + object, + } + } +} + +impl WrapTask for SendMessageTask { + fn wrap_rc(&mut self, object: *mut RcImpl) { + self.object = object; + } +} + +impl ImplTask for SendMessageTask { + fn execute(&self) { + match self.message.replace(Message::Noop) { + Message::CreateWindow { + window_id, + webview_id, + pending, + after_window_creation: _todo, + } => create_window(&self.context, window_id, webview_id, pending), + Message::Task(t) => t(), + Message::UserEvent(evt) => { + (self.context.callback.borrow_mut())(RunEvent::UserEvent(evt)); + } + Message::Noop => {} + } + } + + fn get_raw(&self) -> *mut cef_dll_sys::_cef_task_t { + unsafe { &mut (&mut *self.object).cef_object } + } +} + +fn create_window( + context: &Context, + window_id: WindowId, + webview_id: u32, + pending: PendingWindow>, +) { + 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 browser_view = browser_view_create( + Some(&mut client), + url.as_ref(), + Some(&Default::default()), + Option::<&mut DictionaryValue>::None, + Option::<&mut RequestContext>::None, + Option::<&mut BrowserViewDelegate>::None, + ) + .expect("Failed to create browser view"); + + let mut delegate = AppWindowDelegate::new(browser_view); + + let window = window_create_top_level(Some(&mut delegate)).expect("Failed to create window"); + + context + .windows + .borrow_mut() + .insert(window_id, AppWindow { label, window }); +} diff --git a/crates/tauri-runtime-cef/src/lib.rs b/crates/tauri-runtime-cef/src/lib.rs new file mode 100644 index 000000000..dc6890ec8 --- /dev/null +++ b/crates/tauri-runtime-cef/src/lib.rs @@ -0,0 +1,1166 @@ +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +#![allow(dead_code, unused_variables)] + +use cef::{rc::Rc, ImplTaskRunner}; +use tauri_runtime::{ + dpi::{PhysicalPosition, PhysicalSize, Position, Size}, + monitor::Monitor, + webview::{DetachedWebview, PendingWebview}, + window::{ + CursorIcon, DetachedWindow, DetachedWindowWebview, PendingWindow, RawWindow, WindowBuilder, + WindowBuilderBase, WindowEvent, WindowId, + }, + DeviceEventFilter, EventLoopProxy, Icon, ProgressBarState, Result, RunEvent, Runtime, + RuntimeHandle, RuntimeInitArgs, UserAttentionType, UserEvent, WebviewDispatch, WindowDispatch, + WindowEventId, +}; + +#[cfg(target_os = "macos")] +use tauri_utils::TitleBarStyle; +use tauri_utils::{config::WindowConfig, Theme}; +use url::Url; + +#[cfg(windows)] +use windows::Win32::Foundation::HWND; + +use std::{ + cell::RefCell, + collections::HashMap, + fmt, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, Mutex, + }, +}; + +mod cef_impl; + +type ShortcutMap = HashMap>; + +enum Message { + Task(Box), + CreateWindow { + window_id: WindowId, + webview_id: u32, + pending: PendingWindow>, + after_window_creation: Option>, + }, + UserEvent(T), + Noop, +} + +impl Clone for Message { + fn clone(&self) -> Self { + match self { + Self::UserEvent(t) => Self::UserEvent(t.clone()), + _ => unimplemented!(), + } + } +} + +struct AppWindow { + label: String, + window: cef::Window, +} + +#[derive(Clone)] +pub struct RuntimeContext { + is_running: Arc, + windows: Arc>>, + main_thread_task_runner: cef::TaskRunner, + cef_context: cef_impl::Context, + event_queue: Arc>>>, +} + +// SAFETY: we ensure this type is only used on the main thread. +#[allow(clippy::non_send_fields_in_send_ty)] +unsafe impl Send for RuntimeContext {} + +// SAFETY: we ensure this type is only used on the main thread. +#[allow(clippy::non_send_fields_in_send_ty)] +unsafe impl Sync for RuntimeContext {} + +impl RuntimeContext { + fn post_message(&self, message: Message) -> Result<()> { + self + .main_thread_task_runner + .post_task(Some(&mut cef_impl::SendMessageTask::new( + self.cef_context.clone(), + message, + ))); + Ok(()) + } + + fn create_window( + &self, + pending: PendingWindow>, + after_window_creation: Option, + ) -> Result>> { + let label = pending.label.clone(); + let context = self.clone(); + let window_id = self.cef_context.next_window_id(); + let (webview_id, use_https_scheme) = pending + .webview + .as_ref() + .map(|w| { + ( + Some(context.cef_context.next_webview_id()), + w.webview_attributes.use_https_scheme, + ) + }) + .unwrap_or((None, false)); + + self.post_message(Message::CreateWindow { + window_id, + webview_id: webview_id.unwrap_or_default(), + pending, + after_window_creation: after_window_creation + .map(|f| Box::new(f) as Box), + })?; + + let dispatcher = CefWindowDispatcher { + window_id, + context: self.clone(), + }; + + let detached_webview = webview_id.map(|id| { + let webview = DetachedWebview { + label: label.clone(), + dispatcher: CefWebviewDispatcher { + window_id: Arc::new(Mutex::new(window_id)), + webview_id: id, + context: self.clone(), + }, + }; + DetachedWindowWebview { + webview, + use_https_scheme, + } + }); + + Ok(DetachedWindow { + id: window_id, + label, + dispatcher, + webview: detached_webview, + }) + } +} + +impl fmt::Debug for RuntimeContext { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RuntimeContext").finish() + } +} + +#[derive(Debug, Clone)] +pub struct CefRuntimeHandle { + context: RuntimeContext, +} + +impl RuntimeHandle for CefRuntimeHandle { + type Runtime = CefRuntime; + + fn create_proxy(&self) -> >::EventLoopProxy { + EventProxy { + context: self.context.clone(), + } + } + + #[cfg(target_os = "macos")] + #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))] + fn set_activation_policy( + &self, + activation_policy: tauri_runtime::ActivationPolicy, + ) -> Result<()> { + Ok(()) + } + + fn request_exit(&self, code: i32) -> Result<()> { + unimplemented!() + } + + /// Create a new webview window. + fn create_window) + Send + 'static>( + &self, + pending: PendingWindow, + after_window_creation: Option, + ) -> Result> { + self.context.create_window(pending, after_window_creation) + } + + fn create_webview( + &self, + window_id: WindowId, + pending: PendingWebview, + ) -> Result> { + todo!() + } + + /// Run a task on the main thread. + fn run_on_main_thread(&self, f: F) -> Result<()> { + self.context.post_message(Message::Task(Box::new(f))) + } + + fn display_handle( + &self, + ) -> std::result::Result, raw_window_handle::HandleError> { + #[cfg(target_os = "linux")] + return Ok(unsafe { + raw_window_handle::DisplayHandle::borrow_raw(raw_window_handle::RawDisplayHandle::Xlib( + raw_window_handle::XlibDisplayHandle::new(None, 0), + )) + }); + #[cfg(target_os = "macos")] + return Ok(unsafe { + raw_window_handle::DisplayHandle::borrow_raw(raw_window_handle::RawDisplayHandle::AppKit( + raw_window_handle::AppKitDisplayHandle::new(), + )) + }); + #[cfg(windows)] + return Ok(unsafe { + raw_window_handle::DisplayHandle::borrow_raw(raw_window_handle::RawDisplayHandle::Windows( + raw_window_handle::WindowsDisplayHandle::new(), + )) + }); + #[cfg(not(any(target_os = "linux", target_os = "macos", windows)))] + unimplemented!(); + } + + fn primary_monitor(&self) -> Option { + unimplemented!() + } + + fn monitor_from_point(&self, x: f64, y: f64) -> Option { + unimplemented!() + } + + fn available_monitors(&self) -> Vec { + unimplemented!() + } + + fn set_theme(&self, theme: Option) { + unimplemented!() + } + + /// Shows the application, but does not automatically focus it. + #[cfg(target_os = "macos")] + fn show(&self) -> Result<()> { + Ok(()) + } + + /// Hides the application. + #[cfg(target_os = "macos")] + fn hide(&self) -> Result<()> { + Ok(()) + } + + #[cfg(target_os = "android")] + fn find_class<'a>( + &self, + env: &mut jni::JNIEnv<'a>, + activity: &jni::objects::JObject<'_>, + name: impl Into, + ) -> std::result::Result, jni::errors::Error> { + todo!() + } + + #[cfg(target_os = "android")] + fn run_on_android_context(&self, f: F) + where + F: FnOnce(&mut jni::JNIEnv, &jni::objects::JObject, &jni::objects::JObject) + Send + 'static, + { + todo!() + } + + fn cursor_position(&self) -> Result> { + Ok(PhysicalPosition::new(0.0, 0.0)) + } +} + +#[derive(Debug, Clone)] +pub struct CefWebviewDispatcher { + window_id: Arc>, + webview_id: u32, + context: RuntimeContext, +} + +#[derive(Debug, Clone)] +pub struct CefWindowDispatcher { + window_id: WindowId, + context: RuntimeContext, +} + +#[derive(Debug, Clone)] +pub struct CefWindowBuilder {} + +impl WindowBuilderBase for CefWindowBuilder {} + +impl WindowBuilder for CefWindowBuilder { + fn new() -> Self { + Self {} + } + + fn with_config(config: &WindowConfig) -> Self { + Self {} + } + + fn center(self) -> Self { + self + } + + fn position(self, x: f64, y: f64) -> Self { + self + } + + fn inner_size(self, min_width: f64, min_height: f64) -> Self { + self + } + + fn min_inner_size(self, min_width: f64, min_height: f64) -> Self { + self + } + + fn max_inner_size(self, max_width: f64, max_height: f64) -> Self { + self + } + + fn inner_size_constraints( + self, + constraints: tauri_runtime::window::WindowSizeConstraints, + ) -> Self { + self + } + + fn resizable(self, resizable: bool) -> Self { + self + } + + fn maximizable(self, resizable: bool) -> Self { + self + } + + fn minimizable(self, resizable: bool) -> Self { + self + } + + fn closable(self, resizable: bool) -> Self { + self + } + + fn title>(self, title: S) -> Self { + self + } + + fn fullscreen(self, fullscreen: bool) -> Self { + self + } + + fn focused(self, focused: bool) -> Self { + self + } + + fn maximized(self, maximized: bool) -> Self { + self + } + + fn visible(self, visible: bool) -> Self { + self + } + + #[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))] + #[cfg_attr( + docsrs, + doc(cfg(any(not(target_os = "macos"), feature = "macos-private-api"))) + )] + fn transparent(self, transparent: bool) -> Self { + self + } + + fn decorations(self, decorations: bool) -> Self { + self + } + + fn always_on_bottom(self, always_on_bottom: bool) -> Self { + self + } + + fn always_on_top(self, always_on_top: bool) -> Self { + self + } + + fn visible_on_all_workspaces(self, visible_on_all_workspaces: bool) -> Self { + self + } + + fn content_protected(self, protected: bool) -> Self { + self + } + + fn icon(self, icon: Icon<'_>) -> Result { + Ok(self) + } + + fn skip_taskbar(self, skip: bool) -> Self { + self + } + + fn window_classname>(self, classname: S) -> Self { + self + } + + fn shadow(self, enable: bool) -> Self { + self + } + + #[cfg(windows)] + fn owner(self, owner: HWND) -> Self { + self + } + + #[cfg(windows)] + fn parent(self, parent: HWND) -> Self { + self + } + + #[cfg(target_os = "macos")] + fn parent(self, parent: *mut std::ffi::c_void) -> Self { + self + } + + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + fn transient_for(self, parent: &impl gtk::glib::IsA) -> Self { + self + } + + #[cfg(windows)] + fn drag_and_drop(self, enabled: bool) -> Self { + self + } + + #[cfg(target_os = "macos")] + fn title_bar_style(self, style: TitleBarStyle) -> Self { + self + } + + #[cfg(target_os = "macos")] + fn hidden_title(self, transparent: bool) -> Self { + self + } + + #[cfg(target_os = "macos")] + fn tabbing_identifier(self, identifier: &str) -> Self { + self + } + + fn theme(self, theme: Option) -> Self { + self + } + + fn has_icon(&self) -> bool { + false + } + + fn get_theme(&self) -> Option { + None + } + + fn background_color(self, _color: tauri_utils::config::Color) -> Self { + self + } +} + +impl WebviewDispatch for CefWebviewDispatcher { + type Runtime = CefRuntime; + + fn run_on_main_thread(&self, f: F) -> Result<()> { + self.context.post_message(Message::Task(Box::new(f))) + } + + fn on_webview_event( + &self, + f: F, + ) -> tauri_runtime::WebviewEventId { + 0 + } + + fn with_webview) + Send + 'static>(&self, f: F) -> Result<()> { + Ok(()) + } + + #[cfg(any(debug_assertions, feature = "devtools"))] + fn open_devtools(&self) {} + + #[cfg(any(debug_assertions, feature = "devtools"))] + fn close_devtools(&self) {} + + #[cfg(any(debug_assertions, feature = "devtools"))] + fn is_devtools_open(&self) -> Result { + Ok(false) + } + + fn set_zoom(&self, scale_factor: f64) -> Result<()> { + Ok(()) + } + + fn eval_script>(&self, script: S) -> Result<()> { + Ok(()) + } + + fn url(&self) -> Result { + todo!() + } + + fn bounds(&self) -> Result { + Ok(tauri_runtime::Rect::default()) + } + + fn position(&self) -> Result> { + Ok(PhysicalPosition { x: 0, y: 0 }) + } + + fn size(&self) -> Result> { + Ok(PhysicalSize { + width: 0, + height: 0, + }) + } + + fn navigate(&self, url: Url) -> Result<()> { + Ok(()) + } + + fn reload(&self) -> Result<()> { + Ok(()) + } + + fn print(&self) -> Result<()> { + Ok(()) + } + + fn close(&self) -> Result<()> { + Ok(()) + } + + fn set_bounds(&self, bounds: tauri_runtime::Rect) -> Result<()> { + Ok(()) + } + + fn set_size(&self, _size: Size) -> Result<()> { + Ok(()) + } + + fn set_position(&self, _position: Position) -> Result<()> { + Ok(()) + } + + fn set_focus(&self) -> Result<()> { + Ok(()) + } + + fn reparent(&self, window_id: WindowId) -> Result<()> { + Ok(()) + } + + fn set_auto_resize(&self, auto_resize: bool) -> Result<()> { + Ok(()) + } + + fn clear_all_browsing_data(&self) -> Result<()> { + Ok(()) + } + + fn hide(&self) -> Result<()> { + Ok(()) + } + + fn show(&self) -> Result<()> { + Ok(()) + } + + fn set_background_color(&self, color: Option) -> Result<()> { + Ok(()) + } +} + +impl WindowDispatch for CefWindowDispatcher { + type Runtime = CefRuntime; + + type WindowBuilder = CefWindowBuilder; + + fn run_on_main_thread(&self, f: F) -> Result<()> { + self.context.post_message(Message::Task(Box::new(f))) + } + + fn on_window_event(&self, f: F) -> WindowEventId { + 0 + } + + fn scale_factor(&self) -> Result { + Ok(1.0) + } + + fn inner_position(&self) -> Result> { + Ok(PhysicalPosition { x: 0, y: 0 }) + } + + fn outer_position(&self) -> Result> { + Ok(PhysicalPosition { x: 0, y: 0 }) + } + + fn inner_size(&self) -> Result> { + Ok(PhysicalSize { + width: 0, + height: 0, + }) + } + + fn outer_size(&self) -> Result> { + Ok(PhysicalSize { + width: 0, + height: 0, + }) + } + + fn is_fullscreen(&self) -> Result { + Ok(false) + } + + fn is_minimized(&self) -> Result { + Ok(false) + } + + fn is_maximized(&self) -> Result { + Ok(false) + } + + fn is_focused(&self) -> Result { + Ok(false) + } + + fn is_decorated(&self) -> Result { + Ok(false) + } + + fn is_resizable(&self) -> Result { + Ok(false) + } + + fn is_maximizable(&self) -> Result { + Ok(true) + } + + fn is_minimizable(&self) -> Result { + Ok(true) + } + + fn is_closable(&self) -> Result { + Ok(true) + } + + fn is_visible(&self) -> Result { + Ok(true) + } + + fn title(&self) -> Result { + Ok(String::new()) + } + + fn current_monitor(&self) -> Result> { + Ok(None) + } + + fn primary_monitor(&self) -> Result> { + Ok(None) + } + + fn monitor_from_point(&self, x: f64, y: f64) -> Result> { + Ok(None) + } + + fn available_monitors(&self) -> Result> { + Ok(Vec::new()) + } + + fn theme(&self) -> Result { + Ok(Theme::Light) + } + + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + fn gtk_window(&self) -> Result { + unimplemented!() + } + + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + fn default_vbox(&self) -> Result { + unimplemented!() + } + + fn window_handle( + &self, + ) -> std::result::Result, raw_window_handle::HandleError> { + #[cfg(target_os = "linux")] + return unsafe { + Ok(raw_window_handle::WindowHandle::borrow_raw( + raw_window_handle::RawWindowHandle::Xlib(raw_window_handle::XlibWindowHandle::new(0)), + )) + }; + #[cfg(target_os = "macos")] + return unsafe { + Ok(raw_window_handle::WindowHandle::borrow_raw( + raw_window_handle::RawWindowHandle::AppKit(raw_window_handle::AppKitWindowHandle::new( + std::ptr::NonNull::from(&()).cast(), + )), + )) + }; + #[cfg(windows)] + return unsafe { + Ok(raw_window_handle::WindowHandle::borrow_raw( + raw_window_handle::RawWindowHandle::Win32(raw_window_handle::Win32WindowHandle::new( + std::num::NonZeroIsize::MIN, + )), + )) + }; + #[cfg(not(any(target_os = "linux", target_os = "macos", windows)))] + unimplemented!(); + } + + fn center(&self) -> Result<()> { + Ok(()) + } + + fn request_user_attention(&self, request_type: Option) -> Result<()> { + Ok(()) + } + + fn create_window) + Send + 'static>( + &mut self, + pending: PendingWindow, + after_window_creation: Option, + ) -> Result> { + self.context.create_window(pending, after_window_creation) + } + + fn create_webview( + &mut self, + pending: PendingWebview, + ) -> Result> { + todo!() + } + + fn set_resizable(&self, resizable: bool) -> Result<()> { + Ok(()) + } + + fn set_maximizable(&self, maximizable: bool) -> Result<()> { + Ok(()) + } + + fn set_minimizable(&self, minimizable: bool) -> Result<()> { + Ok(()) + } + + fn set_closable(&self, closable: bool) -> Result<()> { + Ok(()) + } + + fn set_title>(&self, title: S) -> Result<()> { + Ok(()) + } + + fn maximize(&self) -> Result<()> { + Ok(()) + } + + fn unmaximize(&self) -> Result<()> { + Ok(()) + } + + fn minimize(&self) -> Result<()> { + Ok(()) + } + + fn unminimize(&self) -> Result<()> { + Ok(()) + } + + fn show(&self) -> Result<()> { + Ok(()) + } + + fn hide(&self) -> Result<()> { + Ok(()) + } + + fn close(&self) -> Result<()> { + Ok(()) + } + + fn destroy(&self) -> Result<()> { + Ok(()) + } + + fn set_decorations(&self, decorations: bool) -> Result<()> { + Ok(()) + } + + fn set_shadow(&self, shadow: bool) -> Result<()> { + Ok(()) + } + + fn set_always_on_bottom(&self, always_on_bottom: bool) -> Result<()> { + Ok(()) + } + + fn set_always_on_top(&self, always_on_top: bool) -> Result<()> { + Ok(()) + } + + fn set_visible_on_all_workspaces(&self, visible_on_all_workspaces: bool) -> Result<()> { + Ok(()) + } + + fn set_content_protected(&self, protected: bool) -> Result<()> { + Ok(()) + } + + fn set_size(&self, size: Size) -> Result<()> { + Ok(()) + } + + fn set_min_size(&self, size: Option) -> Result<()> { + Ok(()) + } + + fn set_max_size(&self, size: Option) -> Result<()> { + Ok(()) + } + + fn set_position(&self, position: Position) -> Result<()> { + Ok(()) + } + + fn set_fullscreen(&self, fullscreen: bool) -> Result<()> { + Ok(()) + } + + fn set_focus(&self) -> Result<()> { + Ok(()) + } + + fn set_icon(&self, icon: Icon<'_>) -> Result<()> { + Ok(()) + } + + fn set_skip_taskbar(&self, skip: bool) -> Result<()> { + Ok(()) + } + + fn set_cursor_grab(&self, grab: bool) -> Result<()> { + Ok(()) + } + + fn set_cursor_visible(&self, visible: bool) -> Result<()> { + Ok(()) + } + + fn set_cursor_icon(&self, icon: CursorIcon) -> Result<()> { + Ok(()) + } + + fn set_cursor_position>(&self, position: Pos) -> Result<()> { + Ok(()) + } + + fn set_ignore_cursor_events(&self, ignore: bool) -> Result<()> { + Ok(()) + } + + fn start_dragging(&self) -> Result<()> { + Ok(()) + } + + fn start_resize_dragging(&self, direction: tauri_runtime::ResizeDirection) -> Result<()> { + Ok(()) + } + + fn set_progress_bar(&self, progress_state: ProgressBarState) -> Result<()> { + Ok(()) + } + + fn set_badge_count(&self, count: Option, desktop_filename: Option) -> Result<()> { + Ok(()) + } + + fn set_badge_label(&self, label: Option) -> Result<()> { + Ok(()) + } + + fn set_overlay_icon(&self, icon: Option>) -> Result<()> { + Ok(()) + } + + fn set_title_bar_style(&self, style: tauri_utils::TitleBarStyle) -> Result<()> { + Ok(()) + } + + fn set_size_constraints( + &self, + constraints: tauri_runtime::window::WindowSizeConstraints, + ) -> Result<()> { + Ok(()) + } + + fn set_theme(&self, theme: Option) -> Result<()> { + Ok(()) + } + + fn set_enabled(&self, enabled: bool) -> Result<()> { + Ok(()) + } + + fn is_enabled(&self) -> Result { + Ok(true) + } + + fn set_background_color(&self, color: Option) -> Result<()> { + Ok(()) + } +} + +#[derive(Clone)] +pub struct EventProxy { + context: RuntimeContext, +} + +// SAFETY: we ensure the context is only used on the main thread. +#[allow(clippy::non_send_fields_in_send_ty)] +unsafe impl Send for EventProxy {} + +// SAFETY: we ensure the context is only used on the main thread. +#[allow(clippy::non_send_fields_in_send_ty)] +unsafe impl Sync for EventProxy {} + +impl fmt::Debug for EventProxy { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("EventProxy").finish() + } +} + +impl EventLoopProxy for EventProxy { + fn send_event(&self, event: T) -> Result<()> { + self.context.post_message(Message::UserEvent(event)) + } +} + +#[derive(Debug)] +pub struct CefRuntime { + is_running: Arc, + pub context: RuntimeContext, +} + +impl CefRuntime { + fn init() -> Self { + let is_running = Arc::new(AtomicBool::new(false)); + + #[cfg(target_os = "macos")] + let _loader = { + let loader = library_loader::LibraryLoader::new(&std::env::current_exe().unwrap(), false); + assert!(loader.load()); + loader + }; + + let _ = cef::api_hash(cef::sys::CEF_API_VERSION_LAST, 0); + + let args = cef::args::Args::new(); + let cmd = args.as_cmd_line().unwrap(); + + let sandbox = cef::sandbox_info::SandboxInfo::new(); + + let event_queue = Arc::new(RefCell::new(Vec::new())); + let event_queue_ = event_queue.clone(); + + let cef_context = cef_impl::Context { + windows: Default::default(), + callback: Arc::new(RefCell::new(Box::new(move |event| { + event_queue_.borrow_mut().push(event); + }))), + next_webview_event_id: Default::default(), + next_webview_id: Default::default(), + next_window_id: Default::default(), + next_window_event_id: Default::default(), + }; + let mut app = cef_impl::TauriApp::new(cef_context.clone()); + + let ret = cef::execute_process( + Some(args.as_main_args()), + Some(&mut app), + sandbox.as_mut_ptr(), + ); + + assert!(ret == -1, "cannot execute browser process"); + + let settings = cef::Settings::default(); + assert_eq!( + cef::initialize( + Some(args.as_main_args()), + Some(&settings), + Some(&mut app), + sandbox.as_mut_ptr() + ), + 1 + ); + + let context = RuntimeContext { + is_running: is_running.clone(), + windows: Default::default(), + main_thread_task_runner: cef::task_runner_get_for_current_thread().expect("null task runner"), + cef_context, + event_queue, + }; + Self { + is_running, + context, + } + } +} + +impl Runtime for CefRuntime { + type WindowDispatcher = CefWindowDispatcher; + type WebviewDispatcher = CefWebviewDispatcher; + type Handle = CefRuntimeHandle; + type EventLoopProxy = EventProxy; + + fn new(_args: RuntimeInitArgs) -> Result { + Ok(Self::init()) + } + + #[cfg(any(windows, target_os = "linux"))] + fn new_any_thread(_args: RuntimeInitArgs) -> Result { + Ok(Self::init()) + } + + fn create_proxy(&self) -> Self::EventLoopProxy { + EventProxy { + context: self.context.clone(), + } + } + + fn handle(&self) -> Self::Handle { + CefRuntimeHandle { + context: self.context.clone(), + } + } + + fn create_window) + Send + 'static>( + &self, + pending: PendingWindow, + _after_window_creation: Option, + ) -> Result> { + todo!() + } + + fn create_webview( + &self, + window_id: WindowId, + pending: PendingWebview, + ) -> Result> { + todo!() + } + + fn primary_monitor(&self) -> Option { + unimplemented!() + } + + fn monitor_from_point(&self, x: f64, y: f64) -> Option { + unimplemented!() + } + + fn available_monitors(&self) -> Vec { + unimplemented!() + } + + fn set_theme(&self, theme: Option) { + unimplemented!() + } + + #[cfg(target_os = "macos")] + #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))] + fn set_activation_policy(&mut self, activation_policy: tauri_runtime::ActivationPolicy) {} + + #[cfg(target_os = "macos")] + #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))] + fn show(&self) {} + + #[cfg(target_os = "macos")] + #[cfg_attr(docsrs, doc(cfg(target_os = "macos")))] + fn hide(&self) {} + + fn set_device_event_filter(&mut self, filter: DeviceEventFilter) {} + + #[cfg(any( + target_os = "macos", + windows, + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + fn run_iteration)>(&mut self, callback: F) {} + + fn run) + 'static>(self, callback: F) { + self.is_running.store(true, Ordering::Relaxed); + + let callback = Arc::new(RefCell::new(callback)); + let callback_ = callback.clone(); + let _ = self + .context + .cef_context + .callback + .replace(Box::new(move |event| { + (callback_.borrow_mut())(event); + })); + + // clear event queue + for event in self.context.event_queue.replace(Vec::new()) { + (callback.borrow_mut())(event); + } + + cef::run_message_loop(); + + let windows = self.context.windows.borrow(); + for window in windows.values() { + assert!(window.window.has_one_ref()); + } + + cef::shutdown(); + + (callback.borrow_mut())(RunEvent::Exit); + } + + fn cursor_position(&self) -> Result> { + Ok(PhysicalPosition::new(0.0, 0.0)) + } +} diff --git a/crates/tauri/Cargo.toml b/crates/tauri/Cargo.toml index 7d453f8e7..d234f5cb9 100644 --- a/crates/tauri/Cargo.toml +++ b/crates/tauri/Cargo.toml @@ -62,6 +62,7 @@ tauri-utils = { version = "2.7.0", features = [ "resources", ], path = "../tauri-utils" } tauri-runtime-wry = { version = "2.8.1", path = "../tauri-runtime-wry", default-features = false, optional = true } +tauri-runtime-cef = { version = "0.1.0", path = "../tauri-runtime-cef", optional = true } getrandom = "0.3" serde_repr = "0.1" http = "1" @@ -194,6 +195,7 @@ tracing = ["dep:tracing", "tauri-macros/tracing", "tauri-runtime-wry?/tracing"] test = [] compression = ["tauri-macros/compression", "tauri-utils/compression"] wry = ["webview2-com", "webkit2gtk", "tauri-runtime-wry"] +cef = ["tauri-runtime-cef"] # TODO: Remove in v3 - wry does not have this feature anymore objc-exception = [] linux-libxdo = ["tray-icon/libxdo", "muda/libxdo"] diff --git a/crates/tauri/src/app.rs b/crates/tauri/src/app.rs index 06a3abe44..16bceca7c 100644 --- a/crates/tauri/src/app.rs +++ b/crates/tauri/src/app.rs @@ -1295,7 +1295,7 @@ impl App { let app_handle = self.handle().clone(); let manager = self.manager.clone(); - move |event| match event { + move |event| match dbg!(&event) { RuntimeRunEvent::Ready => { if let Err(e) = setup(&mut self) { panic!("Failed to setup app: {e}"); @@ -1449,8 +1449,8 @@ impl Default for Builder { } } -#[cfg(not(feature = "wry"))] -#[cfg_attr(docsrs, doc(cfg(not(feature = "wry"))))] +#[cfg(not(any(feature = "wry", feature = "cef")))] +#[cfg_attr(docsrs, doc(cfg(not(any(feature = "wry", feature = "cef")))))] impl Default for Builder { fn default() -> Self { Self::new() diff --git a/crates/tauri/src/lib.rs b/crates/tauri/src/lib.rs index 68a58edcc..258a34f74 100644 --- a/crates/tauri/src/lib.rs +++ b/crates/tauri/src/lib.rs @@ -124,6 +124,15 @@ pub type Wry = tauri_runtime_wry::Wry; #[cfg_attr(docsrs, doc(cfg(feature = "wry")))] pub type WryHandle = tauri_runtime_wry::WryHandle; +/// A Tauri [`Runtime`] wrapper around cef. +#[cfg(feature = "cef")] +#[cfg_attr(docsrs, doc(cfg(feature = "cef")))] +pub type Cef = tauri_runtime_cef::CefRuntime; +/// A Tauri [`RuntimeHandle`] wrapper around cef. +#[cfg(feature = "cef")] +#[cfg_attr(docsrs, doc(cfg(feature = "cef")))] +pub type CefHandle = tauri_runtime_cef::CefRuntimeHandle; + #[cfg(all(feature = "wry", target_os = "android"))] #[cfg_attr(docsrs, doc(cfg(all(feature = "wry", target_os = "android"))))] #[doc(hidden)] diff --git a/examples/helloworld/main.rs b/examples/helloworld/main.rs index ee822803b..e51bd0314 100644 --- a/examples/helloworld/main.rs +++ b/examples/helloworld/main.rs @@ -10,7 +10,7 @@ fn greet(name: &str) -> String { } fn main() { - tauri::Builder::default() + tauri::Builder::::new() .invoke_handler(tauri::generate_handler![greet]) .run(tauri::generate_context!( "../../examples/helloworld/tauri.conf.json"